diff options
Diffstat (limited to 'app/assets/javascripts/releases/stores/modules/edit_new/getters.js')
-rw-r--r-- | app/assets/javascripts/releases/stores/modules/edit_new/getters.js | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/app/assets/javascripts/releases/stores/modules/edit_new/getters.js b/app/assets/javascripts/releases/stores/modules/edit_new/getters.js new file mode 100644 index 00000000000..831037c8861 --- /dev/null +++ b/app/assets/javascripts/releases/stores/modules/edit_new/getters.js @@ -0,0 +1,105 @@ +import { isEmpty } from 'lodash'; +import { hasContent } from '~/lib/utils/text_utility'; + +/** + * @returns {Boolean} `true` if the app is editing an existing release. + * `false` if the app is creating a new release. + */ +export const isExistingRelease = (state) => { + return Boolean(state.tagName); +}; + +/** + * @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; + } + + // 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; + } + }); + + 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; +}; |