All files / app/assets/javascripts/releases/stores/modules/edit_new getters.js

97.96% Statements 48/49
96.43% Branches 27/28
100% Functions 13/13
97.83% Lines 45/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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              4x 6x                 52x     4x 2x 1x     7x       4x 2x 1x     1x       4x 8x           8x       8x 6x           8x   8x 45x     45x 4x     41x 10x     41x 10x     41x     41x       15x 15x 15x 35x         26x     41x 15x       8x       4x 3x 3x       4x 5x       5x   5x                       4x 1x         1x                  
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: {},
    },
  };
 
  Iif (!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;
};
 
/** Returns all the variables for a `releaseUpdate` GraphQL mutation */
export const releaseUpdateMutatationVariables = (state) => {
  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,
      description: 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,
          url,
          linkType: linkType.toUpperCase(),
        })),
      },
    },
  };
};