diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 08:27:35 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 08:27:35 +0000 |
commit | 7e9c479f7de77702622631cff2628a9c8dcbc627 (patch) | |
tree | c8f718a08e110ad7e1894510980d2155a6549197 /app/assets/javascripts/terraform | |
parent | e852b0ae16db4052c1c567d9efa4facc81146e88 (diff) | |
download | gitlab-ce-7e9c479f7de77702622631cff2628a9c8dcbc627.tar.gz |
Add latest changes from gitlab-org/gitlab@13-6-stable-eev13.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/terraform')
8 files changed, 355 insertions, 0 deletions
diff --git a/app/assets/javascripts/terraform/components/empty_state.vue b/app/assets/javascripts/terraform/components/empty_state.vue new file mode 100644 index 00000000000..d86ba3af2b1 --- /dev/null +++ b/app/assets/javascripts/terraform/components/empty_state.vue @@ -0,0 +1,44 @@ +<script> +import { GlEmptyState, GlIcon, GlLink, GlSprintf } from '@gitlab/ui'; + +export default { + components: { + GlEmptyState, + GlIcon, + GlLink, + GlSprintf, + }, + props: { + image: { + type: String, + required: true, + }, + }, +}; +</script> + +<template> + <gl-empty-state :svg-path="image" :title="s__('Terraform|Get started with Terraform')"> + <template #description> + <p> + <gl-sprintf + :message=" + s__( + 'Terraform|Find out how to use the %{linkStart}GitLab managed Terraform State%{linkEnd}', + ) + " + > + <template #link="{ content }"> + <gl-link + href="https://docs.gitlab.com/ee/user/infrastructure/index.html" + target="_blank" + > + {{ content }} + <gl-icon name="external-link" /> + </gl-link> + </template> + </gl-sprintf> + </p> + </template> + </gl-empty-state> +</template> diff --git a/app/assets/javascripts/terraform/components/states_table.vue b/app/assets/javascripts/terraform/components/states_table.vue new file mode 100644 index 00000000000..2e4c18c5a5b --- /dev/null +++ b/app/assets/javascripts/terraform/components/states_table.vue @@ -0,0 +1,101 @@ +<script> +import { GlBadge, GlIcon, GlSprintf, GlTable, GlTooltip } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import timeagoMixin from '~/vue_shared/mixins/timeago'; + +export default { + components: { + GlBadge, + GlIcon, + GlSprintf, + GlTable, + GlTooltip, + TimeAgoTooltip, + }, + mixins: [timeagoMixin], + props: { + states: { + required: true, + type: Array, + }, + }, + computed: { + fields() { + return [ + { + key: 'name', + thClass: 'gl-display-none', + }, + { + key: 'updated', + thClass: 'gl-display-none', + tdClass: 'gl-text-right', + }, + ]; + }, + }, + methods: { + createdByUserName(item) { + return item.latestVersion?.createdByUser?.name; + }, + lockedByUserName(item) { + return item.lockedByUser?.name || s__('Terraform|Unknown User'); + }, + updatedTime(item) { + return item.latestVersion?.updatedAt || item.updatedAt; + }, + }, +}; +</script> + +<template> + <gl-table :items="states" :fields="fields" data-testid="terraform-states-table"> + <template #cell(name)="{ item }"> + <div class="gl-display-flex align-items-center" data-testid="terraform-states-table-name"> + <p class="gl-font-weight-bold gl-m-0 gl-text-gray-900"> + {{ item.name }} + </p> + + <div v-if="item.lockedAt" id="terraformLockedBadgeContainer" class="gl-mx-2"> + <gl-badge id="terraformLockedBadge"> + <gl-icon name="lock" /> + {{ s__('Terraform|Locked') }} + </gl-badge> + + <gl-tooltip + container="terraformLockedBadgeContainer" + placement="right" + target="terraformLockedBadge" + > + <gl-sprintf :message="s__('Terraform|Locked by %{user} %{timeAgo}')"> + <template #user> + {{ lockedByUserName(item) }} + </template> + + <template #timeAgo> + {{ timeFormatted(item.lockedAt) }} + </template> + </gl-sprintf> + </gl-tooltip> + </div> + </div> + </template> + + <template #cell(updated)="{ item }"> + <p class="gl-m-0" data-testid="terraform-states-table-updated"> + <gl-sprintf :message="s__('Terraform|%{user} updated %{timeAgo}')"> + <template #user> + <span v-if="item.latestVersion"> + {{ createdByUserName(item) }} + </span> + </template> + + <template #timeAgo> + <time-ago-tooltip :time="updatedTime(item)" /> + </template> + </gl-sprintf> + </p> + </template> + </gl-table> +</template> diff --git a/app/assets/javascripts/terraform/components/terraform_list.vue b/app/assets/javascripts/terraform/components/terraform_list.vue new file mode 100644 index 00000000000..f614bdc8d43 --- /dev/null +++ b/app/assets/javascripts/terraform/components/terraform_list.vue @@ -0,0 +1,134 @@ +<script> +import { GlAlert, GlBadge, GlKeysetPagination, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui'; +import getStatesQuery from '../graphql/queries/get_states.query.graphql'; +import EmptyState from './empty_state.vue'; +import StatesTable from './states_table.vue'; +import { MAX_LIST_COUNT } from '../constants'; + +export default { + apollo: { + states: { + query: getStatesQuery, + variables() { + return { + projectPath: this.projectPath, + ...this.cursor, + }; + }, + update: data => { + return { + count: data?.project?.terraformStates?.count, + list: data?.project?.terraformStates?.nodes, + pageInfo: data?.project?.terraformStates?.pageInfo, + }; + }, + error() { + this.states = null; + }, + }, + }, + components: { + EmptyState, + GlAlert, + GlBadge, + GlKeysetPagination, + GlLoadingIcon, + GlTab, + GlTabs, + StatesTable, + }, + props: { + emptyStateImage: { + required: true, + type: String, + }, + projectPath: { + required: true, + type: String, + }, + }, + data() { + return { + cursor: { + first: MAX_LIST_COUNT, + after: null, + last: null, + before: null, + }, + }; + }, + computed: { + isLoading() { + return this.$apollo.queries.states.loading; + }, + pageInfo() { + return this.states?.pageInfo || {}; + }, + showPagination() { + return this.pageInfo.hasPreviousPage || this.pageInfo.hasNextPage; + }, + statesCount() { + return this.states?.count; + }, + statesList() { + return this.states?.list; + }, + }, + methods: { + updatePagination(item) { + if (item === this.pageInfo.endCursor) { + this.cursor = { + first: MAX_LIST_COUNT, + after: item, + last: null, + before: null, + }; + } else { + this.cursor = { + first: null, + after: null, + last: MAX_LIST_COUNT, + before: item, + }; + } + }, + }, +}; +</script> + +<template> + <section> + <gl-tabs> + <gl-tab> + <template slot="title"> + <p class="gl-m-0"> + {{ s__('Terraform|States') }} + <gl-badge v-if="statesCount">{{ statesCount }}</gl-badge> + </p> + </template> + + <gl-loading-icon v-if="isLoading" size="md" class="gl-mt-3" /> + + <div v-else-if="statesList"> + <div v-if="statesCount"> + <states-table :states="statesList" /> + + <div v-if="showPagination" class="gl-display-flex gl-justify-content-center gl-mt-5"> + <gl-keyset-pagination + v-bind="pageInfo" + @prev="updatePagination" + @next="updatePagination" + /> + </div> + </div> + + <empty-state v-else :image="emptyStateImage" /> + </div> + + <gl-alert v-else variant="danger" :dismissible="false"> + {{ s__('Terraform|An error occurred while loading your Terraform States') }} + </gl-alert> + </gl-tab> + </gl-tabs> + </section> +</template> diff --git a/app/assets/javascripts/terraform/constants.js b/app/assets/javascripts/terraform/constants.js new file mode 100644 index 00000000000..bbc4630f83b --- /dev/null +++ b/app/assets/javascripts/terraform/constants.js @@ -0,0 +1 @@ +export const MAX_LIST_COUNT = 25; diff --git a/app/assets/javascripts/terraform/graphql/fragments/state.fragment.graphql b/app/assets/javascripts/terraform/graphql/fragments/state.fragment.graphql new file mode 100644 index 00000000000..49f9ae3dd97 --- /dev/null +++ b/app/assets/javascripts/terraform/graphql/fragments/state.fragment.graphql @@ -0,0 +1,17 @@ +#import "~/graphql_shared/fragments/user.fragment.graphql" +#import "./state_version.fragment.graphql" + +fragment State on TerraformState { + id + name + lockedAt + updatedAt + + lockedByUser { + ...User + } + + latestVersion { + ...StateVersion + } +} diff --git a/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql b/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql new file mode 100644 index 00000000000..c7e9700c696 --- /dev/null +++ b/app/assets/javascripts/terraform/graphql/fragments/state_version.fragment.graphql @@ -0,0 +1,9 @@ +#import "~/graphql_shared/fragments/user.fragment.graphql" + +fragment StateVersion on TerraformStateVersion { + updatedAt + + createdByUser { + ...User + } +} diff --git a/app/assets/javascripts/terraform/graphql/queries/get_states.query.graphql b/app/assets/javascripts/terraform/graphql/queries/get_states.query.graphql new file mode 100644 index 00000000000..9453e32b1b5 --- /dev/null +++ b/app/assets/javascripts/terraform/graphql/queries/get_states.query.graphql @@ -0,0 +1,18 @@ +#import "../fragments/state.fragment.graphql" +#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" + +query getStates($projectPath: ID!, $first: Int, $last: Int, $before: String, $after: String) { + project(fullPath: $projectPath) { + terraformStates(first: $first, last: $last, before: $before, after: $after) { + count + + nodes { + ...State + } + + pageInfo { + ...PageInfo + } + } + } +} diff --git a/app/assets/javascripts/terraform/index.js b/app/assets/javascripts/terraform/index.js new file mode 100644 index 00000000000..579d2d14023 --- /dev/null +++ b/app/assets/javascripts/terraform/index.js @@ -0,0 +1,31 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import TerraformList from './components/terraform_list.vue'; +import createDefaultClient from '~/lib/graphql'; + +Vue.use(VueApollo); + +export default () => { + const el = document.querySelector('#js-terraform-list'); + + if (!el) { + return null; + } + + const defaultClient = createDefaultClient(); + + const { emptyStateImage, projectPath } = el.dataset; + + return new Vue({ + el, + apolloProvider: new VueApollo({ defaultClient }), + render(createElement) { + return createElement(TerraformList, { + props: { + emptyStateImage, + projectPath, + }, + }); + }, + }); +}; |