Welcome to mirror list, hosted at ThFree Co, Russian Federation.

getters.js « edit_new « modules « stores « releases « javascripts « assets « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 62d6bd42d517a55de46f3321cf592ec9af1989c7 (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
162
163
164
165
166
167
168
169
import { isEmpty } from 'lodash';
import { s__ } from '~/locale';
import { hasContent } from '~/lib/utils/text_utility';
import { getDuplicateItemsFromArray } from '~/lib/utils/array_utility';

/**
 * @param {Object} link The link to test
 * @returns {Boolean} `true` if the release link is empty, i.e. it has
 * empty (or whitespace-only) values for both `url` and `name`.
 * Otherwise, `false`.
 */
const isEmptyReleaseLink = (link) => !hasContent(link.url) && !hasContent(link.name);

/** Returns all release links that aren't empty */
export const releaseLinksToCreate = (state) => {
  if (!state.release) {
    return [];
  }

  return state.release.assets.links.filter((l) => !isEmptyReleaseLink(l));
};

/** Returns all release links that should be deleted */
export const releaseLinksToDelete = (state) => {
  if (!state.originalRelease) {
    return [];
  }

  return state.originalRelease.assets.links;
};

/** Returns all validation errors on the release object */
export const validationErrors = (state) => {
  const errors = {
    assets: {
      links: {},
    },
  };

  if (!state.release) {
    return errors;
  }

  if (!state.release.tagName?.trim?.().length) {
    errors.isTagNameEmpty = true;
  }

  if (state.existingRelease) {
    errors.existingRelease = true;
  }

  // Each key of this object is a URL, and the value is an
  // array of Release link objects that share this URL.
  // This is used for detecting duplicate URLs.
  const urlToLinksMap = new Map();

  state.release.assets.links.forEach((link) => {
    errors.assets.links[link.id] = {};

    // Only validate non-empty URLs
    if (isEmptyReleaseLink(link)) {
      return;
    }

    if (!hasContent(link.url)) {
      errors.assets.links[link.id].isUrlEmpty = true;
    }

    if (!hasContent(link.name)) {
      errors.assets.links[link.id].isNameEmpty = true;
    }

    const normalizedUrl = link.url.trim().toLowerCase();

    // Compare each URL to every other URL and flag any duplicates
    if (urlToLinksMap.has(normalizedUrl)) {
      // a duplicate URL was found!

      // add a validation error for each link that shares this URL
      const duplicates = urlToLinksMap.get(normalizedUrl);
      duplicates.push(link);
      duplicates.forEach((duplicateLink) => {
        errors.assets.links[duplicateLink.id].isDuplicate = true;
      });
    } else {
      // no duplicate URL was found

      urlToLinksMap.set(normalizedUrl, [link]);
    }

    if (!/^(http|https|ftp):\/\//.test(normalizedUrl)) {
      errors.assets.links[link.id].isBadFormat = true;
    }
  });

  // check for duplicated Link Titles
  const linkTitles = state.release.assets.links.map((link) => link.name.trim());
  const duplicatedTitles = getDuplicateItemsFromArray(linkTitles);

  // add a validation error for each link that shares Link Title
  state.release.assets.links.forEach((link) => {
    if (hasContent(link.name) && duplicatedTitles.includes(link.name.trim())) {
      errors.assets.links[link.id].isTitleDuplicate = true;
    }
  });

  return errors;
};

/** Returns whether or not the release object is valid */
export const isValid = (_state, getters) => {
  const errors = getters.validationErrors;
  return (
    Object.values(errors.assets.links).every(isEmpty) &&
    !errors.isTagNameEmpty &&
    !errors.existingRelease
  );
};

/** Returns all the variables for a `releaseUpdate` GraphQL mutation */
export const releaseUpdateMutatationVariables = (state, getters) => {
  const name = state.release.name?.trim().length > 0 ? state.release.name.trim() : null;

  // Milestones may be either a list of milestone objects OR just a list
  // of milestone titles. The GraphQL mutation requires only the titles be sent.
  const milestones = (state.release.milestones || []).map((m) => m.title || m);

  return {
    input: {
      projectPath: state.projectPath,
      tagName: state.release.tagName,
      name,
      releasedAt: state.release.releasedAt,
      description: state.includeTagNotes
        ? getters.formattedReleaseNotes
        : state.release.description,
      milestones,
    },
  };
};

/** Returns all the variables for a `releaseCreate` GraphQL mutation */
export const releaseCreateMutatationVariables = (state, getters) => {
  return {
    input: {
      ...getters.releaseUpdateMutatationVariables.input,
      ref: state.createFrom,
      assets: {
        links: getters.releaseLinksToCreate.map(({ name, url, linkType }) => ({
          name: name.trim(),
          url,
          linkType: linkType.toUpperCase(),
        })),
      },
    },
  };
};

export const releaseDeleteMutationVariables = (state) => ({
  input: {
    projectPath: state.projectPath,
    tagName: state.release.tagName,
  },
});

export const formattedReleaseNotes = ({ includeTagNotes, release: { description }, tagNotes }) =>
  includeTagNotes && tagNotes
    ? `${description}\n\n### ${s__('Releases|Tag message')}\n\n${tagNotes}\n`
    : description;