diff options
Diffstat (limited to 'app/assets/javascripts/releases')
23 files changed, 338 insertions, 71 deletions
diff --git a/app/assets/javascripts/releases/components/app_edit_new.vue b/app/assets/javascripts/releases/components/app_edit_new.vue index 7b7c80a6269..e1edf3d689d 100644 --- a/app/assets/javascripts/releases/components/app_edit_new.vue +++ b/app/assets/javascripts/releases/components/app_edit_new.vue @@ -1,4 +1,5 @@ <script> +/* eslint-disable vue/no-v-html */ import { mapState, mapActions, mapGetters } from 'vuex'; import { GlButton, GlFormInput, GlFormGroup } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; @@ -139,7 +140,7 @@ export default { class="form-control" /> </gl-form-group> - <gl-form-group class="w-50" @keydown.enter.prevent.capture> + <gl-form-group class="w-50" data-testid="milestones-field"> <label>{{ __('Milestones') }}</label> <div class="d-flex flex-column col-md-6 col-sm-10 pl-0"> <milestone-combobox diff --git a/app/assets/javascripts/releases/components/app_index.vue b/app/assets/javascripts/releases/components/app_index.vue index 67085ecca2b..b8cf6ce478f 100644 --- a/app/assets/javascripts/releases/components/app_index.vue +++ b/app/assets/javascripts/releases/components/app_index.vue @@ -1,6 +1,11 @@ <script> import { mapState, mapActions } from 'vuex'; -import { GlSkeletonLoading, GlEmptyState, GlLink, GlButton } from '@gitlab/ui'; +import { + GlDeprecatedSkeletonLoading as GlSkeletonLoading, + GlEmptyState, + GlLink, + GlButton, +} from '@gitlab/ui'; import { getParameterByName, historyPushState, @@ -20,27 +25,16 @@ export default { GlLink, GlButton, }, - props: { - projectId: { - type: String, - required: true, - }, - documentationPath: { - type: String, - required: true, - }, - illustrationPath: { - type: String, - required: true, - }, - newReleasePath: { - type: String, - required: false, - default: '', - }, - }, computed: { - ...mapState('list', ['isLoading', 'releases', 'hasError', 'pageInfo']), + ...mapState('list', [ + 'documentationPath', + 'illustrationPath', + 'newReleasePath', + 'isLoading', + 'releases', + 'hasError', + 'pageInfo', + ]), shouldRenderEmptyState() { return !this.releases.length && !this.hasError && !this.isLoading; }, @@ -56,14 +50,13 @@ export default { created() { this.fetchReleases({ page: getParameterByName('page'), - projectId: this.projectId, }); }, methods: { ...mapActions('list', ['fetchReleases']), onChangePage(page) { historyPushState(buildUrlWithCurrentLocation(`?page=${page}`)); - this.fetchReleases({ page, projectId: this.projectId }); + this.fetchReleases({ page }); }, }, }; diff --git a/app/assets/javascripts/releases/components/app_show.vue b/app/assets/javascripts/releases/components/app_show.vue index 0e65d722952..8b89f0cf3fc 100644 --- a/app/assets/javascripts/releases/components/app_show.vue +++ b/app/assets/javascripts/releases/components/app_show.vue @@ -1,6 +1,6 @@ <script> import { mapState, mapActions } from 'vuex'; -import { GlSkeletonLoading } from '@gitlab/ui'; +import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui'; import ReleaseBlock from './release_block.vue'; export default { diff --git a/app/assets/javascripts/releases/components/evidence_block.vue b/app/assets/javascripts/releases/components/evidence_block.vue index 6468e2ded62..3724162f6d5 100644 --- a/app/assets/javascripts/releases/components/evidence_block.vue +++ b/app/assets/javascripts/releases/components/evidence_block.vue @@ -80,9 +80,7 @@ export default { <span class="js-short monospace">{{ shortSha(index) }}</span> </template> <template #expanded> - <span class="js-expanded monospace gl-pl-1-deprecated-no-really-do-not-use-me">{{ - sha(index) - }}</span> + <span class="js-expanded monospace gl-pl-2">{{ sha(index) }}</span> </template> </expand-button> <clipboard-button diff --git a/app/assets/javascripts/releases/components/release_block.vue b/app/assets/javascripts/releases/components/release_block.vue index e0061d88ccb..2629df08be7 100644 --- a/app/assets/javascripts/releases/components/release_block.vue +++ b/app/assets/javascripts/releases/components/release_block.vue @@ -1,4 +1,5 @@ <script> +/* eslint-disable vue/no-v-html */ import { isEmpty } from 'lodash'; import $ from 'jquery'; import { slugify } from '~/lib/utils/text_utility'; diff --git a/app/assets/javascripts/releases/components/release_block_assets.vue b/app/assets/javascripts/releases/components/release_block_assets.vue index 9583f5737df..8824cbefd7e 100644 --- a/app/assets/javascripts/releases/components/release_block_assets.vue +++ b/app/assets/javascripts/releases/components/release_block_assets.vue @@ -1,7 +1,6 @@ <script> import { GlTooltipDirective, GlLink, GlButton, GlCollapse, GlIcon, GlBadge } from '@gitlab/ui'; import { difference, get } from 'lodash'; -import Icon from '~/vue_shared/components/icon.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { ASSET_LINK_TYPE } from '../constants'; import { __, s__, sprintf } from '~/locale'; @@ -13,7 +12,6 @@ export default { GlButton, GlCollapse, GlIcon, - Icon, GlBadge, }, directives: { @@ -157,7 +155,7 @@ export default { <ul v-if="assets.links.length" class="pl-0 mb-0 gl-mt-3 list-unstyled js-assets-list"> <li v-for="link in assets.links" :key="link.name" class="gl-mb-3"> <gl-link v-gl-tooltip.bottom :title="__('Download asset')" :href="link.directAssetUrl"> - <icon name="package" class="align-middle gl-mr-2 align-text-bottom" /> + <gl-icon name="package" class="align-middle gl-mr-2 align-text-bottom" /> {{ link.name }} <span v-if="link.external" data-testid="external-link-indicator">{{ __('(external source)') @@ -174,9 +172,9 @@ export default { aria-haspopup="true" aria-expanded="false" > - <icon name="doc-code" class="align-top gl-mr-2" /> + <gl-icon name="doc-code" class="align-top gl-mr-2" /> {{ __('Source code') }} - <icon name="chevron-down" /> + <gl-icon name="chevron-down" /> </button> <div class="js-sources-dropdown dropdown-menu"> diff --git a/app/assets/javascripts/releases/components/release_block_footer.vue b/app/assets/javascripts/releases/components/release_block_footer.vue index 26154272d39..3beec466c54 100644 --- a/app/assets/javascripts/releases/components/release_block_footer.vue +++ b/app/assets/javascripts/releases/components/release_block_footer.vue @@ -1,6 +1,5 @@ <script> -import { GlTooltipDirective, GlLink } from '@gitlab/ui'; -import Icon from '~/vue_shared/components/icon.vue'; +import { GlTooltipDirective, GlLink, GlIcon } from '@gitlab/ui'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import timeagoMixin from '~/vue_shared/mixins/timeago'; import { __, sprintf } from '~/locale'; @@ -8,7 +7,7 @@ import { __, sprintf } from '~/locale'; export default { name: 'ReleaseBlockFooter', components: { - Icon, + GlIcon, GlLink, UserAvatarLink, }, @@ -68,7 +67,7 @@ export default { <template> <div> <div v-if="commit" class="float-left mr-3 d-flex align-items-center js-commit-info"> - <icon ref="commitIcon" name="commit" class="mr-1" /> + <gl-icon ref="commitIcon" name="commit" class="mr-1" /> <div v-gl-tooltip.bottom :title="commit.title"> <gl-link v-if="commitPath" :href="commitPath"> {{ commit.shortId }} @@ -78,7 +77,7 @@ export default { </div> <div v-if="tagName" class="float-left mr-3 d-flex align-items-center js-tag-info"> - <icon name="tag" class="mr-1" /> + <gl-icon name="tag" class="mr-1" /> <div v-gl-tooltip.bottom :title="__('Tag')"> <gl-link v-if="tagPath" :href="tagPath"> {{ tagName }} diff --git a/app/assets/javascripts/releases/components/release_block_header.vue b/app/assets/javascripts/releases/components/release_block_header.vue index 310fba0fe76..95292a26bce 100644 --- a/app/assets/javascripts/releases/components/release_block_header.vue +++ b/app/assets/javascripts/releases/components/release_block_header.vue @@ -1,6 +1,5 @@ <script> -import { GlTooltipDirective, GlLink, GlBadge, GlButton } from '@gitlab/ui'; -import Icon from '~/vue_shared/components/icon.vue'; +import { GlTooltipDirective, GlLink, GlBadge, GlButton, GlIcon } from '@gitlab/ui'; import { BACK_URL_PARAM } from '~/releases/constants'; import { setUrlParams } from '~/lib/utils/url_utility'; @@ -9,7 +8,7 @@ export default { components: { GlLink, GlBadge, - Icon, + GlIcon, GlButton, }, directives: { @@ -42,7 +41,7 @@ export default { <template> <div class="card-header d-flex align-items-center bg-white pr-0"> - <h2 class="card-title my-2 mr-auto gl-font-size-20-deprecated-no-really-do-not-use-me"> + <h2 class="card-title my-2 mr-auto"> <gl-link v-if="selfLink" :href="selfLink" class="font-size-inherit"> {{ release.name }} </gl-link> @@ -60,7 +59,7 @@ export default { :title="__('Edit this release')" :href="editLink" > - <icon name="pencil" /> + <gl-icon name="pencil" /> </gl-button> </div> </template> diff --git a/app/assets/javascripts/releases/components/release_block_metadata.vue b/app/assets/javascripts/releases/components/release_block_metadata.vue index 861c2e11798..2247b4c0064 100644 --- a/app/assets/javascripts/releases/components/release_block_metadata.vue +++ b/app/assets/javascripts/releases/components/release_block_metadata.vue @@ -1,7 +1,6 @@ <script> -import { GlTooltipDirective, GlLink } from '@gitlab/ui'; +import { GlTooltipDirective, GlLink, GlIcon } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; -import Icon from '~/vue_shared/components/icon.vue'; import timeagoMixin from '~/vue_shared/mixins/timeago'; import ReleaseBlockAuthor from './release_block_author.vue'; import ReleaseBlockMilestones from './release_block_milestones.vue'; @@ -9,7 +8,7 @@ import ReleaseBlockMilestones from './release_block_milestones.vue'; export default { name: 'ReleaseBlockMetadata', components: { - Icon, + GlIcon, GlLink, ReleaseBlockAuthor, ReleaseBlockMilestones, @@ -58,7 +57,7 @@ export default { <template> <div class="card-subtitle d-flex flex-wrap text-secondary"> <div class="gl-mr-3"> - <icon name="commit" class="align-middle" /> + <gl-icon name="commit" class="align-middle" /> <gl-link v-if="commitUrl" v-gl-tooltip.bottom :title="commit.title" :href="commitUrl"> {{ commit.shortId }} </gl-link> @@ -66,7 +65,7 @@ export default { </div> <div class="gl-mr-3"> - <icon name="tag" class="align-middle" /> + <gl-icon name="tag" class="align-middle" /> <gl-link v-if="tagUrl" v-gl-tooltip.bottom :title="__('Tag')" :href="tagUrl"> {{ release.tagName }} </gl-link> diff --git a/app/assets/javascripts/releases/components/release_block_milestones.vue b/app/assets/javascripts/releases/components/release_block_milestones.vue index 9abd3345b22..1da683764b3 100644 --- a/app/assets/javascripts/releases/components/release_block_milestones.vue +++ b/app/assets/javascripts/releases/components/release_block_milestones.vue @@ -1,13 +1,12 @@ <script> -import { GlTooltipDirective, GlLink } from '@gitlab/ui'; +import { GlTooltipDirective, GlLink, GlIcon } from '@gitlab/ui'; import { n__ } from '~/locale'; -import Icon from '~/vue_shared/components/icon.vue'; export default { name: 'ReleaseBlockMilestones', components: { GlLink, - Icon, + GlIcon, }, directives: { GlTooltip: GlTooltipDirective, @@ -29,7 +28,7 @@ export default { <template> <div> <div class="js-milestone-list-label"> - <icon name="flag" class="align-middle" /> + <gl-icon name="flag" class="align-middle" /> <span class="js-label-text">{{ labelText }}</span> </div> diff --git a/app/assets/javascripts/releases/components/releases_pagination.vue b/app/assets/javascripts/releases/components/releases_pagination.vue new file mode 100644 index 00000000000..062c72b445b --- /dev/null +++ b/app/assets/javascripts/releases/components/releases_pagination.vue @@ -0,0 +1,20 @@ +<script> +import { mapGetters } from 'vuex'; +import ReleasesPaginationGraphql from './releases_pagination_graphql.vue'; +import ReleasesPaginationRest from './releases_pagination_rest.vue'; + +export default { + name: 'ReleasesPagination', + components: { ReleasesPaginationGraphql, ReleasesPaginationRest }, + computed: { + ...mapGetters(['useGraphQLEndpoint']), + }, +}; +</script> + +<template> + <div class="gl-display-flex gl-justify-content-center"> + <releases-pagination-graphql v-if="useGraphQLEndpoint" /> + <releases-pagination-rest v-else /> + </div> +</template> diff --git a/app/assets/javascripts/releases/components/releases_pagination_graphql.vue b/app/assets/javascripts/releases/components/releases_pagination_graphql.vue new file mode 100644 index 00000000000..a4fe407a5bd --- /dev/null +++ b/app/assets/javascripts/releases/components/releases_pagination_graphql.vue @@ -0,0 +1,35 @@ +<script> +import { mapActions, mapState } from 'vuex'; +import { GlKeysetPagination } from '@gitlab/ui'; +import { historyPushState, buildUrlWithCurrentLocation } from '~/lib/utils/common_utils'; + +export default { + name: 'ReleasesPaginationGraphql', + components: { GlKeysetPagination }, + computed: { + ...mapState('list', ['graphQlPageInfo']), + showPagination() { + return this.graphQlPageInfo.hasPreviousPage || this.graphQlPageInfo.hasNextPage; + }, + }, + methods: { + ...mapActions('list', ['fetchReleasesGraphQl']), + onPrev(before) { + historyPushState(buildUrlWithCurrentLocation(`?before=${before}`)); + this.fetchReleasesGraphQl({ before }); + }, + onNext(after) { + historyPushState(buildUrlWithCurrentLocation(`?after=${after}`)); + this.fetchReleasesGraphQl({ after }); + }, + }, +}; +</script> +<template> + <gl-keyset-pagination + v-if="showPagination" + v-bind="graphQlPageInfo" + @prev="onPrev($event)" + @next="onNext($event)" + /> +</template> diff --git a/app/assets/javascripts/releases/components/releases_pagination_rest.vue b/app/assets/javascripts/releases/components/releases_pagination_rest.vue new file mode 100644 index 00000000000..992cc4cd469 --- /dev/null +++ b/app/assets/javascripts/releases/components/releases_pagination_rest.vue @@ -0,0 +1,24 @@ +<script> +import { mapActions, mapState } from 'vuex'; +import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue'; +import { historyPushState, buildUrlWithCurrentLocation } from '~/lib/utils/common_utils'; + +export default { + name: 'ReleasesPaginationRest', + components: { TablePagination }, + computed: { + ...mapState('list', ['pageInfo']), + }, + methods: { + ...mapActions('list', ['fetchReleasesRest']), + onChangePage(page) { + historyPushState(buildUrlWithCurrentLocation(`?page=${page}`)); + this.fetchReleasesRest({ page }); + }, + }, +}; +</script> + +<template> + <table-pagination :change="onChangePage" :page-info="pageInfo" /> +</template> diff --git a/app/assets/javascripts/releases/mount_edit.js b/app/assets/javascripts/releases/mount_edit.js index c7385b3c57f..623b18591a0 100644 --- a/app/assets/javascripts/releases/mount_edit.js +++ b/app/assets/javascripts/releases/mount_edit.js @@ -1,8 +1,11 @@ import Vue from 'vue'; +import Vuex from 'vuex'; import ReleaseEditNewApp from './components/app_edit_new.vue'; import createStore from './stores'; import createDetailModule from './stores/modules/detail'; +Vue.use(Vuex); + export default () => { const el = document.getElementById('js-edit-release-page'); diff --git a/app/assets/javascripts/releases/mount_index.js b/app/assets/javascripts/releases/mount_index.js index 5f0bf3b6459..cd4fa5c5df5 100644 --- a/app/assets/javascripts/releases/mount_index.js +++ b/app/assets/javascripts/releases/mount_index.js @@ -1,7 +1,10 @@ import Vue from 'vue'; +import Vuex from 'vuex'; import ReleaseListApp from './components/app_index.vue'; import createStore from './stores'; -import listModule from './stores/modules/list'; +import createListModule from './stores/modules/list'; + +Vue.use(Vuex); export default () => { const el = document.getElementById('js-releases-page'); @@ -10,12 +13,14 @@ export default () => { el, store: createStore({ modules: { - list: listModule, + list: createListModule(el.dataset), + }, + featureFlags: { + graphqlReleaseData: Boolean(gon.features?.graphqlReleaseData), + graphqlReleasesPage: Boolean(gon.features?.graphqlReleasesPage), + graphqlMilestoneStats: Boolean(gon.features?.graphqlMilestoneStats), }, }), - render: h => - h(ReleaseListApp, { - props: el.dataset, - }), + render: h => h(ReleaseListApp), }); }; diff --git a/app/assets/javascripts/releases/mount_new.js b/app/assets/javascripts/releases/mount_new.js index 68003f6a346..10725e47740 100644 --- a/app/assets/javascripts/releases/mount_new.js +++ b/app/assets/javascripts/releases/mount_new.js @@ -1,8 +1,11 @@ import Vue from 'vue'; +import Vuex from 'vuex'; import ReleaseEditNewApp from './components/app_edit_new.vue'; import createStore from './stores'; import createDetailModule from './stores/modules/detail'; +Vue.use(Vuex); + export default () => { const el = document.getElementById('js-new-release-page'); diff --git a/app/assets/javascripts/releases/mount_show.js b/app/assets/javascripts/releases/mount_show.js index 7ddc8e786c1..eef015ee0a6 100644 --- a/app/assets/javascripts/releases/mount_show.js +++ b/app/assets/javascripts/releases/mount_show.js @@ -1,8 +1,11 @@ import Vue from 'vue'; +import Vuex from 'vuex'; import ReleaseShowApp from './components/app_show.vue'; import createStore from './stores'; import createDetailModule from './stores/modules/detail'; +Vue.use(Vuex); + export default () => { const el = document.getElementById('js-show-release-page'); diff --git a/app/assets/javascripts/releases/queries/all_releases.query.graphql b/app/assets/javascripts/releases/queries/all_releases.query.graphql new file mode 100644 index 00000000000..7a99f32fdfa --- /dev/null +++ b/app/assets/javascripts/releases/queries/all_releases.query.graphql @@ -0,0 +1,69 @@ +query allReleases($fullPath: ID!) { + project(fullPath: $fullPath) { + releases(first: 20) { + count + nodes { + name + tagName + tagPath + descriptionHtml + releasedAt + upcomingRelease + assets { + count + sources { + nodes { + format + url + } + } + links { + nodes { + id + name + url + directAssetUrl + linkType + external + } + } + } + evidences { + nodes { + filepath + collectedAt + sha + } + } + links { + editUrl + issuesUrl + mergeRequestsUrl + selfUrl + } + commit { + sha + webUrl + title + } + author { + webUrl + avatarUrl + username + } + milestones { + nodes { + id + title + description + webPath + stats { + totalIssuesCount + closedIssuesCount + } + } + } + } + } + } +} diff --git a/app/assets/javascripts/releases/stores/index.js b/app/assets/javascripts/releases/stores/index.js index 7f211145ccf..b2e93d789d7 100644 --- a/app/assets/javascripts/releases/stores/index.js +++ b/app/assets/javascripts/releases/stores/index.js @@ -1,8 +1,5 @@ -import Vue from 'vue'; import Vuex from 'vuex'; -Vue.use(Vuex); - export default ({ modules, featureFlags }) => new Vuex.Store({ modules, diff --git a/app/assets/javascripts/releases/stores/modules/list/actions.js b/app/assets/javascripts/releases/stores/modules/list/actions.js index 90fba319e9f..945b093b983 100644 --- a/app/assets/javascripts/releases/stores/modules/list/actions.js +++ b/app/assets/javascripts/releases/stores/modules/list/actions.js @@ -7,6 +7,8 @@ import { parseIntPagination, convertObjectPropsToCamelCase, } from '~/lib/utils/common_utils'; +import allReleasesQuery from '~/releases/queries/all_releases.query.graphql'; +import { gqClient, convertGraphQLResponse } from '../../../util'; /** * Commits a mutation to update the state while the main endpoint is being requested. @@ -21,13 +23,31 @@ export const requestReleases = ({ commit }) => commit(types.REQUEST_RELEASES); * * @param {String} projectId */ -export const fetchReleases = ({ dispatch }, { page = '1', projectId }) => { +export const fetchReleases = ({ dispatch, rootState, state }, { page = '1' }) => { dispatch('requestReleases'); - api - .releases(projectId, { page }) - .then(response => dispatch('receiveReleasesSuccess', response)) - .catch(() => dispatch('receiveReleasesError')); + if ( + rootState.featureFlags.graphqlReleaseData && + rootState.featureFlags.graphqlReleasesPage && + rootState.featureFlags.graphqlMilestoneStats + ) { + gqClient + .query({ + query: allReleasesQuery, + variables: { + fullPath: state.projectPath, + }, + }) + .then(response => { + dispatch('receiveReleasesSuccess', convertGraphQLResponse(response)); + }) + .catch(() => dispatch('receiveReleasesError')); + } else { + api + .releases(state.projectId, { page }) + .then(response => dispatch('receiveReleasesSuccess', response)) + .catch(() => dispatch('receiveReleasesError')); + } }; export const receiveReleasesSuccess = ({ commit }, { data, headers }) => { diff --git a/app/assets/javascripts/releases/stores/modules/list/index.js b/app/assets/javascripts/releases/stores/modules/list/index.js index e4633b15a0c..0f97fa83ced 100644 --- a/app/assets/javascripts/releases/stores/modules/list/index.js +++ b/app/assets/javascripts/releases/stores/modules/list/index.js @@ -1,10 +1,10 @@ -import state from './state'; +import createState from './state'; import * as actions from './actions'; import mutations from './mutations'; -export default { +export default initialState => ({ namespaced: true, actions, mutations, - state, -}; + state: createState(initialState), +}); diff --git a/app/assets/javascripts/releases/stores/modules/list/state.js b/app/assets/javascripts/releases/stores/modules/list/state.js index c251f56c9c5..9fe313745fc 100644 --- a/app/assets/javascripts/releases/stores/modules/list/state.js +++ b/app/assets/javascripts/releases/stores/modules/list/state.js @@ -1,4 +1,16 @@ -export default () => ({ +export default ({ + projectId, + projectPath, + documentationPath, + illustrationPath, + newReleasePath = '', +}) => ({ + projectId, + projectPath, + documentationPath, + illustrationPath, + newReleasePath, + isLoading: false, hasError: false, releases: [], diff --git a/app/assets/javascripts/releases/util.js b/app/assets/javascripts/releases/util.js index 842a423b142..d7fac7a9b65 100644 --- a/app/assets/javascripts/releases/util.js +++ b/app/assets/javascripts/releases/util.js @@ -1,3 +1,6 @@ +import { pick } from 'lodash'; +import createGqClient, { fetchPolicies } from '~/lib/graphql'; +import { truncateSha } from '~/lib/utils/text_utility'; import { convertObjectPropsToCamelCase, convertObjectPropsToSnakeCase, @@ -39,3 +42,89 @@ export const apiJsonToRelease = json => { return release; }; + +export const gqClient = createGqClient({}, { fetchPolicy: fetchPolicies.NO_CACHE }); + +const convertScalarProperties = graphQLRelease => + pick(graphQLRelease, [ + 'name', + 'tagName', + 'tagPath', + 'descriptionHtml', + 'releasedAt', + 'upcomingRelease', + ]); + +const convertAssets = graphQLRelease => ({ + assets: { + count: graphQLRelease.assets.count, + sources: [...graphQLRelease.assets.sources.nodes], + links: graphQLRelease.assets.links.nodes.map(l => ({ + ...l, + linkType: l.linkType?.toLowerCase(), + })), + }, +}); + +const convertEvidences = graphQLRelease => ({ + evidences: graphQLRelease.evidences.nodes.map(e => e), +}); + +const convertLinks = graphQLRelease => ({ + _links: { + ...graphQLRelease.links, + self: graphQLRelease.links?.selfUrl, + }, +}); + +const convertCommit = graphQLRelease => { + if (!graphQLRelease.commit) { + return {}; + } + + return { + commit: { + shortId: truncateSha(graphQLRelease.commit.sha), + title: graphQLRelease.commit.title, + }, + commitPath: graphQLRelease.commit.webUrl, + }; +}; + +const convertAuthor = graphQLRelease => ({ author: graphQLRelease.author }); + +const convertMilestones = graphQLRelease => ({ + milestones: graphQLRelease.milestones.nodes.map(m => ({ + ...m, + webUrl: m.webPath, + webPath: undefined, + issueStats: { + total: m.stats.totalIssuesCount, + closed: m.stats.closedIssuesCount, + }, + stats: undefined, + })), +}); + +/** + * Converts the response from the GraphQL endpoint into the + * same shape as is returned from the Releases REST API. + * + * This allows the release components to use the response + * from either endpoint interchangeably. + * + * @param response The response received from the GraphQL endpoint + */ +export const convertGraphQLResponse = response => { + const releases = response.data.project.releases.nodes.map(r => ({ + ...convertScalarProperties(r), + ...convertAssets(r), + ...convertEvidences(r), + ...convertLinks(r), + ...convertCommit(r), + ...convertAuthor(r), + ...convertMilestones(r), + })); + + return { data: releases }; +}; |