summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/releases
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/releases')
-rw-r--r--app/assets/javascripts/releases/components/app_edit_new.vue3
-rw-r--r--app/assets/javascripts/releases/components/app_index.vue39
-rw-r--r--app/assets/javascripts/releases/components/app_show.vue2
-rw-r--r--app/assets/javascripts/releases/components/evidence_block.vue4
-rw-r--r--app/assets/javascripts/releases/components/release_block.vue1
-rw-r--r--app/assets/javascripts/releases/components/release_block_assets.vue8
-rw-r--r--app/assets/javascripts/releases/components/release_block_footer.vue9
-rw-r--r--app/assets/javascripts/releases/components/release_block_header.vue9
-rw-r--r--app/assets/javascripts/releases/components/release_block_metadata.vue9
-rw-r--r--app/assets/javascripts/releases/components/release_block_milestones.vue7
-rw-r--r--app/assets/javascripts/releases/components/releases_pagination.vue20
-rw-r--r--app/assets/javascripts/releases/components/releases_pagination_graphql.vue35
-rw-r--r--app/assets/javascripts/releases/components/releases_pagination_rest.vue24
-rw-r--r--app/assets/javascripts/releases/mount_edit.js3
-rw-r--r--app/assets/javascripts/releases/mount_index.js17
-rw-r--r--app/assets/javascripts/releases/mount_new.js3
-rw-r--r--app/assets/javascripts/releases/mount_show.js3
-rw-r--r--app/assets/javascripts/releases/queries/all_releases.query.graphql69
-rw-r--r--app/assets/javascripts/releases/stores/index.js3
-rw-r--r--app/assets/javascripts/releases/stores/modules/list/actions.js30
-rw-r--r--app/assets/javascripts/releases/stores/modules/list/index.js8
-rw-r--r--app/assets/javascripts/releases/stores/modules/list/state.js14
-rw-r--r--app/assets/javascripts/releases/util.js89
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 };
+};