diff options
Diffstat (limited to 'app/assets/javascripts/environments')
15 files changed, 537 insertions, 38 deletions
diff --git a/app/assets/javascripts/environments/components/canary_ingress.vue b/app/assets/javascripts/environments/components/canary_ingress.vue index 02d660a91c1..30f3f9dfc75 100644 --- a/app/assets/javascripts/environments/components/canary_ingress.vue +++ b/app/assets/javascripts/environments/components/canary_ingress.vue @@ -17,6 +17,11 @@ export default { required: true, type: Object, }, + graphql: { + required: false, + type: Boolean, + default: false, + }, }, ingressOptions: Array(100 / 5 + 1) .fill(0) @@ -47,11 +52,17 @@ export default { canaryWeightId() { return uniqueId('canary-weight-'); }, + weight() { + if (this.graphql) { + return this.canaryIngress.canaryWeight; + } + return this.canaryIngress.canary_weight; + }, stableWeight() { - return (100 - this.canaryIngress.canary_weight).toString(); + return (100 - this.weight).toString(); }, canaryWeight() { - return this.canaryIngress.canary_weight.toString(); + return this.weight.toString(); }, }, methods: { diff --git a/app/assets/javascripts/environments/components/canary_update_modal.vue b/app/assets/javascripts/environments/components/canary_update_modal.vue index 8b1121c7158..fd4885a9dbd 100644 --- a/app/assets/javascripts/environments/components/canary_update_modal.vue +++ b/app/assets/javascripts/environments/components/canary_update_modal.vue @@ -71,7 +71,7 @@ export default { mutation: updateCanaryIngress, variables: { input: { - id: this.environment.global_id, + id: this.environment.global_id || this.environment.globalId, weight: this.weight, }, }, diff --git a/app/assets/javascripts/environments/components/commit.vue b/app/assets/javascripts/environments/components/commit.vue new file mode 100644 index 00000000000..54b94480685 --- /dev/null +++ b/app/assets/javascripts/environments/components/commit.vue @@ -0,0 +1,54 @@ +<script> +import { GlAvatar, GlAvatarLink, GlLink, GlTooltipDirective as GlTooltip } from '@gitlab/ui'; +import { escape } from 'lodash'; + +export default { + components: { + GlAvatar, + GlAvatarLink, + GlLink, + }, + directives: { + GlTooltip, + }, + props: { + commit: { + required: true, + type: Object, + }, + }, + computed: { + commitMessage() { + return this.commit?.message; + }, + commitAuthorPath() { + // eslint-disable-next-line @gitlab/require-i18n-strings + return this.commit?.author?.path || `mailto:${escape(this.commit?.authorEmail)}`; + }, + commitAuthorAvatar() { + return this.commit?.author?.avatarUrl || this.commit?.authorGravatarUrl; + }, + commitAuthor() { + return this.commit?.author?.name || this.commit?.authorName; + }, + commitPath() { + return this.commit?.commitPath; + }, + }, +}; +</script> +<template> + <div data-testid="deployment-commit" class="gl-display-flex gl-align-items-center"> + <gl-avatar-link v-gl-tooltip :title="commitAuthor" :href="commitAuthorPath"> + <gl-avatar :size="16" :src="commitAuthorAvatar" /> + </gl-avatar-link> + <gl-link + v-gl-tooltip + :title="commitMessage" + :href="commitPath" + class="gl-ml-3 gl-str-truncated" + > + {{ commitMessage }} + </gl-link> + </div> +</template> diff --git a/app/assets/javascripts/environments/components/deploy_board.vue b/app/assets/javascripts/environments/components/deploy_board.vue index c642a07fd1e..8a379ebdf66 100644 --- a/app/assets/javascripts/environments/components/deploy_board.vue +++ b/app/assets/javascripts/environments/components/deploy_board.vue @@ -1,5 +1,4 @@ <script> -/* eslint-disable @gitlab/vue-require-i18n-strings */ /** * Renders a deploy board. * @@ -17,11 +16,11 @@ import { GlTooltip, GlTooltipDirective, GlSafeHtmlDirective as SafeHtml, + GlSprintf, } from '@gitlab/ui'; import { isEmpty } from 'lodash'; -import { n__ } from '~/locale'; +import { s__, n__ } from '~/locale'; import instanceComponent from '~/vue_shared/components/deployment_instance.vue'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { STATUS_MAP, CANARY_STATUS } from '../constants'; import CanaryIngress from './canary_ingress.vue'; @@ -32,13 +31,13 @@ export default { GlIcon, GlLoadingIcon, GlLink, + GlSprintf, GlTooltip, }, directives: { GlTooltip: GlTooltipDirective, SafeHtml, }, - mixins: [glFeatureFlagsMixin()], props: { deployBoardData: { type: Object, @@ -57,6 +56,11 @@ export default { required: false, default: '', }, + graphql: { + type: Boolean, + required: false, + default: false, + }, }, computed: { canRenderDeployBoard() { @@ -65,8 +69,15 @@ export default { canRenderEmptyState() { return this.isEmpty; }, + canaryIngress() { + if (this.graphql) { + return this.deployBoardData.canaryIngress; + } + + return this.deployBoardData.canary_ingress; + }, canRenderCanaryWeight() { - return !isEmpty(this.deployBoardData.canary_ingress); + return !isEmpty(this.canaryIngress); }, instanceCount() { const { instances } = this.deployBoardData; @@ -90,8 +101,20 @@ export default { deployBoardSvg() { return deployBoardSvg; }, + rollbackUrl() { + if (this.graphql) { + return this.deployBoardData.rollbackUrl; + } + return this.deployBoardData.rollback_url; + }, + abortUrl() { + if (this.graphql) { + return this.deployBoardData.abortUrl; + } + return this.deployBoardData.abort_url; + }, deployBoardActions() { - return this.deployBoardData.rollback_url || this.deployBoardData.abort_url; + return this.rollbackUrl || this.abortUrl; }, statuses() { // Canary is not a pod status but it needs to be in the legend. @@ -106,7 +129,17 @@ export default { changeCanaryWeight(weight) { this.$emit('changeCanaryWeight', weight); }, + podName(instance) { + if (this.graphql) { + return instance.podName; + } + + return instance.pod_name; + }, }, + emptyStateText: s__( + 'DeployBoards|To see deployment progress for your environments, make sure you are deploying to %{codeStart}$KUBE_NAMESPACE%{codeEnd} and annotating with %{codeStart}app.gitlab.com/app=$CI_PROJECT_PATH_SLUG%{codeEnd} and %{codeStart}app.gitlab.com/env=$CI_ENVIRONMENT_SLUG%{codeEnd}.', + ), }; </script> <template> @@ -152,7 +185,7 @@ export default { :key="i" :status="instance.status" :tooltip-text="instance.tooltip" - :pod-name="instance.pod_name" + :pod-name="podName(instance)" :logs-path="logsPath" :stable="instance.stable" /> @@ -163,22 +196,23 @@ export default { <canary-ingress v-if="canRenderCanaryWeight" class="deploy-board-canary-ingress" - :canary-ingress="deployBoardData.canary_ingress" + :canary-ingress="canaryIngress" + :graphql="graphql" @change="changeCanaryWeight" /> <section v-if="deployBoardActions" class="deploy-board-actions"> <gl-link - v-if="deployBoardData.rollback_url" - :href="deployBoardData.rollback_url" + v-if="rollbackUrl" + :href="rollbackUrl" class="btn" data-method="post" rel="nofollow" >{{ __('Rollback') }}</gl-link > <gl-link - v-if="deployBoardData.abort_url" - :href="deployBoardData.abort_url" + v-if="abortUrl" + :href="abortUrl" class="btn btn-danger btn-inverted" data-method="post" rel="nofollow" @@ -196,11 +230,11 @@ export default { __('Kubernetes deployment not found') }}</span> <span> - To see deployment progress for your environments, make sure you are deploying to - <code>$KUBE_NAMESPACE</code> and annotating with - <code>app.gitlab.com/app=$CI_PROJECT_PATH_SLUG</code> - and - <code>app.gitlab.com/env=$CI_ENVIRONMENT_SLUG</code>. + <gl-sprintf :message="$options.emptyStateText"> + <template #code="{ content }"> + <code>{{ content }}</code> + </template> + </gl-sprintf> </span> </section> </div> diff --git a/app/assets/javascripts/environments/components/deploy_board_wrapper.vue b/app/assets/javascripts/environments/components/deploy_board_wrapper.vue new file mode 100644 index 00000000000..d9d77088ad3 --- /dev/null +++ b/app/assets/javascripts/environments/components/deploy_board_wrapper.vue @@ -0,0 +1,86 @@ +<script> +import { GlCollapse, GlButton } from '@gitlab/ui'; +import { __, s__ } from '~/locale'; +import setEnvironmentToChangeCanaryMutation from '../graphql/mutations/set_environment_to_change_canary.mutation.graphql'; +import DeployBoard from './deploy_board.vue'; + +export default { + components: { + DeployBoard, + GlButton, + GlCollapse, + }, + props: { + rolloutStatus: { + required: true, + type: Object, + }, + environment: { + required: true, + type: Object, + }, + }, + data() { + return { visible: false }; + }, + computed: { + icon() { + return this.visible ? 'angle-down' : 'angle-right'; + }, + label() { + return this.visible ? this.$options.i18n.collapse : this.$options.i18n.expand; + }, + isLoading() { + return this.rolloutStatus.status === 'loading'; + }, + isEmpty() { + return this.rolloutStatus.status === 'not_found'; + }, + }, + methods: { + toggleCollapse() { + this.visible = !this.visible; + }, + changeCanaryWeight(weight) { + this.$apollo.mutate({ + mutation: setEnvironmentToChangeCanaryMutation, + variables: { + environment: this.environment, + weight, + }, + }); + }, + }, + i18n: { + collapse: __('Collapse'), + expand: __('Expand'), + pods: s__('DeployBoard|Kubernetes Pods'), + }, +}; +</script> +<template> + <div> + <div> + <gl-button + class="gl-mr-4 gl-min-w-fit-content" + :icon="icon" + :aria-label="label" + size="small" + category="tertiary" + @click="toggleCollapse" + /> + <span>{{ $options.i18n.pods }}</span> + </div> + <gl-collapse :visible="visible"> + <deploy-board + :deploy-board-data="rolloutStatus" + :is-loading="isLoading" + :is-empty="isEmpty" + :environment="environment" + graphql + class="gl-reset-bg!" + @changeCanaryWeight="changeCanaryWeight" + /> + </gl-collapse> + </div> +</template> diff --git a/app/assets/javascripts/environments/components/deployment.vue b/app/assets/javascripts/environments/components/deployment.vue index ef43ca6bc33..f98edb6bb7d 100644 --- a/app/assets/javascripts/environments/components/deployment.vue +++ b/app/assets/javascripts/environments/components/deployment.vue @@ -1,25 +1,240 @@ <script> +import { + GlBadge, + GlButton, + GlCollapse, + GlIcon, + GlLink, + GlTooltipDirective as GlTooltip, + GlTruncate, +} from '@gitlab/ui'; +import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; +import { __, s__ } from '~/locale'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import DeploymentStatusBadge from './deployment_status_badge.vue'; +import Commit from './commit.vue'; export default { components: { + ClipboardButton, + Commit, DeploymentStatusBadge, + GlBadge, + GlButton, + GlCollapse, + GlIcon, + GlLink, + GlTruncate, + TimeAgoTooltip, + }, + directives: { + GlTooltip, }, props: { deployment: { type: Object, required: true, }, + latest: { + type: Boolean, + default: false, + required: false, + }, + }, + data() { + return { visible: false }; }, computed: { status() { return this.deployment?.status; }, + iid() { + return this.deployment?.iid; + }, + shortSha() { + return this.commit?.shortId; + }, + createdAt() { + return this.deployment?.createdAt; + }, + isMobile() { + return !GlBreakpointInstance.isDesktop(); + }, + detailsButton() { + return this.visible + ? { text: this.$options.i18n.hideDetails, icon: 'expand-up' } + : { text: this.$options.i18n.showDetails, icon: 'expand-down' }; + }, + detailsButtonClasses() { + return this.isMobile ? 'gl-sr-only' : ''; + }, + commit() { + return this.deployment?.commit; + }, + user() { + return this.deployment?.user; + }, + username() { + return `@${this.user.username}`; + }, + userPath() { + return this.user?.path; + }, + deployable() { + return this.deployment?.deployable; + }, + jobName() { + return this.deployable?.name; + }, + jobPath() { + return this.deployable?.buildPath; + }, + refLabel() { + return this.deployment?.tag ? this.$options.i18n.tag : this.$options.i18n.branch; + }, + ref() { + return this.deployment?.ref; + }, + refName() { + return this.ref?.name; + }, + refPath() { + return this.ref?.refPath; + }, + }, + methods: { + toggleCollapse() { + this.visible = !this.visible; + }, + }, + i18n: { + latestBadge: s__('Deployment|Latest Deployed'), + deploymentId: s__('Deployment|Deployment ID'), + copyButton: __('Copy commit SHA'), + commitSha: __('Commit SHA'), + showDetails: __('Show details'), + hideDetails: __('Hide details'), + triggerer: s__('Deployment|Triggerer'), + job: __('Job'), + api: __('API'), + branch: __('Branch'), + tag: __('Tag'), }, + headerClasses: [ + 'gl-display-flex', + 'gl-align-items-flex-start', + 'gl-md-align-items-center', + 'gl-justify-content-space-between', + 'gl-pr-6', + ], + headerDetailsClasses: [ + 'gl-display-flex', + 'gl-flex-direction-column', + 'gl-md-flex-direction-row', + 'gl-align-items-flex-start', + 'gl-md-align-items-center', + 'gl-font-sm', + 'gl-text-gray-700', + ], + deploymentStatusClasses: [ + 'gl-display-flex', + 'gl-gap-x-3', + 'gl-mr-0', + 'gl-md-mr-5', + 'gl-mb-3', + 'gl-md-mb-0', + ], }; </script> <template> <div> - <deployment-status-badge v-if="status" :status="status" /> + <div :class="$options.headerClasses"> + <div :class="$options.headerDetailsClasses"> + <div :class="$options.deploymentStatusClasses"> + <deployment-status-badge v-if="status" :status="status" /> + <gl-badge v-if="latest" variant="info">{{ $options.i18n.latestBadge }}</gl-badge> + </div> + <div class="gl-display-flex gl-align-items-center gl-gap-x-5"> + <div + v-if="iid" + v-gl-tooltip + class="gl-display-flex" + :title="$options.i18n.deploymentId" + :aria-label="$options.i18n.deploymentId" + > + <gl-icon ref="deployment-iid-icon" name="deployments" /> + <span class="gl-ml-2">#{{ iid }}</span> + </div> + <div + v-if="shortSha" + data-testid="deployment-commit-sha" + class="gl-font-monospace gl-display-flex gl-align-items-center" + > + <gl-icon ref="deployment-commit-icon" name="commit" class="gl-mr-2" /> + <span v-gl-tooltip :title="$options.i18n.commitSha">{{ shortSha }}</span> + <clipboard-button + :text="shortSha" + category="tertiary" + :title="$options.i18n.copyButton" + size="small" + /> + </div> + <time-ago-tooltip v-if="createdAt" :time="createdAt" class="gl-display-flex"> + <template #default="{ timeAgo }"> + <gl-icon name="calendar" /> + <span class="gl-mr-2 gl-white-space-nowrap">{{ timeAgo }}</span> + </template> + </time-ago-tooltip> + </div> + </div> + <gl-button + ref="details-toggle" + category="tertiary" + :icon="detailsButton.icon" + :button-text-classes="detailsButtonClasses" + @click="toggleCollapse" + > + {{ detailsButton.text }} + </gl-button> + </div> + <commit v-if="commit" :commit="commit" class="gl-mt-3" /> + <gl-collapse :visible="visible"> + <div + class="gl-display-flex gl-md-align-items-center gl-mt-5 gl-flex-direction-column gl-md-flex-direction-row gl-pr-4 gl-md-pr-0" + > + <div v-if="user" class="gl-display-flex gl-flex-direction-column gl-md-max-w-15p"> + <span class="gl-text-gray-500">{{ $options.i18n.triggerer }}</span> + <gl-link :href="userPath" class="gl-font-monospace gl-mt-3"> + <gl-truncate :text="username" with-tooltip /> + </gl-link> + </div> + <div + class="gl-display-flex gl-flex-direction-column gl-md-pl-7 gl-md-max-w-15p gl-mt-4 gl-md-mt-0" + > + <span class="gl-text-gray-500" :class="{ 'gl-ml-3': !deployable }"> + {{ $options.i18n.job }} + </span> + <gl-link v-if="jobPath" :href="jobPath" class="gl-font-monospace gl-mt-3"> + <gl-truncate :text="jobName" with-tooltip position="middle" /> + </gl-link> + <span v-else-if="jobName" class="gl-font-monospace gl-mt-3"> + <gl-truncate :text="jobName" with-tooltip position="middle" /> + </span> + <gl-badge v-else class="gl-font-monospace gl-mt-3" variant="info"> + {{ $options.i18n.api }} + </gl-badge> + </div> + <div + v-if="ref" + class="gl-display-flex gl-flex-direction-column gl-md-pl-7 gl-md-max-w-15p gl-mt-4 gl-md-mt-0" + > + <span class="gl-text-gray-500">{{ refLabel }}</span> + <gl-link :href="refPath" class="gl-font-monospace gl-mt-3"> + <gl-truncate :text="refName" with-tooltip /> + </gl-link> + </div> + </div> + </gl-collapse> </div> </template> diff --git a/app/assets/javascripts/environments/components/empty_state.vue b/app/assets/javascripts/environments/components/empty_state.vue index 977da12e8a9..36b9b647af7 100644 --- a/app/assets/javascripts/environments/components/empty_state.vue +++ b/app/assets/javascripts/environments/components/empty_state.vue @@ -12,10 +12,10 @@ export default { <template> <div class="empty-state"> <div class="text-content"> - <h4 class="blank-state-title js-blank-state-title"> + <h4 class="js-blank-state-title"> {{ s__("Environments|You don't have any environments right now") }} </h4> - <p class="blank-state-text"> + <p> {{ s__(`Environments|Environments are places where code gets deployed, such as staging or production.`) diff --git a/app/assets/javascripts/environments/components/environment_pin.vue b/app/assets/javascripts/environments/components/environment_pin.vue index 0b753d53ee3..f5a83b97552 100644 --- a/app/assets/javascripts/environments/components/environment_pin.vue +++ b/app/assets/javascripts/environments/components/environment_pin.vue @@ -6,6 +6,7 @@ import { GlDropdownItem } from '@gitlab/ui'; import { __ } from '~/locale'; import eventHub from '../event_hub'; +import cancelAutoStopMutation from '../graphql/mutations/cancel_auto_stop.mutation.graphql'; export default { components: { @@ -16,10 +17,22 @@ export default { type: String, required: true, }, + graphql: { + type: Boolean, + required: false, + default: false, + }, }, methods: { onPinClick() { - eventHub.$emit('cancelAutoStop', this.autoStopUrl); + if (this.graphql) { + this.$apollo.mutate({ + mutation: cancelAutoStopMutation, + variables: { autoStopUrl: this.autoStopUrl }, + }); + } else { + eventHub.$emit('cancelAutoStop', this.autoStopUrl); + } }, }, title: __('Prevent auto-stopping'), diff --git a/app/assets/javascripts/environments/components/new_environment_item.vue b/app/assets/javascripts/environments/components/new_environment_item.vue index d3624103c13..27a763fb9c4 100644 --- a/app/assets/javascripts/environments/components/new_environment_item.vue +++ b/app/assets/javascripts/environments/components/new_environment_item.vue @@ -4,10 +4,12 @@ import { GlDropdown, GlButton, GlLink, + GlSprintf, GlTooltipDirective as GlTooltip, } from '@gitlab/ui'; -import { __ } from '~/locale'; +import { __, s__ } from '~/locale'; import { truncate } from '~/lib/utils/text_utility'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import isLastDeployment from '../graphql/queries/is_last_deployment.query.graphql'; import ExternalUrl from './environment_external_url.vue'; import Actions from './environment_actions.vue'; @@ -18,6 +20,7 @@ import Monitoring from './environment_monitoring.vue'; import Terminal from './environment_terminal_button.vue'; import Delete from './environment_delete.vue'; import Deployment from './deployment.vue'; +import DeployBoardWrapper from './deploy_board_wrapper.vue'; export default { components: { @@ -25,19 +28,23 @@ export default { GlDropdown, GlButton, GlLink, + GlSprintf, Actions, Deployment, + DeployBoardWrapper, ExternalUrl, StopComponent, Rollback, Monitoring, Pin, Terminal, + TimeAgoTooltip, Delete, }, directives: { GlTooltip, }, + inject: ['helpPagePath'], props: { environment: { required: true, @@ -60,6 +67,10 @@ export default { i18n: { collapse: __('Collapse'), expand: __('Expand'), + emptyState: s__( + 'Environments|There are no deployments for this environment yet. %{linkStart}Learn more about setting up deployments.%{linkEnd}', + ), + autoStopIn: s__('Environment|Auto stop %{time}'), }, data() { return { visible: false }; @@ -83,12 +94,15 @@ export default { upcomingDeployment() { return this.environment?.upcomingDeployment; }, + hasDeployment() { + return Boolean(this.environment?.upcomingDeployment || this.environment?.lastDeployment); + }, actions() { if (!this.lastDeployment) { return []; } - const { manualActions = [], scheduledActions = [] } = this.lastDeployment; - const combinedActions = [...manualActions, ...scheduledActions]; + const { manualActions, scheduledActions } = this.lastDeployment; + const combinedActions = [...(manualActions ?? []), ...(scheduledActions ?? [])]; return combinedActions.map((action) => ({ ...action, })); @@ -133,6 +147,9 @@ export default { displayName() { return truncate(this.name, 80); }, + rolloutStatus() { + return this.environment?.rolloutStatus; + }, }, methods: { toggleCollapse() { @@ -144,7 +161,15 @@ export default { 'gl-border-t-solid', 'gl-border-1', 'gl-py-5', - 'gl-pl-7', + 'gl-md-pl-7', + 'gl-bg-gray-10', + ], + deployBoardClasses: [ + 'gl-border-gray-100', + 'gl-border-t-solid', + 'gl-border-1', + 'gl-py-4', + 'gl-md-pl-7', 'gl-bg-gray-10', ], }; @@ -176,7 +201,14 @@ export default { {{ displayName }} </gl-link> </div> - <div> + <div class="gl-display-flex gl-align-items-center"> + <p v-if="canShowAutoStopDate" class="gl-font-sm gl-text-gray-700 gl-mr-5 gl-mb-0"> + <gl-sprintf :message="$options.i18n.autoStopIn"> + <template #time> + <time-ago-tooltip :time="environment.autoStopAt" css-class="gl-font-weight-bold" /> + </template> + </gl-sprintf> + </p> <div class="btn-group table-action-buttons" role="group"> <external-url v-if="externalUrl" @@ -224,6 +256,7 @@ export default { <pin v-if="canShowAutoStopDate" :auto-stop-url="autoStopPath" + graphql data-track-action="click_button" data-track-label="environment_pin" /> @@ -254,11 +287,37 @@ export default { </div> </div> <gl-collapse :visible="visible"> - <div v-if="lastDeployment" :class="$options.deploymentClasses"> - <deployment :deployment="lastDeployment" :class="{ 'gl-ml-7': inFolder }" /> + <template v-if="hasDeployment"> + <div v-if="lastDeployment" :class="$options.deploymentClasses"> + <deployment + :deployment="lastDeployment" + :class="{ 'gl-ml-7': inFolder }" + latest + class="gl-pl-4" + /> + </div> + <div v-if="upcomingDeployment" :class="$options.deploymentClasses"> + <deployment + :deployment="upcomingDeployment" + :class="{ 'gl-ml-7': inFolder }" + class="gl-pl-4" + /> + </div> + </template> + <div v-else :class="$options.deploymentClasses"> + <gl-sprintf :message="$options.i18n.emptyState"> + <template #link="{ content }"> + <gl-link :href="helpPagePath">{{ content }}</gl-link> + </template> + </gl-sprintf> </div> - <div v-if="upcomingDeployment" :class="$options.deploymentClasses"> - <deployment :deployment="upcomingDeployment" :class="{ 'gl-ml-7': inFolder }" /> + <div v-if="rolloutStatus" :class="$options.deployBoardClasses"> + <deploy-board-wrapper + :rollout-status="rolloutStatus" + :environment="environment" + :class="{ 'gl-ml-7': inFolder }" + class="gl-pl-4" + /> </div> </gl-collapse> </div> diff --git a/app/assets/javascripts/environments/components/new_environments_app.vue b/app/assets/javascripts/environments/components/new_environments_app.vue index cb36e226d0e..3699f39b611 100644 --- a/app/assets/javascripts/environments/components/new_environments_app.vue +++ b/app/assets/javascripts/environments/components/new_environments_app.vue @@ -8,16 +8,19 @@ import pageInfoQuery from '../graphql/queries/page_info.query.graphql'; import environmentToDeleteQuery from '../graphql/queries/environment_to_delete.query.graphql'; import environmentToRollbackQuery from '../graphql/queries/environment_to_rollback.query.graphql'; import environmentToStopQuery from '../graphql/queries/environment_to_stop.query.graphql'; +import environmentToChangeCanaryQuery from '../graphql/queries/environment_to_change_canary.query.graphql'; import EnvironmentFolder from './new_environment_folder.vue'; import EnableReviewAppModal from './enable_review_app_modal.vue'; import StopEnvironmentModal from './stop_environment_modal.vue'; import EnvironmentItem from './new_environment_item.vue'; import ConfirmRollbackModal from './confirm_rollback_modal.vue'; import DeleteEnvironmentModal from './delete_environment_modal.vue'; +import CanaryUpdateModal from './canary_update_modal.vue'; export default { components: { DeleteEnvironmentModal, + CanaryUpdateModal, ConfirmRollbackModal, EnvironmentFolder, EnableReviewAppModal, @@ -56,6 +59,12 @@ export default { environmentToStop: { query: environmentToStopQuery, }, + environmentToChangeCanary: { + query: environmentToChangeCanaryQuery, + }, + weight: { + query: environmentToChangeCanaryQuery, + }, }, inject: ['newEnvironmentPath', 'canCreateEnvironment'], i18n: { @@ -80,6 +89,8 @@ export default { environmentToDelete: {}, environmentToRollback: {}, environmentToStop: {}, + environmentToChangeCanary: {}, + weight: 0, }; }, computed: { @@ -186,6 +197,7 @@ export default { <delete-environment-modal :environment="environmentToDelete" graphql /> <stop-environment-modal :environment="environmentToStop" graphql /> <confirm-rollback-modal :environment="environmentToRollback" graphql /> + <canary-update-modal :environment="environmentToChangeCanary" :weight="weight" /> <gl-tabs :action-secondary="addEnvironment" :action-primary="openReviewAppModal" diff --git a/app/assets/javascripts/environments/graphql/mutations/cancel_auto_stop.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/cancel_auto_stop.mutation.graphql index 22dfb8a7a89..0b473495710 100644 --- a/app/assets/javascripts/environments/graphql/mutations/cancel_auto_stop.mutation.graphql +++ b/app/assets/javascripts/environments/graphql/mutations/cancel_auto_stop.mutation.graphql @@ -1,5 +1,5 @@ -mutation cancelAutoStop($environment: LocalEnvironment) { - cancelAutoStop(environment: $environment) @client { +mutation cancelAutoStop($autoStopUrl: String!) { + cancelAutoStop(autoStopUrl: $autoStopUrl) @client { errors } } diff --git a/app/assets/javascripts/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql new file mode 100644 index 00000000000..0f48c1f5c05 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/mutations/set_environment_to_change_canary.mutation.graphql @@ -0,0 +1,3 @@ +mutation SetEnvironmentToChangeCanary($environment: LocalEnvironmentInput, $weight: Int!) { + setEnvironmentToChangeCanary(environment: $environment, weight: $weight) @client +} diff --git a/app/assets/javascripts/environments/graphql/queries/environment_to_change_canary.query.graphql b/app/assets/javascripts/environments/graphql/queries/environment_to_change_canary.query.graphql new file mode 100644 index 00000000000..b582ae55ba1 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/queries/environment_to_change_canary.query.graphql @@ -0,0 +1,4 @@ +query environmentToChangeCanary { + environmentToChangeCanary @client + weight @client +} diff --git a/app/assets/javascripts/environments/graphql/resolvers.js b/app/assets/javascripts/environments/graphql/resolvers.js index 812fa0c81f0..dc763b77157 100644 --- a/app/assets/javascripts/environments/graphql/resolvers.js +++ b/app/assets/javascripts/environments/graphql/resolvers.js @@ -10,6 +10,7 @@ import pollIntervalQuery from './queries/poll_interval.query.graphql'; import environmentToRollbackQuery from './queries/environment_to_rollback.query.graphql'; import environmentToStopQuery from './queries/environment_to_stop.query.graphql'; import environmentToDeleteQuery from './queries/environment_to_delete.query.graphql'; +import environmentToChangeCanaryQuery from './queries/environment_to_change_canary.query.graphql'; import pageInfoQuery from './queries/page_info.query.graphql'; const buildErrors = (errors = []) => ({ @@ -134,9 +135,15 @@ export const resolvers = (endpoint) => ({ data: { environmentToRollback: environment }, }); }, - cancelAutoStop(_, { environment: { autoStopPath } }) { + setEnvironmentToChangeCanary(_, { environment, weight }, { client }) { + client.writeQuery({ + query: environmentToChangeCanaryQuery, + data: { environmentToChangeCanary: environment, weight }, + }); + }, + cancelAutoStop(_, { autoStopUrl }) { return axios - .post(autoStopPath) + .post(autoStopUrl) .then(() => buildErrors()) .catch((err) => buildErrors([ diff --git a/app/assets/javascripts/environments/graphql/typedefs.graphql b/app/assets/javascripts/environments/graphql/typedefs.graphql index c02f6b2838a..b4d1f7326f6 100644 --- a/app/assets/javascripts/environments/graphql/typedefs.graphql +++ b/app/assets/javascripts/environments/graphql/typedefs.graphql @@ -77,9 +77,10 @@ extend type Mutation { stopEnvironment(environment: LocalEnvironmentInput): LocalErrors deleteEnvironment(environment: LocalEnvironmentInput): LocalErrors rollbackEnvironment(environment: LocalEnvironmentInput): LocalErrors - cancelAutoStop(environment: LocalEnvironmentInput): LocalErrors + cancelAutoStop(autoStopUrl: String!): LocalErrors setEnvironmentToDelete(environment: LocalEnvironmentInput): LocalErrors setEnvironmentToRollback(environment: LocalEnvironmentInput): LocalErrors setEnvironmentToStop(environment: LocalEnvironmentInput): LocalErrors + setEnvironmentToChangeCanary(environment: LocalEnvironmentInput, weight: Int): LocalErrors action(environment: LocalEnvironmentInput): LocalErrors } |