diff options
Diffstat (limited to 'app/assets/javascripts/environments')
18 files changed, 343 insertions, 44 deletions
diff --git a/app/assets/javascripts/environments/components/delete_environment_modal.vue b/app/assets/javascripts/environments/components/delete_environment_modal.vue index 2eb2be351b3..26ec882472b 100644 --- a/app/assets/javascripts/environments/components/delete_environment_modal.vue +++ b/app/assets/javascripts/environments/components/delete_environment_modal.vue @@ -1,6 +1,6 @@ <script> import { GlTooltipDirective, GlModal } from '@gitlab/ui'; -import { s__, sprintf } from '~/locale'; +import { __, s__, sprintf } from '~/locale'; import eventHub from '../event_hub'; export default { @@ -27,7 +27,7 @@ export default { }, cancelProps() { return { - text: s__('Cancel'), + text: __('Cancel'), }; }, confirmDeleteMessage() { diff --git a/app/assets/javascripts/environments/components/new_environment_folder.vue b/app/assets/javascripts/environments/components/new_environment_folder.vue new file mode 100644 index 00000000000..0615bdef537 --- /dev/null +++ b/app/assets/javascripts/environments/components/new_environment_folder.vue @@ -0,0 +1,69 @@ +<script> +import { GlCollapse, GlIcon, GlBadge, GlLink } from '@gitlab/ui'; +import folderQuery from '../graphql/queries/folder.query.graphql'; + +export default { + components: { + GlCollapse, + GlIcon, + GlBadge, + GlLink, + }, + props: { + nestedEnvironment: { + type: Object, + required: true, + }, + }, + data() { + return { visible: false }; + }, + apollo: { + folder: { + query: folderQuery, + variables() { + return { environment: this.nestedEnvironment.latest }; + }, + }, + }, + computed: { + icons() { + return this.visible + ? { caret: 'angle-down', folder: 'folder-open' } + : { caret: 'angle-right', folder: 'folder-o' }; + }, + count() { + return this.folder?.availableCount ?? 0; + }, + folderClass() { + return { 'gl-font-weight-bold': this.visible }; + }, + folderPath() { + return this.nestedEnvironment.latest.folderPath; + }, + }, + methods: { + toggleCollapse() { + this.visible = !this.visible; + }, + }, +}; +</script> +<template> + <div class="gl-border-b-solid gl-border-gray-100 gl-border-1 gl-px-3 gl-pt-3 gl-pb-5"> + <div class="gl-w-full gl-display-flex gl-align-items-center" @click="toggleCollapse"> + <gl-icon + class="gl-mr-2 gl-fill-current-color gl-text-gray-500" + :name="icons.caret" + :size="12" + /> + <gl-icon class="gl-mr-2 gl-fill-current-color gl-text-gray-500" :name="icons.folder" /> + <div class="gl-mr-2 gl-text-gray-500" :class="folderClass"> + {{ nestedEnvironment.name }} + </div> + <gl-badge size="sm" class="gl-mr-auto">{{ count }}</gl-badge> + <gl-link v-if="visible" :href="folderPath">{{ s__('Environments|Show all') }}</gl-link> + </div> + <gl-collapse :visible="visible" /> + </div> +</template> diff --git a/app/assets/javascripts/environments/components/new_environments_app.vue b/app/assets/javascripts/environments/components/new_environments_app.vue new file mode 100644 index 00000000000..a5526f9cd71 --- /dev/null +++ b/app/assets/javascripts/environments/components/new_environments_app.vue @@ -0,0 +1,47 @@ +<script> +import { GlBadge, GlTab, GlTabs } from '@gitlab/ui'; +import environmentAppQuery from '../graphql/queries/environmentApp.query.graphql'; +import EnvironmentFolder from './new_environment_folder.vue'; + +export default { + components: { + EnvironmentFolder, + GlBadge, + GlTab, + GlTabs, + }, + apollo: { + environmentApp: { + query: environmentAppQuery, + }, + }, + computed: { + folders() { + return this.environmentApp?.environments.filter((e) => e.size > 1) ?? []; + }, + availableCount() { + return this.environmentApp?.availableCount; + }, + }, +}; +</script> +<template> + <div> + <gl-tabs> + <gl-tab> + <template #title> + <span>{{ __('Available') }}</span> + <gl-badge size="sm" class="gl-tab-counter-badge"> + {{ availableCount }} + </gl-badge> + </template> + <environment-folder + v-for="folder in folders" + :key="folder.name" + class="gl-mb-3" + :nested-environment="folder" + /> + </gl-tab> + </gl-tabs> + </div> +</template> diff --git a/app/assets/javascripts/environments/components/rollback_modal_manager.vue b/app/assets/javascripts/environments/components/rollback_modal_manager.vue index 6aa7d96fdfd..3a8b9ebcb84 100644 --- a/app/assets/javascripts/environments/components/rollback_modal_manager.vue +++ b/app/assets/javascripts/environments/components/rollback_modal_manager.vue @@ -15,7 +15,6 @@ export default { data() { return { environment: null, - retryPath: '', visible: false, }; }, @@ -35,9 +34,9 @@ export default { name: environmentName, commitShortSha, commitUrl, + retryUrl: retryPath, isLastDeployment: parseBoolean(isLastDeployment), }; - this.retryPath = retryPath; this.visible = true; }); }); @@ -51,7 +50,5 @@ export default { v-model="visible" :environment="environment" :has-multiple-commits="false" - :retry-url="retryPath" /> - <div v-else></div> </template> diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js index f248e9ec079..206381e0b7e 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js +++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js @@ -8,7 +8,7 @@ Vue.use(Translate); Vue.use(VueApollo); const apolloProvider = new VueApollo({ - defaultClient: createDefaultClient({}, { assumeImmutableResults: true }), + defaultClient: createDefaultClient(), }); export default () => { diff --git a/app/assets/javascripts/environments/graphql/client.js b/app/assets/javascripts/environments/graphql/client.js new file mode 100644 index 00000000000..c734c2fba0c --- /dev/null +++ b/app/assets/javascripts/environments/graphql/client.js @@ -0,0 +1,25 @@ +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import environmentApp from './queries/environmentApp.query.graphql'; +import { resolvers } from './resolvers'; +import typeDefs from './typedefs.graphql'; + +export const apolloProvider = (endpoint) => { + const defaultClient = createDefaultClient(resolvers(endpoint), { + typeDefs, + }); + const { cache } = defaultClient; + + cache.writeQuery({ + query: environmentApp, + data: { + availableCount: 0, + environments: [], + reviewApp: {}, + stoppedCount: 0, + }, + }); + return new VueApollo({ + defaultClient, + }); +}; 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 new file mode 100644 index 00000000000..22dfb8a7a89 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/mutations/cancel_auto_stop.mutation.graphql @@ -0,0 +1,5 @@ +mutation cancelAutoStop($environment: LocalEnvironment) { + cancelAutoStop(environment: $environment) @client { + errors + } +} diff --git a/app/assets/javascripts/environments/graphql/mutations/delete_environment.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/delete_environment.mutation.graphql new file mode 100644 index 00000000000..9bb68857923 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/mutations/delete_environment.mutation.graphql @@ -0,0 +1,5 @@ +mutation deleteEnvironment($environment: LocalEnvironment) { + deleteEnvironment(environment: $environment) @client { + errors + } +} diff --git a/app/assets/javascripts/environments/graphql/mutations/rollback_environment.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/rollback_environment.mutation.graphql new file mode 100644 index 00000000000..3db4dc2b9a5 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/mutations/rollback_environment.mutation.graphql @@ -0,0 +1,5 @@ +mutation rollbackEnvironment($environment: LocalEnvironment) { + rollbackEnvironment(environment: $environment) @client { + errors + } +} diff --git a/app/assets/javascripts/environments/graphql/mutations/stop_environment.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/stop_environment.mutation.graphql new file mode 100644 index 00000000000..7eae0ef4ce4 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/mutations/stop_environment.mutation.graphql @@ -0,0 +1,5 @@ +mutation stopEnvironment($environment: LocalEnvironment) { + stopEnvironment(environment: $environment) @client { + errors + } +} diff --git a/app/assets/javascripts/environments/graphql/mutations/update_canary_ingress.mutation.graphql b/app/assets/javascripts/environments/graphql/mutations/update_canary_ingress.mutation.graphql index 04ea5cbcaef..936bf49a1ac 100644 --- a/app/assets/javascripts/environments/graphql/mutations/update_canary_ingress.mutation.graphql +++ b/app/assets/javascripts/environments/graphql/mutations/update_canary_ingress.mutation.graphql @@ -1,4 +1,4 @@ -mutation($input: EnvironmentsCanaryIngressUpdateInput!) { +mutation updateCanaryIngress($input: EnvironmentsCanaryIngressUpdateInput!) { environmentsCanaryIngressUpdate(input: $input) { errors } diff --git a/app/assets/javascripts/environments/graphql/queries/environmentApp.query.graphql b/app/assets/javascripts/environments/graphql/queries/environmentApp.query.graphql new file mode 100644 index 00000000000..faa76c0a42c --- /dev/null +++ b/app/assets/javascripts/environments/graphql/queries/environmentApp.query.graphql @@ -0,0 +1,8 @@ +query getEnvironmentApp { + environmentApp @client { + availableCount + environments + reviewApp + stoppedCount + } +} diff --git a/app/assets/javascripts/environments/graphql/queries/folder.query.graphql b/app/assets/javascripts/environments/graphql/queries/folder.query.graphql new file mode 100644 index 00000000000..3292c916b2e --- /dev/null +++ b/app/assets/javascripts/environments/graphql/queries/folder.query.graphql @@ -0,0 +1,7 @@ +query getEnvironmentFolder($environment: NestedLocalEnvironment) { + folder(environment: $environment) @client { + availableCount + environments + stoppedCount + } +} diff --git a/app/assets/javascripts/environments/graphql/resolvers.js b/app/assets/javascripts/environments/graphql/resolvers.js new file mode 100644 index 00000000000..8322b806370 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/resolvers.js @@ -0,0 +1,50 @@ +import axios from '~/lib/utils/axios_utils'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; + +const mapNestedEnvironment = (env) => ({ + ...convertObjectPropsToCamelCase(env, { deep: true }), + __typename: 'NestedLocalEnvironment', +}); +const mapEnvironment = (env) => ({ + ...convertObjectPropsToCamelCase(env), + __typename: 'LocalEnvironment', +}); + +export const resolvers = (endpoint) => ({ + Query: { + environmentApp() { + return axios.get(endpoint, { params: { nested: true } }).then((res) => ({ + availableCount: res.data.available_count, + environments: res.data.environments.map(mapNestedEnvironment), + reviewApp: { + ...convertObjectPropsToCamelCase(res.data.review_app), + __typename: 'ReviewApp', + }, + stoppedCount: res.data.stopped_count, + __typename: 'LocalEnvironmentApp', + })); + }, + folder(_, { environment: { folderPath } }) { + return axios.get(folderPath, { params: { per_page: 3 } }).then((res) => ({ + availableCount: res.data.available_count, + environments: res.data.environments.map(mapEnvironment), + stoppedCount: res.data.stopped_count, + __typename: 'LocalEnvironmentFolder', + })); + }, + }, + Mutations: { + stopEnvironment(_, { environment: { stopPath } }) { + return axios.post(stopPath); + }, + deleteEnvironment(_, { environment: { deletePath } }) { + return axios.delete(deletePath); + }, + rollbackEnvironment(_, { environment: { retryUrl } }) { + return axios.post(retryUrl); + }, + cancelAutoStop(_, { environment: { autoStopPath } }) { + return axios.post(autoStopPath); + }, + }, +}); diff --git a/app/assets/javascripts/environments/graphql/typedefs.graphql b/app/assets/javascripts/environments/graphql/typedefs.graphql new file mode 100644 index 00000000000..49ea719449e --- /dev/null +++ b/app/assets/javascripts/environments/graphql/typedefs.graphql @@ -0,0 +1,35 @@ +type LocalEnvironment { + id: Int! + globalId: ID! + name: String! + folderPath: String + stopPath: String + deletePath: String + retryUrl: String + autoStopPath: String +} + +type NestedLocalEnvironment { + name: String! + size: Int! + latest: LocalEnvironment! +} + +type LocalEnvironmentFolder { + environments: [LocalEnvironment!]! + availableCount: Int! + stoppedCount: Int! +} + +type ReviewApp { + canSetupReviewApp: Boolean! + allClustersEmpty: Boolean! + reviewSnippet: String +} + +type LocalEnvironmentApp { + stoppedCount: Int! + availableCount: Int! + environments: [NestedLocalEnvironment!]! + reviewApp: ReviewApp! +} diff --git a/app/assets/javascripts/environments/index.js b/app/assets/javascripts/environments/index.js index 5e33923d518..3b1d35c1f22 100644 --- a/app/assets/javascripts/environments/index.js +++ b/app/assets/javascripts/environments/index.js @@ -9,40 +9,43 @@ Vue.use(Translate); Vue.use(VueApollo); const apolloProvider = new VueApollo({ - defaultClient: createDefaultClient({}, { assumeImmutableResults: true }), + defaultClient: createDefaultClient(), }); -export default () => { - const el = document.getElementById('environments-list-view'); - return new Vue({ - el, - components: { - environmentsComponent, - }, - apolloProvider, - provide: { - projectPath: el.dataset.projectPath, - defaultBranchName: el.dataset.defaultBranchName, - }, - data() { - const environmentsData = el.dataset; +export default (el) => { + if (el) { + return new Vue({ + el, + components: { + environmentsComponent, + }, + apolloProvider, + provide: { + projectPath: el.dataset.projectPath, + defaultBranchName: el.dataset.defaultBranchName, + }, + data() { + const environmentsData = el.dataset; - return { - endpoint: environmentsData.environmentsDataEndpoint, - newEnvironmentPath: environmentsData.newEnvironmentPath, - helpPagePath: environmentsData.helpPagePath, - canCreateEnvironment: parseBoolean(environmentsData.canCreateEnvironment), - }; - }, - render(createElement) { - return createElement('environments-component', { - props: { - endpoint: this.endpoint, - newEnvironmentPath: this.newEnvironmentPath, - helpPagePath: this.helpPagePath, - canCreateEnvironment: this.canCreateEnvironment, - }, - }); - }, - }); + return { + endpoint: environmentsData.environmentsDataEndpoint, + newEnvironmentPath: environmentsData.newEnvironmentPath, + helpPagePath: environmentsData.helpPagePath, + canCreateEnvironment: parseBoolean(environmentsData.canCreateEnvironment), + }; + }, + render(createElement) { + return createElement('environments-component', { + props: { + endpoint: this.endpoint, + newEnvironmentPath: this.newEnvironmentPath, + helpPagePath: this.helpPagePath, + canCreateEnvironment: this.canCreateEnvironment, + }, + }); + }, + }); + } + + return null; }; diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js index 85cff73cc3e..0f9741784d6 100644 --- a/app/assets/javascripts/environments/mixins/environments_mixin.js +++ b/app/assets/javascripts/environments/mixins/environments_mixin.js @@ -6,7 +6,7 @@ import Visibility from 'visibilityjs'; import createFlash from '~/flash'; import Poll from '../../lib/utils/poll'; import { getParameterByName } from '../../lib/utils/url_utility'; -import { s__ } from '../../locale'; +import { s__, __ } from '../../locale'; import tabs from '../../vue_shared/components/navigation_tabs.vue'; import tablePagination from '../../vue_shared/components/pagination/table_pagination.vue'; import container from '../components/container.vue'; @@ -207,13 +207,13 @@ export default { tabs() { return [ { - name: s__('Available'), + name: __('Available'), scope: 'available', count: this.state.availableCounter, isActive: this.scope === 'available', }, { - name: s__('Stopped'), + name: __('Stopped'), scope: 'stopped', count: this.state.stoppedCounter, isActive: this.scope === 'stopped', diff --git a/app/assets/javascripts/environments/new_index.js b/app/assets/javascripts/environments/new_index.js new file mode 100644 index 00000000000..dd5c709c75a --- /dev/null +++ b/app/assets/javascripts/environments/new_index.js @@ -0,0 +1,38 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import { parseBoolean } from '../lib/utils/common_utils'; +import { apolloProvider } from './graphql/client'; +import EnvironmentsApp from './components/new_environments_app.vue'; + +Vue.use(VueApollo); + +export default (el) => { + if (el) { + const { + canCreateEnvironment, + endpoint, + newEnvironmentPath, + helpPagePath, + projectPath, + defaultBranchName, + } = el.dataset; + + return new Vue({ + el, + apolloProvider: apolloProvider(endpoint), + provide: { + projectPath, + defaultBranchName, + endpoint, + newEnvironmentPath, + helpPagePath, + canCreateEnvironment: parseBoolean(canCreateEnvironment), + }, + render(h) { + return h(EnvironmentsApp); + }, + }); + } + + return null; +}; |