summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/releases/stores/modules/detail/actions.js
blob: 2f298faf37e7c7344de1d0902b6ca05a57f94d51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import * as types from './mutation_types';
import api from '~/api';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
import { releaseToApiJson, apiJsonToRelease } from '~/releases/util';

export const initializeRelease = ({ commit, dispatch, getters }) => {
  if (getters.isExistingRelease) {
    // When editing an existing release,
    // fetch the release object from the API
    return dispatch('fetchRelease');
  }

  // When creating a new release, initialize the
  // store with an empty release object
  commit(types.INITIALIZE_EMPTY_RELEASE);
  return Promise.resolve();
};

export const fetchRelease = ({ commit, state }) => {
  commit(types.REQUEST_RELEASE);

  return api
    .release(state.projectId, state.tagName)
    .then(({ data }) => {
      commit(types.RECEIVE_RELEASE_SUCCESS, apiJsonToRelease(data));
    })
    .catch(error => {
      commit(types.RECEIVE_RELEASE_ERROR, error);
      createFlash(s__('Release|Something went wrong while getting the release details'));
    });
};

export const updateReleaseTagName = ({ commit }, tagName) =>
  commit(types.UPDATE_RELEASE_TAG_NAME, tagName);

export const updateCreateFrom = ({ commit }, createFrom) =>
  commit(types.UPDATE_CREATE_FROM, createFrom);

export const updateReleaseTitle = ({ commit }, title) => commit(types.UPDATE_RELEASE_TITLE, title);

export const updateReleaseNotes = ({ commit }, notes) => commit(types.UPDATE_RELEASE_NOTES, notes);

export const updateReleaseMilestones = ({ commit }, milestones) =>
  commit(types.UPDATE_RELEASE_MILESTONES, milestones);

export const updateReleaseGroupMilestones = ({ commit }, groupMilestones) =>
  commit(types.UPDATE_RELEASE_GROUP_MILESTONES, groupMilestones);

export const addEmptyAssetLink = ({ commit }) => {
  commit(types.ADD_EMPTY_ASSET_LINK);
};

export const updateAssetLinkUrl = ({ commit }, { linkIdToUpdate, newUrl }) => {
  commit(types.UPDATE_ASSET_LINK_URL, { linkIdToUpdate, newUrl });
};

export const updateAssetLinkName = ({ commit }, { linkIdToUpdate, newName }) => {
  commit(types.UPDATE_ASSET_LINK_NAME, { linkIdToUpdate, newName });
};

export const updateAssetLinkType = ({ commit }, { linkIdToUpdate, newType }) => {
  commit(types.UPDATE_ASSET_LINK_TYPE, { linkIdToUpdate, newType });
};

export const removeAssetLink = ({ commit }, linkIdToRemove) => {
  commit(types.REMOVE_ASSET_LINK, linkIdToRemove);
};

export const receiveSaveReleaseSuccess = ({ commit, state, rootState }, release) => {
  commit(types.RECEIVE_SAVE_RELEASE_SUCCESS);
  redirectTo(rootState.featureFlags.releaseShowPage ? release._links.self : state.releasesPagePath);
};

export const saveRelease = ({ commit, dispatch, getters }) => {
  commit(types.REQUEST_SAVE_RELEASE);

  dispatch(getters.isExistingRelease ? 'updateRelease' : 'createRelease');
};

export const createRelease = ({ commit, dispatch, state, getters }) => {
  const apiJson = releaseToApiJson(
    {
      ...state.release,
      assets: {
        links: getters.releaseLinksToCreate,
      },
    },
    state.createFrom,
  );

  return api
    .createRelease(state.projectId, apiJson)
    .then(({ data }) => {
      dispatch('receiveSaveReleaseSuccess', apiJsonToRelease(data));
    })
    .catch(error => {
      commit(types.RECEIVE_SAVE_RELEASE_ERROR, error);
      createFlash(s__('Release|Something went wrong while creating a new release'));
    });
};

export const updateRelease = ({ commit, dispatch, state, getters }) => {
  const apiJson = releaseToApiJson({
    ...state.release,
    assets: {
      links: getters.releaseLinksToCreate,
    },
  });

  let updatedRelease = null;

  return (
    api
      .updateRelease(state.projectId, state.tagName, apiJson)

      /**
       * Currently, we delete all existing links and then
       * recreate new ones on each edit. This is because the
       * REST API doesn't support bulk updating of Release links,
       * and updating individual links can lead to validation
       * race conditions (in particular, the "URLs must be unique")
       * constraint.
       *
       * This isn't ideal since this is no longer an atomic
       * operation - parts of it can fail while others succeed,
       * leaving the Release in an inconsistent state.
       *
       * This logic should be refactored to use GraphQL once
       * https://gitlab.com/gitlab-org/gitlab/-/issues/208702
       * is closed.
       */
      .then(({ data }) => {
        // Save this response since we need it later in the Promise chain
        updatedRelease = data;

        // Delete all links currently associated with this Release
        return Promise.all(
          getters.releaseLinksToDelete.map(l =>
            api.deleteReleaseLink(state.projectId, state.release.tagName, l.id),
          ),
        );
      })
      .then(() => {
        // Create a new link for each link in the form
        return Promise.all(
          apiJson.assets.links.map(l =>
            api.createReleaseLink(state.projectId, state.release.tagName, l),
          ),
        );
      })
      .then(() => {
        dispatch('receiveSaveReleaseSuccess', apiJsonToRelease(updatedRelease));
      })
      .catch(error => {
        commit(types.RECEIVE_SAVE_RELEASE_ERROR, error);
        createFlash(s__('Release|Something went wrong while saving the release details'));
      })
  );
};