summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/environments
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/environments')
-rw-r--r--app/assets/javascripts/environments/components/deploy_board.vue2
-rw-r--r--app/assets/javascripts/environments/environment_details/constants.js47
-rw-r--r--app/assets/javascripts/environments/environment_details/index.vue118
-rw-r--r--app/assets/javascripts/environments/graphql/queries/environment_details.query.graphql48
-rw-r--r--app/assets/javascripts/environments/helpers/deployment_data_transformation_helper.js62
-rw-r--r--app/assets/javascripts/environments/mount_show.js30
6 files changed, 305 insertions, 2 deletions
diff --git a/app/assets/javascripts/environments/components/deploy_board.vue b/app/assets/javascripts/environments/components/deploy_board.vue
index f22a0705b3d..31bc462f0b9 100644
--- a/app/assets/javascripts/environments/components/deploy_board.vue
+++ b/app/assets/javascripts/environments/components/deploy_board.vue
@@ -15,10 +15,10 @@ import {
GlLink,
GlTooltip,
GlTooltipDirective,
- GlSafeHtmlDirective as SafeHtml,
GlSprintf,
} from '@gitlab/ui';
import { isEmpty } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__, n__ } from '~/locale';
import InstanceComponent from '~/vue_shared/components/deployment_instance.vue';
import { STATUS_MAP, CANARY_STATUS } from '../constants';
diff --git a/app/assets/javascripts/environments/environment_details/constants.js b/app/assets/javascripts/environments/environment_details/constants.js
new file mode 100644
index 00000000000..56c70c354b7
--- /dev/null
+++ b/app/assets/javascripts/environments/environment_details/constants.js
@@ -0,0 +1,47 @@
+import { __ } from '~/locale';
+
+export const ENVIRONMENT_DETAILS_PAGE_SIZE = 20;
+export const ENVIRONMENT_DETAILS_TABLE_FIELDS = [
+ {
+ key: 'status',
+ label: __('Status'),
+ columnClass: 'gl-w-10p',
+ tdClass: 'gl-vertical-align-middle!',
+ },
+ {
+ key: 'id',
+ label: __('ID'),
+ columnClass: 'gl-w-5p',
+ tdClass: 'gl-vertical-align-middle!',
+ },
+ {
+ key: 'triggerer',
+ label: __('Triggerer'),
+ columnClass: 'gl-w-10p',
+ tdClass: 'gl-vertical-align-middle!',
+ },
+ {
+ key: 'commit',
+ label: __('Commit'),
+ columnClass: 'gl-w-20p',
+ tdClass: 'gl-vertical-align-middle!',
+ },
+ {
+ key: 'job',
+ label: __('Job'),
+ columnClass: 'gl-w-20p',
+ tdClass: 'gl-vertical-align-middle!',
+ },
+ {
+ key: 'created',
+ label: __('Created'),
+ columnClass: 'gl-w-10p',
+ tdClass: 'gl-vertical-align-middle! gl-white-space-nowrap',
+ },
+ {
+ key: 'deployed',
+ label: __('Deployed'),
+ columnClass: 'gl-w-10p',
+ tdClass: 'gl-vertical-align-middle! gl-white-space-nowrap',
+ },
+];
diff --git a/app/assets/javascripts/environments/environment_details/index.vue b/app/assets/javascripts/environments/environment_details/index.vue
new file mode 100644
index 00000000000..435d3fd820e
--- /dev/null
+++ b/app/assets/javascripts/environments/environment_details/index.vue
@@ -0,0 +1,118 @@
+<script>
+import {
+ GlTableLite,
+ GlAvatarLink,
+ GlAvatar,
+ GlLink,
+ GlTooltipDirective,
+ GlTruncate,
+ GlBadge,
+ GlLoadingIcon,
+} from '@gitlab/ui';
+import Commit from '~/vue_shared/components/commit.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import environmentDetailsQuery from '../graphql/queries/environment_details.query.graphql';
+import { convertToDeploymentTableRow } from '../helpers/deployment_data_transformation_helper';
+import DeploymentStatusBadge from '../components/deployment_status_badge.vue';
+import { ENVIRONMENT_DETAILS_PAGE_SIZE, ENVIRONMENT_DETAILS_TABLE_FIELDS } from './constants';
+
+export default {
+ components: {
+ GlLoadingIcon,
+ GlBadge,
+ DeploymentStatusBadge,
+ TimeAgoTooltip,
+ GlTableLite,
+ GlAvatarLink,
+ GlAvatar,
+ GlLink,
+ GlTruncate,
+ Commit,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ projectFullPath: {
+ type: String,
+ required: true,
+ },
+ environmentName: {
+ type: String,
+ required: true,
+ },
+ },
+ apollo: {
+ project: {
+ query: environmentDetailsQuery,
+ variables() {
+ return {
+ projectFullPath: this.projectFullPath,
+ environmentName: this.environmentName,
+ pageSize: ENVIRONMENT_DETAILS_PAGE_SIZE,
+ };
+ },
+ },
+ },
+ data() {
+ return {
+ project: {
+ loading: true,
+ },
+ loading: 0,
+ tableFields: ENVIRONMENT_DETAILS_TABLE_FIELDS,
+ };
+ },
+ computed: {
+ deployments() {
+ return this.project.environment?.deployments.nodes.map(convertToDeploymentTableRow) || [];
+ },
+ isLoading() {
+ return this.$apollo.queries.project.loading;
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-loading-icon v-if="isLoading" size="lg" class="mt-3" />
+ <gl-table-lite v-else :items="deployments" :fields="tableFields" fixed stacked="lg">
+ <template #table-colgroup="{ fields }">
+ <col v-for="field in fields" :key="field.key" :class="field.columnClass" />
+ </template>
+ <template #cell(status)="{ item }">
+ <div>
+ <deployment-status-badge :status="item.status" />
+ </div>
+ </template>
+ <template #cell(id)="{ item }">
+ <strong>{{ item.id }}</strong>
+ </template>
+ <template #cell(triggerer)="{ item }">
+ <gl-avatar-link :href="item.triggerer.webUrl">
+ <gl-avatar
+ v-gl-tooltip
+ :title="item.triggerer.name"
+ :src="item.triggerer.avatarUrl"
+ :size="24"
+ />
+ </gl-avatar-link>
+ </template>
+ <template #cell(commit)="{ item }">
+ <commit v-bind="item.commit" />
+ </template>
+ <template #cell(job)="{ item }">
+ <gl-link v-if="item.job" :href="item.job.webPath">
+ <gl-truncate :text="item.job.label" />
+ </gl-link>
+ <gl-badge v-else variant="info">{{ __('API') }}</gl-badge>
+ </template>
+ <template #cell(created)="{ item }">
+ <time-ago-tooltip :time="item.created" />
+ </template>
+ <template #cell(deployed)="{ item }">
+ <time-ago-tooltip :time="item.deployed" />
+ </template>
+ </gl-table-lite>
+ </div>
+</template>
diff --git a/app/assets/javascripts/environments/graphql/queries/environment_details.query.graphql b/app/assets/javascripts/environments/graphql/queries/environment_details.query.graphql
new file mode 100644
index 00000000000..e8f2a2cdf7f
--- /dev/null
+++ b/app/assets/javascripts/environments/graphql/queries/environment_details.query.graphql
@@ -0,0 +1,48 @@
+query getEnvironmentDetails($projectFullPath: ID!, $environmentName: String, $pageSize: Int) {
+ project(fullPath: $projectFullPath) {
+ id
+ name
+ fullPath
+ environment(name: $environmentName) {
+ id
+ name
+ deployments(orderBy: { createdAt: DESC }, first: $pageSize) {
+ nodes {
+ id
+ iid
+ status
+ ref
+ tag
+ job {
+ name
+ id
+ webPath
+ }
+ commit {
+ id
+ shortId
+ message
+ webUrl
+ authorGravatar
+ authorName
+ authorEmail
+ author {
+ id
+ name
+ avatarUrl
+ webUrl
+ }
+ }
+ triggerer {
+ id
+ webUrl
+ name
+ avatarUrl
+ }
+ createdAt
+ finishedAt
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/environments/helpers/deployment_data_transformation_helper.js b/app/assets/javascripts/environments/helpers/deployment_data_transformation_helper.js
new file mode 100644
index 00000000000..bfe92fe3125
--- /dev/null
+++ b/app/assets/javascripts/environments/helpers/deployment_data_transformation_helper.js
@@ -0,0 +1,62 @@
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+
+/**
+ * This function transforms Commit object coming from GraphQL to object compatible with app/assets/javascripts/vue_shared/components/commit.vue author object
+ * @param {Object} Commit
+ * @returns {Object}
+ */
+export const getAuthorFromCommit = (commit) => {
+ if (commit.author) {
+ return {
+ username: commit.author.name,
+ path: commit.author.webUrl,
+ avatar_url: commit.author.avatarUrl,
+ };
+ }
+ return {
+ username: commit.authorName,
+ path: `mailto:${commit.authorEmail}`,
+ avatar_url: commit.authorGravatar,
+ };
+};
+
+/**
+ * This function transforms deploymentNode object coming from GraphQL to object compatible with app/assets/javascripts/vue_shared/components/commit.vue
+ * @param {Object} deploymentNode
+ * @returns {Object}
+ */
+export const getCommitFromDeploymentNode = (deploymentNode) => {
+ if (!deploymentNode.commit) {
+ throw new Error("deploymentNode argument doesn't have 'commit' field", deploymentNode);
+ }
+ return {
+ title: deploymentNode.commit.message,
+ commitUrl: deploymentNode.commit.webUrl,
+ shortSha: deploymentNode.commit.shortId,
+ tag: deploymentNode.tag,
+ commitRef: {
+ name: deploymentNode.ref,
+ },
+ author: getAuthorFromCommit(deploymentNode.commit),
+ };
+};
+
+/**
+ * This function transforms deploymentNode object coming from GraphQL to object compatible with app/assets/javascripts/environments/environment_details/page.vue table
+ * @param {Object} deploymentNode
+ * @returns {Object}
+ */
+export const convertToDeploymentTableRow = (deploymentNode) => {
+ return {
+ status: deploymentNode.status.toLowerCase(),
+ id: deploymentNode.iid,
+ triggerer: deploymentNode.triggerer,
+ commit: getCommitFromDeploymentNode(deploymentNode),
+ job: deploymentNode.job && {
+ webPath: deploymentNode.job.webPath,
+ label: `${deploymentNode.job.name} (#${getIdFromGraphQLId(deploymentNode.job.id)})`,
+ },
+ created: deploymentNode.createdAt || '',
+ deployed: deploymentNode.finishedAt || '',
+ };
+};
diff --git a/app/assets/javascripts/environments/mount_show.js b/app/assets/javascripts/environments/mount_show.js
index 6df4fad83f2..ba816599ac2 100644
--- a/app/assets/javascripts/environments/mount_show.js
+++ b/app/assets/javascripts/environments/mount_show.js
@@ -1,6 +1,8 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import EnvironmentsDetailHeader from './components/environments_detail_header.vue';
+import { apolloProvider } from './graphql/client';
import environmentsMixin from './mixins/environments_mixin';
export const initHeader = () => {
@@ -41,7 +43,33 @@ export const initHeader = () => {
cancelAutoStopPath: dataset.environmentCancelAutoStopPath,
terminalPath: dataset.environmentTerminalPath,
metricsPath: dataset.environmentMetricsPath,
- updatePath: dataset.environmentEditPath,
+ updatePath: dataset.tnvironmentEditPath,
+ },
+ });
+ },
+ });
+};
+
+export const initPage = async () => {
+ if (!gon.features.environmentDetailsVue) {
+ return null;
+ }
+ const EnvironmentsDetailPageModule = await import('./environment_details/index.vue');
+ const EnvironmentsDetailPage = EnvironmentsDetailPageModule.default;
+ const dataElement = document.getElementById('environments-detail-view');
+ const dataSet = convertObjectPropsToCamelCase(JSON.parse(dataElement.dataset.details));
+
+ Vue.use(VueApollo);
+ const el = document.getElementById('environment_details_page');
+ return new Vue({
+ el,
+ apolloProvider: apolloProvider(),
+ provide: {},
+ render(createElement) {
+ return createElement(EnvironmentsDetailPage, {
+ props: {
+ projectFullPath: dataSet.projectFullPath,
+ environmentName: dataSet.name,
},
});
},