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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-10-21 10:08:36 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-10-21 10:08:36 +0300
commit48aff82709769b098321c738f3444b9bdaa694c6 (patch)
treee00c7c43e2d9b603a5a6af576b1685e400410dee /app/assets/javascripts/releases
parent879f5329ee916a948223f8f43d77fba4da6cd028 (diff)
Add latest changes from gitlab-org/gitlab@13-5-stable-eev13.5.0-rc42
Diffstat (limited to 'app/assets/javascripts/releases')
-rw-r--r--app/assets/javascripts/releases/components/app_edit_new.vue42
-rw-r--r--app/assets/javascripts/releases/components/app_index.vue49
-rw-r--r--app/assets/javascripts/releases/components/app_show.vue6
-rw-r--r--app/assets/javascripts/releases/components/asset_links_form.vue3
-rw-r--r--app/assets/javascripts/releases/components/evidence_block.vue6
-rw-r--r--app/assets/javascripts/releases/components/release_block.vue17
-rw-r--r--app/assets/javascripts/releases/components/release_block_assets.vue137
-rw-r--r--app/assets/javascripts/releases/components/release_block_author.vue42
-rw-r--r--app/assets/javascripts/releases/components/release_block_header.vue8
-rw-r--r--app/assets/javascripts/releases/components/release_block_metadata.vue90
-rw-r--r--app/assets/javascripts/releases/components/release_block_milestones.vue50
-rw-r--r--app/assets/javascripts/releases/components/release_skeleton_loader.vue51
-rw-r--r--app/assets/javascripts/releases/components/releases_pagination_graphql.vue6
-rw-r--r--app/assets/javascripts/releases/components/releases_pagination_rest.vue8
-rw-r--r--app/assets/javascripts/releases/constants.js2
-rw-r--r--app/assets/javascripts/releases/mount_edit.js3
-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.graphql74
-rw-r--r--app/assets/javascripts/releases/queries/one_release.query.graphql9
-rw-r--r--app/assets/javascripts/releases/queries/release.fragment.graphql62
-rw-r--r--app/assets/javascripts/releases/stores/getters.js11
-rw-r--r--app/assets/javascripts/releases/stores/index.js2
-rw-r--r--app/assets/javascripts/releases/stores/modules/detail/actions.js37
-rw-r--r--app/assets/javascripts/releases/stores/modules/detail/mutation_types.js1
-rw-r--r--app/assets/javascripts/releases/stores/modules/detail/mutations.js5
-rw-r--r--app/assets/javascripts/releases/stores/modules/detail/state.js2
-rw-r--r--app/assets/javascripts/releases/stores/modules/list/actions.js111
-rw-r--r--app/assets/javascripts/releases/stores/modules/list/mutations.js7
-rw-r--r--app/assets/javascripts/releases/stores/modules/list/state.js3
-rw-r--r--app/assets/javascripts/releases/util.js54
31 files changed, 416 insertions, 488 deletions
diff --git a/app/assets/javascripts/releases/components/app_edit_new.vue b/app/assets/javascripts/releases/components/app_edit_new.vue
index e1edf3d689d..1a07e0ed762 100644
--- a/app/assets/javascripts/releases/components/app_edit_new.vue
+++ b/app/assets/javascripts/releases/components/app_edit_new.vue
@@ -1,13 +1,11 @@
<script>
-/* eslint-disable vue/no-v-html */
import { mapState, mapActions, mapGetters } from 'vuex';
-import { GlButton, GlFormInput, GlFormGroup } from '@gitlab/ui';
-import { __, sprintf } from '~/locale';
+import { GlButton, GlFormInput, GlFormGroup, GlSprintf } from '@gitlab/ui';
+import { __ } from '~/locale';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { BACK_URL_PARAM } from '~/releases/constants';
import { getParameterByName } from '~/lib/utils/common_utils';
import AssetLinksForm from './asset_links_form.vue';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import MilestoneCombobox from '~/milestones/project_milestone_combobox.vue';
import TagField from './tag_field.vue';
@@ -17,12 +15,12 @@ export default {
GlFormInput,
GlFormGroup,
GlButton,
+ GlSprintf,
MarkdownField,
AssetLinksForm,
MilestoneCombobox,
TagField,
},
- mixins: [glFeatureFlagsMixin()],
computed: {
...mapState('detail', [
'isFetchingRelease',
@@ -41,18 +39,6 @@ export default {
showForm() {
return Boolean(!this.isFetchingRelease && !this.fetchError && this.release);
},
- subtitleText() {
- return sprintf(
- __(
- 'Releases are based on Git tags. We recommend tags that use semantic versioning, for example %{codeStart}v1.0%{codeEnd}, %{codeStart}v2.0-pre%{codeEnd}.',
- ),
- {
- codeStart: '<code>',
- codeEnd: '</code>',
- },
- false,
- );
- },
releaseTitle: {
get() {
return this.$store.state.detail.release.name;
@@ -80,9 +66,6 @@ export default {
cancelPath() {
return getParameterByName(BACK_URL_PARAM) || this.releasesPagePath;
},
- showAssetLinksForm() {
- return this.glFeatures.releaseAssetLinkEditing;
- },
saveButtonLabel() {
return this.isExistingRelease ? __('Save changes') : __('Create release');
},
@@ -127,7 +110,19 @@ export default {
</script>
<template>
<div class="d-flex flex-column">
- <p class="pt-3 js-subtitle-text" v-html="subtitleText"></p>
+ <p class="pt-3 js-subtitle-text">
+ <gl-sprintf
+ :message="
+ __(
+ 'Releases are based on Git tags. We recommend tags that use semantic versioning, for example %{codeStart}v1.0%{codeEnd}, %{codeStart}v2.0-pre%{codeEnd}.',
+ )
+ "
+ >
+ <template #code="{ content }">
+ <code>{{ content }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
<form v-if="showForm" class="js-quick-submit" @submit.prevent="submitForm">
<tag-field />
<gl-form-group>
@@ -150,7 +145,7 @@ export default {
/>
</div>
</gl-form-group>
- <gl-form-group>
+ <gl-form-group data-testid="release-notes">
<label for="release-notes">{{ __('Release notes') }}</label>
<div class="bordered-box pr-3 pl-3">
<markdown-field
@@ -158,6 +153,7 @@ export default {
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
:add-spacing-classes="false"
+ :textarea-value="releaseNotes"
class="gl-mt-3 gl-mb-3"
>
<template #textarea>
@@ -175,7 +171,7 @@ export default {
</div>
</gl-form-group>
- <asset-links-form v-if="showAssetLinksForm" />
+ <asset-links-form />
<div class="d-flex pt-3">
<gl-button
diff --git a/app/assets/javascripts/releases/components/app_index.vue b/app/assets/javascripts/releases/components/app_index.vue
index b8cf6ce478f..422d8bf630d 100644
--- a/app/assets/javascripts/releases/components/app_index.vue
+++ b/app/assets/javascripts/releases/components/app_index.vue
@@ -1,29 +1,21 @@
<script>
import { mapState, mapActions } from 'vuex';
-import {
- GlDeprecatedSkeletonLoading as GlSkeletonLoading,
- GlEmptyState,
- GlLink,
- GlButton,
-} from '@gitlab/ui';
-import {
- getParameterByName,
- historyPushState,
- buildUrlWithCurrentLocation,
-} from '~/lib/utils/common_utils';
+import { GlEmptyState, GlLink, GlButton } from '@gitlab/ui';
+import { getParameterByName } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
-import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import ReleaseBlock from './release_block.vue';
+import ReleasesPagination from './releases_pagination.vue';
+import ReleaseSkeletonLoader from './release_skeleton_loader.vue';
export default {
name: 'ReleasesApp',
components: {
- GlSkeletonLoading,
GlEmptyState,
- ReleaseBlock,
- TablePagination,
GlLink,
GlButton,
+ ReleaseBlock,
+ ReleasesPagination,
+ ReleaseSkeletonLoader,
},
computed: {
...mapState('list', [
@@ -33,7 +25,6 @@ export default {
'isLoading',
'releases',
'hasError',
- 'pageInfo',
]),
shouldRenderEmptyState() {
return !this.releases.length && !this.hasError && !this.isLoading;
@@ -48,15 +39,23 @@ export default {
},
},
created() {
- this.fetchReleases({
- page: getParameterByName('page'),
- });
+ this.fetchReleases();
+
+ window.addEventListener('popstate', this.fetchReleases);
},
methods: {
- ...mapActions('list', ['fetchReleases']),
- onChangePage(page) {
- historyPushState(buildUrlWithCurrentLocation(`?page=${page}`));
- this.fetchReleases({ page });
+ ...mapActions('list', {
+ fetchReleasesStoreAction: 'fetchReleases',
+ }),
+ fetchReleases() {
+ this.fetchReleasesStoreAction({
+ // these two parameters are only used in "GraphQL mode"
+ before: getParameterByName('before'),
+ after: getParameterByName('after'),
+
+ // this parameter is only used when in "REST mode"
+ page: getParameterByName('page'),
+ });
},
},
};
@@ -74,7 +73,7 @@ export default {
{{ __('New release') }}
</gl-button>
- <gl-skeleton-loading v-if="isLoading" class="js-loading" />
+ <release-skeleton-loader v-if="isLoading" class="js-loading" />
<gl-empty-state
v-else-if="shouldRenderEmptyState"
@@ -105,7 +104,7 @@ export default {
/>
</div>
- <table-pagination v-if="!isLoading" :change="onChangePage" :page-info="pageInfo" />
+ <releases-pagination v-if="!isLoading" />
</div>
</template>
<style>
diff --git a/app/assets/javascripts/releases/components/app_show.vue b/app/assets/javascripts/releases/components/app_show.vue
index 8b89f0cf3fc..9ef38503c10 100644
--- a/app/assets/javascripts/releases/components/app_show.vue
+++ b/app/assets/javascripts/releases/components/app_show.vue
@@ -1,13 +1,13 @@
<script>
import { mapState, mapActions } from 'vuex';
-import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import ReleaseBlock from './release_block.vue';
+import ReleaseSkeletonLoader from './release_skeleton_loader.vue';
export default {
name: 'ReleaseShowApp',
components: {
- GlSkeletonLoading,
ReleaseBlock,
+ ReleaseSkeletonLoader,
},
computed: {
...mapState('detail', ['isFetchingRelease', 'fetchError', 'release']),
@@ -22,7 +22,7 @@ export default {
</script>
<template>
<div class="gl-mt-3">
- <gl-skeleton-loading v-if="isFetchingRelease" />
+ <release-skeleton-loader v-if="isFetchingRelease" />
<release-block v-else-if="!fetchError" :release="release" />
</div>
diff --git a/app/assets/javascripts/releases/components/asset_links_form.vue b/app/assets/javascripts/releases/components/asset_links_form.vue
index 07fab840067..331cc8ade6c 100644
--- a/app/assets/javascripts/releases/components/asset_links_form.vue
+++ b/app/assets/javascripts/releases/components/asset_links_form.vue
@@ -10,7 +10,6 @@ import {
GlFormInput,
GlFormSelect,
} from '@gitlab/ui';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { DEFAULT_ASSET_LINK_TYPE, ASSET_LINK_TYPE } from '../constants';
import { s__ } from '~/locale';
@@ -26,7 +25,6 @@ export default {
GlFormSelect,
},
directives: { GlTooltip: GlTooltipDirective },
- mixins: [glFeatureFlagsMixin()],
computed: {
...mapState('detail', ['release', 'releaseAssetsDocsPath']),
...mapGetters('detail', ['validationErrors']),
@@ -195,7 +193,6 @@ export default {
</gl-form-group>
<gl-form-group
- v-if="glFeatures.releaseAssetLinkType"
class="link-type-field col-auto px-sm-2"
:label="__('Type')"
:label-for="`asset-type-${index}`"
diff --git a/app/assets/javascripts/releases/components/evidence_block.vue b/app/assets/javascripts/releases/components/evidence_block.vue
index 3724162f6d5..6e6017637d4 100644
--- a/app/assets/javascripts/releases/components/evidence_block.vue
+++ b/app/assets/javascripts/releases/components/evidence_block.vue
@@ -83,11 +83,7 @@ export default {
<span class="js-expanded monospace gl-pl-2">{{ sha(index) }}</span>
</template>
</expand-button>
- <clipboard-button
- :title="__('Copy evidence SHA')"
- :text="sha(index)"
- css-class="btn-default btn-transparent btn-clipboard"
- />
+ <clipboard-button :title="__('Copy evidence SHA')" :text="sha(index)" category="tertiary" />
</div>
<div class="d-flex align-items-center text-muted">
diff --git a/app/assets/javascripts/releases/components/release_block.vue b/app/assets/javascripts/releases/components/release_block.vue
index 2629df08be7..e9163a52792 100644
--- a/app/assets/javascripts/releases/components/release_block.vue
+++ b/app/assets/javascripts/releases/components/release_block.vue
@@ -11,7 +11,6 @@ import EvidenceBlock from './evidence_block.vue';
import ReleaseBlockAssets from './release_block_assets.vue';
import ReleaseBlockFooter from './release_block_footer.vue';
import ReleaseBlockHeader from './release_block_header.vue';
-import ReleaseBlockMetadata from './release_block_metadata.vue';
import ReleaseBlockMilestoneInfo from './release_block_milestone_info.vue';
export default {
@@ -21,7 +20,6 @@ export default {
ReleaseBlockAssets,
ReleaseBlockFooter,
ReleaseBlockHeader,
- ReleaseBlockMetadata,
ReleaseBlockMilestoneInfo,
},
mixins: [glFeatureFlagsMixin()],
@@ -54,22 +52,13 @@ export default {
milestones() {
return this.release.milestones || [];
},
- shouldShowEvidence() {
- return this.glFeatures.releaseEvidenceCollection;
- },
- shouldShowFooter() {
- return this.glFeatures.releaseIssueSummary;
- },
shouldRenderAssets() {
return Boolean(
this.assets.links.length || (this.assets.sources && this.assets.sources.length),
);
},
- shouldRenderReleaseMetaData() {
- return !this.glFeatures.releaseIssueSummary;
- },
shouldRenderMilestoneInfo() {
- return Boolean(this.glFeatures.releaseIssueSummary && !isEmpty(this.release.milestones));
+ return Boolean(!isEmpty(this.release.milestones));
},
},
@@ -105,9 +94,8 @@ export default {
<hr class="mb-3 mt-0" />
</div>
- <release-block-metadata v-if="shouldRenderReleaseMetaData" :release="release" />
<release-block-assets v-if="shouldRenderAssets" :assets="assets" />
- <evidence-block v-if="hasEvidence && shouldShowEvidence" :release="release" />
+ <evidence-block v-if="hasEvidence" :release="release" />
<div ref="gfm-content" class="card-text gl-mt-3">
<div class="md" v-html="release.descriptionHtml"></div>
@@ -115,7 +103,6 @@ export default {
</div>
<release-block-footer
- v-if="shouldShowFooter"
class="card-footer"
:commit="release.commit"
:commit-path="release.commitPath"
diff --git a/app/assets/javascripts/releases/components/release_block_assets.vue b/app/assets/javascripts/releases/components/release_block_assets.vue
index 8824cbefd7e..eb83d8657c0 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 glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { ASSET_LINK_TYPE } from '../constants';
import { __, s__, sprintf } from '~/locale';
@@ -17,7 +16,6 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
- mixins: [glFeatureFlagsMixin()],
props: {
assets: {
type: Object,
@@ -30,9 +28,6 @@ export default {
};
},
computed: {
- hasAssets() {
- return Boolean(this.assets.count);
- },
imageLinks() {
return this.linksForType(ASSET_LINK_TYPE.IMAGE);
},
@@ -95,94 +90,50 @@ export default {
<template>
<div class="card-text gl-mt-3">
- <template v-if="glFeatures.releaseAssetLinkType">
- <gl-button
- data-testid="accordion-button"
- variant="link"
- class="gl-font-weight-bold"
- @click="toggleAssetsExpansion"
- >
- <gl-icon
- name="chevron-right"
- class="gl-transition-medium"
- :class="{ 'gl-rotate-90': isAssetsExpanded }"
- />
- {{ __('Assets') }}
- <gl-badge size="sm" variant="neutral" class="gl-display-inline-block">{{
- assets.count
- }}</gl-badge>
- </gl-button>
- <gl-collapse v-model="isAssetsExpanded">
- <div class="gl-pl-6 gl-pt-3 js-assets-list">
- <template v-for="(section, index) in sections">
- <h5 v-if="section.title" :key="`section-header-${index}`" class="gl-mb-2">
- {{ section.title }}
- </h5>
- <ul :key="`section-body-${index}`" class="list-unstyled gl-m-0">
- <li v-for="link in section.links" :key="link.url">
- <gl-link
- :href="link.directAssetUrl || link.url"
- class="gl-display-flex gl-align-items-center gl-line-height-24"
- >
- <gl-icon
- :name="section.iconName"
- class="gl-mr-2 gl-flex-shrink-0 gl-flex-grow-0"
- />
- {{ link.name }}
- <gl-icon
- v-if="link.external"
- v-gl-tooltip
- name="external-link"
- :aria-label="$options.externalLinkTooltipText"
- :title="$options.externalLinkTooltipText"
- data-testid="external-link-indicator"
- class="gl-ml-2 gl-flex-shrink-0 gl-flex-grow-0 gl-text-gray-400"
- />
- </gl-link>
- </li>
- </ul>
- </template>
- </div>
- </gl-collapse>
- </template>
-
- <template v-else>
- <b>
- {{ __('Assets') }}
- <span class="js-assets-count badge badge-pill">{{ assets.count }}</span>
- </b>
-
- <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">
- <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)')
- }}</span>
- </gl-link>
- </li>
- </ul>
-
- <div v-if="hasAssets" class="dropdown">
- <button
- type="button"
- class="btn btn-link"
- data-toggle="dropdown"
- aria-haspopup="true"
- aria-expanded="false"
- >
- <gl-icon name="doc-code" class="align-top gl-mr-2" />
- {{ __('Source code') }}
- <gl-icon name="chevron-down" />
- </button>
-
- <div class="js-sources-dropdown dropdown-menu">
- <li v-for="asset in assets.sources" :key="asset.url">
- <gl-link :href="asset.url">{{ __('Download') }} {{ asset.format }}</gl-link>
- </li>
- </div>
+ <gl-button
+ data-testid="accordion-button"
+ variant="link"
+ class="gl-font-weight-bold"
+ @click="toggleAssetsExpansion"
+ >
+ <gl-icon
+ name="chevron-right"
+ class="gl-transition-medium"
+ :class="{ 'gl-rotate-90': isAssetsExpanded }"
+ />
+ {{ __('Assets') }}
+ <gl-badge size="sm" variant="neutral" class="gl-display-inline-block">{{
+ assets.count
+ }}</gl-badge>
+ </gl-button>
+ <gl-collapse v-model="isAssetsExpanded">
+ <div class="gl-pl-6 gl-pt-3 js-assets-list">
+ <template v-for="(section, index) in sections">
+ <h5 v-if="section.title" :key="`section-header-${index}`" class="gl-mb-2">
+ {{ section.title }}
+ </h5>
+ <ul :key="`section-body-${index}`" class="list-unstyled gl-m-0">
+ <li v-for="link in section.links" :key="link.url" class="gl-display-flex">
+ <gl-link
+ :href="link.directAssetUrl || link.url"
+ class="gl-display-flex gl-align-items-center gl-line-height-24"
+ >
+ <gl-icon :name="section.iconName" class="gl-mr-2 gl-flex-shrink-0 gl-flex-grow-0" />
+ {{ link.name }}
+ <gl-icon
+ v-if="link.external"
+ v-gl-tooltip
+ name="external-link"
+ :aria-label="$options.externalLinkTooltipText"
+ :title="$options.externalLinkTooltipText"
+ data-testid="external-link-indicator"
+ class="gl-ml-2 gl-flex-shrink-0 gl-flex-grow-0 gl-text-gray-400"
+ />
+ </gl-link>
+ </li>
+ </ul>
+ </template>
</div>
- </template>
+ </gl-collapse>
</div>
</template>
diff --git a/app/assets/javascripts/releases/components/release_block_author.vue b/app/assets/javascripts/releases/components/release_block_author.vue
deleted file mode 100644
index 72c578068cd..00000000000
--- a/app/assets/javascripts/releases/components/release_block_author.vue
+++ /dev/null
@@ -1,42 +0,0 @@
-<script>
-import { GlSprintf } from '@gitlab/ui';
-import { __, sprintf } from '~/locale';
-import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
-
-export default {
- name: 'ReleaseBlockAuthor',
- components: {
- GlSprintf,
- UserAvatarLink,
- },
- props: {
- author: {
- type: Object,
- required: true,
- },
- },
- computed: {
- userImageAltDescription() {
- return this.author && this.author.username
- ? sprintf(__("%{username}'s avatar"), { username: this.author.username })
- : null;
- },
- },
-};
-</script>
-
-<template>
- <div class="d-flex">
- <gl-sprintf :message="__('by %{user}')">
- <template #user>
- <user-avatar-link
- class="gl-ml-2"
- :link-href="author.webUrl"
- :img-src="author.avatarUrl"
- :img-alt="userImageAltDescription"
- :tooltip-text="author.username"
- />
- </template>
- </gl-sprintf>
- </div>
-</template>
diff --git a/app/assets/javascripts/releases/components/release_block_header.vue b/app/assets/javascripts/releases/components/release_block_header.vue
index 95292a26bce..87538244f1a 100644
--- a/app/assets/javascripts/releases/components/release_block_header.vue
+++ b/app/assets/javascripts/releases/components/release_block_header.vue
@@ -1,5 +1,5 @@
<script>
-import { GlTooltipDirective, GlLink, GlBadge, GlButton, GlIcon } from '@gitlab/ui';
+import { GlTooltipDirective, GlLink, GlBadge, GlButton } from '@gitlab/ui';
import { BACK_URL_PARAM } from '~/releases/constants';
import { setUrlParams } from '~/lib/utils/url_utility';
@@ -8,7 +8,6 @@ export default {
components: {
GlLink,
GlBadge,
- GlIcon,
GlButton,
},
directives: {
@@ -55,11 +54,10 @@ export default {
v-gl-tooltip
category="primary"
variant="default"
+ icon="pencil"
class="gl-mr-3 js-edit-button ml-2 pb-2"
:title="__('Edit this release')"
:href="editLink"
- >
- <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
deleted file mode 100644
index 2247b4c0064..00000000000
--- a/app/assets/javascripts/releases/components/release_block_metadata.vue
+++ /dev/null
@@ -1,90 +0,0 @@
-<script>
-import { GlTooltipDirective, GlLink, GlIcon } from '@gitlab/ui';
-import { __, sprintf } from '~/locale';
-import timeagoMixin from '~/vue_shared/mixins/timeago';
-import ReleaseBlockAuthor from './release_block_author.vue';
-import ReleaseBlockMilestones from './release_block_milestones.vue';
-
-export default {
- name: 'ReleaseBlockMetadata',
- components: {
- GlIcon,
- GlLink,
- ReleaseBlockAuthor,
- ReleaseBlockMilestones,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- mixins: [timeagoMixin],
- props: {
- release: {
- type: Object,
- required: true,
- },
- },
- computed: {
- author() {
- return this.release.author;
- },
- commit() {
- return this.release.commit || {};
- },
- commitUrl() {
- return this.release.commitPath;
- },
- hasAuthor() {
- return Boolean(this.author);
- },
- releasedTimeAgo() {
- const now = new Date();
- const isFuture = now < new Date(this.release.releasedAt);
- const time = this.timeFormatted(this.release.releasedAt);
- return isFuture
- ? sprintf(__('will be released %{time}'), { time })
- : sprintf(__('released %{time}'), { time });
- },
- shouldRenderMilestones() {
- return Boolean(this.release.milestones?.length);
- },
- tagUrl() {
- return this.release.tagPath;
- },
- },
-};
-</script>
-
-<template>
- <div class="card-subtitle d-flex flex-wrap text-secondary">
- <div class="gl-mr-3">
- <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>
- <span v-else v-gl-tooltip.bottom :title="commit.title">{{ commit.shortId }}</span>
- </div>
-
- <div class="gl-mr-3">
- <gl-icon name="tag" class="align-middle" />
- <gl-link v-if="tagUrl" v-gl-tooltip.bottom :title="__('Tag')" :href="tagUrl">
- {{ release.tagName }}
- </gl-link>
- <span v-else v-gl-tooltip.bottom :title="__('Tag')">{{ release.tagName }}</span>
- </div>
-
- <release-block-milestones v-if="shouldRenderMilestones" :milestones="release.milestones" />
-
- <div class="gl-mr-2">
- &bull;
- <span
- v-gl-tooltip.bottom
- class="js-release-date-info"
- :title="tooltipTitle(release.releasedAt)"
- >
- {{ releasedTimeAgo }}
- </span>
- </div>
-
- <release-block-author v-if="hasAuthor" :author="author" />
- </div>
-</template>
diff --git a/app/assets/javascripts/releases/components/release_block_milestones.vue b/app/assets/javascripts/releases/components/release_block_milestones.vue
deleted file mode 100644
index 1da683764b3..00000000000
--- a/app/assets/javascripts/releases/components/release_block_milestones.vue
+++ /dev/null
@@ -1,50 +0,0 @@
-<script>
-import { GlTooltipDirective, GlLink, GlIcon } from '@gitlab/ui';
-import { n__ } from '~/locale';
-
-export default {
- name: 'ReleaseBlockMilestones',
- components: {
- GlLink,
- GlIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- milestones: {
- type: Array,
- required: true,
- },
- },
- computed: {
- labelText() {
- return n__('Milestone', 'Milestones', this.milestones.length);
- },
- },
-};
-</script>
-
-<template>
- <div>
- <div class="js-milestone-list-label">
- <gl-icon name="flag" class="align-middle" />
- <span class="js-label-text">{{ labelText }}</span>
- </div>
-
- <template v-for="(milestone, index) in milestones">
- <gl-link
- :key="milestone.id"
- v-gl-tooltip
- :title="milestone.description"
- :href="milestone.webUrl"
- class="mx-1 js-milestone-link"
- >
- {{ milestone.title }}
- </gl-link>
- <template v-if="index !== milestones.length - 1">
- &bull;
- </template>
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/releases/components/release_skeleton_loader.vue b/app/assets/javascripts/releases/components/release_skeleton_loader.vue
new file mode 100644
index 00000000000..054620af636
--- /dev/null
+++ b/app/assets/javascripts/releases/components/release_skeleton_loader.vue
@@ -0,0 +1,51 @@
+<script>
+import { GlSkeletonLoader } from '@gitlab/ui';
+
+export default {
+ name: 'ReleaseSkeletonLoader',
+ components: { GlSkeletonLoader },
+};
+</script>
+<template>
+ <gl-skeleton-loader :width="1248" :height="420">
+ <!-- Outside border -->
+ <path
+ d="M 4.5 0 C 2.0156486 0 0 2.0156486 0 4.5 L 0 415.5 C 0 417.98435 2.0156486 420 4.5 420 L 1243.5 420 C 1245.9844 420 1248 417.98435 1248 415.5 L 1248 4.5 C 1248 2.0156486 1245.9844 0 1243.5 0 L 4.5 0 z M 4.5 1 L 1243.5 1 C 1245.4476 1 1247 2.5523514 1247 4.5 L 1247 415.5 C 1247 417.44765 1245.4476 419 1243.5 419 L 4.5 419 C 2.5523514 419 1 417.44765 1 415.5 L 1 4.5 C 1 2.5523514 2.5523514 1 4.5 1 z "
+ />
+
+ <!-- Header bottom border -->
+ <rect x="0" y="63.5" width="1248" height="1" />
+
+ <!-- Release title -->
+ <rect x="16" y="20" width="293" height="24" />
+
+ <!-- Edit (pencil) button -->
+ <rect x="1207" y="16" rx="4" width="32" height="32" />
+
+ <!-- Asset link 1 -->
+ <rect x="40" y="121" rx="4" width="16" height="16" />
+ <rect x="60" y="125" width="116" height="8" />
+
+ <!-- Asset link 2 -->
+ <rect x="40" y="145" rx="4" width="16" height="16" />
+ <rect x="60" y="149" width="132" height="8" />
+
+ <!-- Asset link 3 -->
+ <rect x="40" y="169" rx="4" width="16" height="16" />
+ <rect x="60" y="173" width="140" height="8" />
+
+ <!-- Asset link 4 -->
+ <rect x="40" y="193" rx="4" width="16" height="16" />
+ <rect x="60" y="197" width="112" height="8" />
+
+ <!-- Release notes -->
+ <rect x="16" y="228" width="480" height="8" />
+ <rect x="16" y="252" width="560" height="8" />
+ <rect x="16" y="276" width="480" height="8" />
+ <rect x="16" y="300" width="560" height="8" />
+ <rect x="16" y="324" width="320" height="8" />
+
+ <!-- Footer top border -->
+ <rect x="0" y="373" width="1248" height="1" />
+ </gl-skeleton-loader>
+</template>
diff --git a/app/assets/javascripts/releases/components/releases_pagination_graphql.vue b/app/assets/javascripts/releases/components/releases_pagination_graphql.vue
index a4fe407a5bd..cb6f1fa18a1 100644
--- a/app/assets/javascripts/releases/components/releases_pagination_graphql.vue
+++ b/app/assets/javascripts/releases/components/releases_pagination_graphql.vue
@@ -13,14 +13,14 @@ export default {
},
},
methods: {
- ...mapActions('list', ['fetchReleasesGraphQl']),
+ ...mapActions('list', ['fetchReleases']),
onPrev(before) {
historyPushState(buildUrlWithCurrentLocation(`?before=${before}`));
- this.fetchReleasesGraphQl({ before });
+ this.fetchReleases({ before });
},
onNext(after) {
historyPushState(buildUrlWithCurrentLocation(`?after=${after}`));
- this.fetchReleasesGraphQl({ after });
+ this.fetchReleases({ after });
},
},
};
diff --git a/app/assets/javascripts/releases/components/releases_pagination_rest.vue b/app/assets/javascripts/releases/components/releases_pagination_rest.vue
index 992cc4cd469..334458a2302 100644
--- a/app/assets/javascripts/releases/components/releases_pagination_rest.vue
+++ b/app/assets/javascripts/releases/components/releases_pagination_rest.vue
@@ -7,18 +7,18 @@ export default {
name: 'ReleasesPaginationRest',
components: { TablePagination },
computed: {
- ...mapState('list', ['pageInfo']),
+ ...mapState('list', ['restPageInfo']),
},
methods: {
- ...mapActions('list', ['fetchReleasesRest']),
+ ...mapActions('list', ['fetchReleases']),
onChangePage(page) {
historyPushState(buildUrlWithCurrentLocation(`?page=${page}`));
- this.fetchReleasesRest({ page });
+ this.fetchReleases({ page });
},
},
};
</script>
<template>
- <table-pagination :change="onChangePage" :page-info="pageInfo" />
+ <table-pagination :change="onChangePage" :page-info="restPageInfo" />
</template>
diff --git a/app/assets/javascripts/releases/constants.js b/app/assets/javascripts/releases/constants.js
index 361cee70747..953e7b4189c 100644
--- a/app/assets/javascripts/releases/constants.js
+++ b/app/assets/javascripts/releases/constants.js
@@ -10,3 +10,5 @@ export const ASSET_LINK_TYPE = Object.freeze({
});
export const DEFAULT_ASSET_LINK_TYPE = ASSET_LINK_TYPE.OTHER;
+
+export const PAGE_SIZE = 20;
diff --git a/app/assets/javascripts/releases/mount_edit.js b/app/assets/javascripts/releases/mount_edit.js
index 623b18591a0..2f4b0e64e36 100644
--- a/app/assets/javascripts/releases/mount_edit.js
+++ b/app/assets/javascripts/releases/mount_edit.js
@@ -13,9 +13,6 @@ export default () => {
modules: {
detail: createDetailModule(el.dataset),
},
- featureFlags: {
- releaseShowPage: Boolean(gon.features?.releaseShowPage),
- },
});
return new Vue({
diff --git a/app/assets/javascripts/releases/mount_new.js b/app/assets/javascripts/releases/mount_new.js
index 10725e47740..5c481498ffb 100644
--- a/app/assets/javascripts/releases/mount_new.js
+++ b/app/assets/javascripts/releases/mount_new.js
@@ -13,9 +13,6 @@ export default () => {
modules: {
detail: createDetailModule(el.dataset),
},
- featureFlags: {
- releaseShowPage: Boolean(gon.features?.releaseShowPage),
- },
});
return new Vue({
diff --git a/app/assets/javascripts/releases/mount_show.js b/app/assets/javascripts/releases/mount_show.js
index eef015ee0a6..b09ecc9fb55 100644
--- a/app/assets/javascripts/releases/mount_show.js
+++ b/app/assets/javascripts/releases/mount_show.js
@@ -13,6 +13,9 @@ export default () => {
modules: {
detail: createDetailModule(el.dataset),
},
+ featureFlags: {
+ graphqlIndividualReleasePage: Boolean(gon.features?.graphqlIndividualReleasePage),
+ },
});
return new Vue({
diff --git a/app/assets/javascripts/releases/queries/all_releases.query.graphql b/app/assets/javascripts/releases/queries/all_releases.query.graphql
index 7a99f32fdfa..c35306f163d 100644
--- a/app/assets/javascripts/releases/queries/all_releases.query.graphql
+++ b/app/assets/javascripts/releases/queries/all_releases.query.graphql
@@ -1,68 +1,16 @@
-query allReleases($fullPath: ID!) {
+#import "./release.fragment.graphql"
+
+query allReleases($fullPath: ID!, $first: Int, $last: Int, $before: String, $after: String) {
project(fullPath: $fullPath) {
- releases(first: 20) {
- count
+ releases(first: $first, last: $last, before: $before, after: $after) {
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
- }
- }
- }
+ ...Release
+ }
+ pageInfo {
+ startCursor
+ hasPreviousPage
+ hasNextPage
+ endCursor
}
}
}
diff --git a/app/assets/javascripts/releases/queries/one_release.query.graphql b/app/assets/javascripts/releases/queries/one_release.query.graphql
new file mode 100644
index 00000000000..b893aea94b0
--- /dev/null
+++ b/app/assets/javascripts/releases/queries/one_release.query.graphql
@@ -0,0 +1,9 @@
+#import "./release.fragment.graphql"
+
+query oneRelease($fullPath: ID!, $tagName: String!) {
+ project(fullPath: $fullPath) {
+ release(tagName: $tagName) {
+ ...Release
+ }
+ }
+}
diff --git a/app/assets/javascripts/releases/queries/release.fragment.graphql b/app/assets/javascripts/releases/queries/release.fragment.graphql
new file mode 100644
index 00000000000..445ed616348
--- /dev/null
+++ b/app/assets/javascripts/releases/queries/release.fragment.graphql
@@ -0,0 +1,62 @@
+fragment Release on Release {
+ 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/getters.js b/app/assets/javascripts/releases/stores/getters.js
new file mode 100644
index 00000000000..6a1da63289c
--- /dev/null
+++ b/app/assets/javascripts/releases/stores/getters.js
@@ -0,0 +1,11 @@
+/**
+ * @returns {Boolean} `true` if all the feature flags
+ * required to enable the GraphQL endpoint are enabled
+ */
+export const useGraphQLEndpoint = rootState => {
+ return Boolean(
+ rootState.featureFlags.graphqlReleaseData &&
+ rootState.featureFlags.graphqlReleasesPage &&
+ rootState.featureFlags.graphqlMilestoneStats,
+ );
+};
diff --git a/app/assets/javascripts/releases/stores/index.js b/app/assets/javascripts/releases/stores/index.js
index b2e93d789d7..cc8b586964f 100644
--- a/app/assets/javascripts/releases/stores/index.js
+++ b/app/assets/javascripts/releases/stores/index.js
@@ -1,7 +1,9 @@
import Vuex from 'vuex';
+import * as getters from './getters';
export default ({ modules, featureFlags }) =>
new Vuex.Store({
modules,
state: { featureFlags },
+ getters,
});
diff --git a/app/assets/javascripts/releases/stores/modules/detail/actions.js b/app/assets/javascripts/releases/stores/modules/detail/actions.js
index 5b682a0ab0f..e8a46f40d20 100644
--- a/app/assets/javascripts/releases/stores/modules/detail/actions.js
+++ b/app/assets/javascripts/releases/stores/modules/detail/actions.js
@@ -3,7 +3,13 @@ import api from '~/api';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
-import { releaseToApiJson, apiJsonToRelease } from '~/releases/util';
+import {
+ releaseToApiJson,
+ apiJsonToRelease,
+ gqClient,
+ convertOneReleaseGraphQLResponse,
+} from '~/releases/util';
+import oneReleaseQuery from '~/releases/queries/one_release.query.graphql';
export const initializeRelease = ({ commit, dispatch, getters }) => {
if (getters.isExistingRelease) {
@@ -18,9 +24,29 @@ export const initializeRelease = ({ commit, dispatch, getters }) => {
return Promise.resolve();
};
-export const fetchRelease = ({ commit, state }) => {
+export const fetchRelease = ({ commit, state, rootState }) => {
commit(types.REQUEST_RELEASE);
+ if (rootState.featureFlags?.graphqlIndividualReleasePage) {
+ return gqClient
+ .query({
+ query: oneReleaseQuery,
+ variables: {
+ fullPath: state.projectPath,
+ tagName: state.tagName,
+ },
+ })
+ .then(response => {
+ const { data: release } = convertOneReleaseGraphQLResponse(response);
+
+ commit(types.RECEIVE_RELEASE_SUCCESS, release);
+ })
+ .catch(error => {
+ commit(types.RECEIVE_RELEASE_ERROR, error);
+ createFlash(s__('Release|Something went wrong while getting the release details'));
+ });
+ }
+
return api
.release(state.projectId, state.tagName)
.then(({ data }) => {
@@ -45,6 +71,9 @@ export const updateReleaseNotes = ({ commit }, notes) => commit(types.UPDATE_REL
export const updateReleaseMilestones = ({ commit }, milestones) =>
commit(types.UPDATE_RELEASE_MILESTONES, milestones);
+export const updateReleaseGroupMilestones = ({ commit }, groupMilestones) =>
+ commit(types.UPDATE_RELEASE_GROUP_MILESTONES, groupMilestones);
+
export const addEmptyAssetLink = ({ commit }) => {
commit(types.ADD_EMPTY_ASSET_LINK);
};
@@ -65,9 +94,9 @@ export const removeAssetLink = ({ commit }, linkIdToRemove) => {
commit(types.REMOVE_ASSET_LINK, linkIdToRemove);
};
-export const receiveSaveReleaseSuccess = ({ commit, state, rootState }, release) => {
+export const receiveSaveReleaseSuccess = ({ commit }, release) => {
commit(types.RECEIVE_SAVE_RELEASE_SUCCESS);
- redirectTo(rootState.featureFlags.releaseShowPage ? release._links.self : state.releasesPagePath);
+ redirectTo(release._links.self);
};
export const saveRelease = ({ commit, dispatch, getters }) => {
diff --git a/app/assets/javascripts/releases/stores/modules/detail/mutation_types.js b/app/assets/javascripts/releases/stores/modules/detail/mutation_types.js
index 7784e0cc741..1b2f5f33f02 100644
--- a/app/assets/javascripts/releases/stores/modules/detail/mutation_types.js
+++ b/app/assets/javascripts/releases/stores/modules/detail/mutation_types.js
@@ -9,6 +9,7 @@ export const UPDATE_CREATE_FROM = 'UPDATE_CREATE_FROM';
export const UPDATE_RELEASE_TITLE = 'UPDATE_RELEASE_TITLE';
export const UPDATE_RELEASE_NOTES = 'UPDATE_RELEASE_NOTES';
export const UPDATE_RELEASE_MILESTONES = 'UPDATE_RELEASE_MILESTONES';
+export const UPDATE_RELEASE_GROUP_MILESTONES = 'UPDATE_RELEASE_GROUP_MILESTONES';
export const REQUEST_SAVE_RELEASE = 'REQUEST_SAVE_RELEASE';
export const RECEIVE_SAVE_RELEASE_SUCCESS = 'RECEIVE_SAVE_RELEASE_SUCCESS';
diff --git a/app/assets/javascripts/releases/stores/modules/detail/mutations.js b/app/assets/javascripts/releases/stores/modules/detail/mutations.js
index 750f496665d..58a1958c5e2 100644
--- a/app/assets/javascripts/releases/stores/modules/detail/mutations.js
+++ b/app/assets/javascripts/releases/stores/modules/detail/mutations.js
@@ -13,6 +13,7 @@ export default {
name: '',
description: '',
milestones: [],
+ groupMilestones: [],
assets: {
links: [],
},
@@ -51,6 +52,10 @@ export default {
state.release.milestones = milestones;
},
+ [types.UPDATE_RELEASE_GROUP_MILESTONES](state, groupMilestones) {
+ state.release.groupMilestones = groupMilestones;
+ },
+
[types.REQUEST_SAVE_RELEASE](state) {
state.isUpdatingRelease = true;
},
diff --git a/app/assets/javascripts/releases/stores/modules/detail/state.js b/app/assets/javascripts/releases/stores/modules/detail/state.js
index a46e750df53..782a5c46d6c 100644
--- a/app/assets/javascripts/releases/stores/modules/detail/state.js
+++ b/app/assets/javascripts/releases/stores/modules/detail/state.js
@@ -1,5 +1,6 @@
export default ({
projectId,
+ projectPath,
markdownDocsPath,
markdownPreviewPath,
updateReleaseApiDocsPath,
@@ -12,6 +13,7 @@ export default ({
defaultBranch = null,
}) => ({
projectId,
+ projectPath,
markdownDocsPath,
markdownPreviewPath,
updateReleaseApiDocsPath,
diff --git a/app/assets/javascripts/releases/stores/modules/list/actions.js b/app/assets/javascripts/releases/stores/modules/list/actions.js
index 945b093b983..02e67415e63 100644
--- a/app/assets/javascripts/releases/stores/modules/list/actions.js
+++ b/app/assets/javascripts/releases/stores/modules/list/actions.js
@@ -8,55 +8,90 @@ import {
convertObjectPropsToCamelCase,
} from '~/lib/utils/common_utils';
import allReleasesQuery from '~/releases/queries/all_releases.query.graphql';
-import { gqClient, convertGraphQLResponse } from '../../../util';
+import { gqClient, convertAllReleasesGraphQLResponse } from '../../../util';
+import { PAGE_SIZE } from '../../../constants';
/**
- * Commits a mutation to update the state while the main endpoint is being requested.
+ * Gets a paginated list of releases from the server
+ *
+ * @param {Object} vuexParams
+ * @param {Object} actionParams
+ * @param {Number} [actionParams.page] The page number of results to fetch
+ * (this parameter is only used when fetching results from the REST API)
+ * @param {String} [actionParams.before] A GraphQL cursor. If provided,
+ * the items returned will proceed the provided cursor (this parameter is only
+ * used when fetching results from the GraphQL API).
+ * @param {String} [actionParams.after] A GraphQL cursor. If provided,
+ * the items returned will follow the provided cursor (this parameter is only
+ * used when fetching results from the GraphQL API).
*/
-export const requestReleases = ({ commit }) => commit(types.REQUEST_RELEASES);
+export const fetchReleases = ({ dispatch, rootGetters }, { page = 1, before, after }) => {
+ if (rootGetters.useGraphQLEndpoint) {
+ dispatch('fetchReleasesGraphQl', { before, after });
+ } else {
+ dispatch('fetchReleasesRest', { page });
+ }
+};
/**
- * Fetches the main endpoint.
- * Will dispatch requestNamespace action before starting the request.
- * Will dispatch receiveNamespaceSuccess if the request is successful
- * Will dispatch receiveNamesapceError if the request returns an error
- *
- * @param {String} projectId
+ * Gets a paginated list of releases from the GraphQL endpoint
*/
-export const fetchReleases = ({ dispatch, rootState, state }, { page = '1' }) => {
- dispatch('requestReleases');
+export const fetchReleasesGraphQl = (
+ { dispatch, commit, state },
+ { before = null, after = null },
+) => {
+ commit(types.REQUEST_RELEASES);
- 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'));
+ let paginationParams;
+ if (!before && !after) {
+ paginationParams = { first: PAGE_SIZE };
+ } else if (before && !after) {
+ paginationParams = { last: PAGE_SIZE, before };
+ } else if (!before && after) {
+ paginationParams = { first: PAGE_SIZE, after };
} else {
- api
- .releases(state.projectId, { page })
- .then(response => dispatch('receiveReleasesSuccess', response))
- .catch(() => dispatch('receiveReleasesError'));
+ throw new Error(
+ 'Both a `before` and an `after` parameter were provided to fetchReleasesGraphQl. These parameters cannot be used together.',
+ );
}
+
+ gqClient
+ .query({
+ query: allReleasesQuery,
+ variables: {
+ fullPath: state.projectPath,
+ ...paginationParams,
+ },
+ })
+ .then(response => {
+ const { data, paginationInfo: graphQlPageInfo } = convertAllReleasesGraphQLResponse(response);
+
+ commit(types.RECEIVE_RELEASES_SUCCESS, {
+ data,
+ graphQlPageInfo,
+ });
+ })
+ .catch(() => dispatch('receiveReleasesError'));
};
-export const receiveReleasesSuccess = ({ commit }, { data, headers }) => {
- const pageInfo = parseIntPagination(normalizeHeaders(headers));
- const camelCasedReleases = convertObjectPropsToCamelCase(data, { deep: true });
- commit(types.RECEIVE_RELEASES_SUCCESS, {
- data: camelCasedReleases,
- pageInfo,
- });
+/**
+ * Gets a paginated list of releases from the REST endpoint
+ */
+export const fetchReleasesRest = ({ dispatch, commit, state }, { page }) => {
+ commit(types.REQUEST_RELEASES);
+
+ api
+ .releases(state.projectId, { page })
+ .then(({ data, headers }) => {
+ const restPageInfo = parseIntPagination(normalizeHeaders(headers));
+ const camelCasedReleases = convertObjectPropsToCamelCase(data, { deep: true });
+
+ commit(types.RECEIVE_RELEASES_SUCCESS, {
+ data: camelCasedReleases,
+ restPageInfo,
+ });
+ })
+ .catch(() => dispatch('receiveReleasesError'));
};
export const receiveReleasesError = ({ commit }) => {
diff --git a/app/assets/javascripts/releases/stores/modules/list/mutations.js b/app/assets/javascripts/releases/stores/modules/list/mutations.js
index 99fc096264a..296487cfee2 100644
--- a/app/assets/javascripts/releases/stores/modules/list/mutations.js
+++ b/app/assets/javascripts/releases/stores/modules/list/mutations.js
@@ -17,11 +17,12 @@ export default {
* @param {Object} state
* @param {Object} resp
*/
- [types.RECEIVE_RELEASES_SUCCESS](state, { data, pageInfo }) {
+ [types.RECEIVE_RELEASES_SUCCESS](state, { data, restPageInfo, graphQlPageInfo }) {
state.hasError = false;
state.isLoading = false;
state.releases = data;
- state.pageInfo = pageInfo;
+ state.restPageInfo = restPageInfo;
+ state.graphQlPageInfo = graphQlPageInfo;
},
/**
@@ -35,5 +36,7 @@ export default {
state.isLoading = false;
state.releases = [];
state.hasError = true;
+ state.restPageInfo = {};
+ state.graphQlPageInfo = {};
},
};
diff --git a/app/assets/javascripts/releases/stores/modules/list/state.js b/app/assets/javascripts/releases/stores/modules/list/state.js
index 9fe313745fc..0bffaa0f9db 100644
--- a/app/assets/javascripts/releases/stores/modules/list/state.js
+++ b/app/assets/javascripts/releases/stores/modules/list/state.js
@@ -14,5 +14,6 @@ export default ({
isLoading: false,
hasError: false,
releases: [],
- pageInfo: {},
+ restPageInfo: {},
+ graphQlPageInfo: {},
});
diff --git a/app/assets/javascripts/releases/util.js b/app/assets/javascripts/releases/util.js
index d7fac7a9b65..445c429fd96 100644
--- a/app/assets/javascripts/releases/util.js
+++ b/app/assets/javascripts/releases/util.js
@@ -107,7 +107,24 @@ const convertMilestones = graphQLRelease => ({
});
/**
- * Converts the response from the GraphQL endpoint into the
+ * Converts a single release object fetched from GraphQL
+ * into a release object that matches the shape of the REST API
+ * (the same shape that is returned by `apiJsonToRelease` above.)
+ *
+ * @param graphQLRelease The release object returned from a GraphQL query
+ */
+export const convertGraphQLRelease = graphQLRelease => ({
+ ...convertScalarProperties(graphQLRelease),
+ ...convertAssets(graphQLRelease),
+ ...convertEvidences(graphQLRelease),
+ ...convertLinks(graphQLRelease),
+ ...convertCommit(graphQLRelease),
+ ...convertAuthor(graphQLRelease),
+ ...convertMilestones(graphQLRelease),
+});
+
+/**
+ * Converts the response from all_releases.query.graphql into the
* same shape as is returned from the Releases REST API.
*
* This allows the release components to use the response
@@ -115,16 +132,27 @@ const convertMilestones = graphQLRelease => ({
*
* @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 };
+export const convertAllReleasesGraphQLResponse = response => {
+ const releases = response.data.project.releases.nodes.map(convertGraphQLRelease);
+
+ const paginationInfo = {
+ ...response.data.project.releases.pageInfo,
+ };
+
+ return { data: releases, paginationInfo };
+};
+
+/**
+ * Converts the response from one_release.query.graphql 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 convertOneReleaseGraphQLResponse = response => {
+ const release = convertGraphQLRelease(response.data.project.release);
+
+ return { data: release };
};