diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 11:27:35 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-19 11:27:35 +0300 |
commit | 7e9c479f7de77702622631cff2628a9c8dcbc627 (patch) | |
tree | c8f718a08e110ad7e1894510980d2155a6549197 /app/assets/javascripts/pipeline_editor | |
parent | e852b0ae16db4052c1c567d9efa4facc81146e88 (diff) |
Add latest changes from gitlab-org/gitlab@13-6-stable-eev13.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/pipeline_editor')
6 files changed, 196 insertions, 0 deletions
diff --git a/app/assets/javascripts/pipeline_editor/components/text_editor.vue b/app/assets/javascripts/pipeline_editor/components/text_editor.vue new file mode 100644 index 00000000000..a925077c906 --- /dev/null +++ b/app/assets/javascripts/pipeline_editor/components/text_editor.vue @@ -0,0 +1,26 @@ +<script> +import EditorLite from '~/vue_shared/components/editor_lite.vue'; + +export default { + components: { + EditorLite, + }, + props: { + value: { + type: String, + required: false, + default: '', + }, + }, +}; +</script> +<template> + <div class="gl-border-solid gl-border-gray-100 gl-border-1"> + <editor-lite + v-model="value" + file-name="*.yml" + :editor-options="{ readOnly: true }" + @editor-ready="$emit('editor-ready')" + /> + </div> +</template> diff --git a/app/assets/javascripts/pipeline_editor/graphql/queries/blob_content.graphql b/app/assets/javascripts/pipeline_editor/graphql/queries/blob_content.graphql new file mode 100644 index 00000000000..9f1b5b13088 --- /dev/null +++ b/app/assets/javascripts/pipeline_editor/graphql/queries/blob_content.graphql @@ -0,0 +1,5 @@ +query getBlobContent($projectPath: ID!, $path: String, $ref: String!) { + blobContent(projectPath: $projectPath, path: $path, ref: $ref) @client { + rawData + } +} diff --git a/app/assets/javascripts/pipeline_editor/graphql/resolvers.js b/app/assets/javascripts/pipeline_editor/graphql/resolvers.js new file mode 100644 index 00000000000..7b8c70ac93e --- /dev/null +++ b/app/assets/javascripts/pipeline_editor/graphql/resolvers.js @@ -0,0 +1,16 @@ +import Api from '~/api'; + +export const resolvers = { + Query: { + blobContent(_, { projectPath, path, ref }) { + return { + __typename: 'BlobContent', + rawData: Api.getRawFile(projectPath, path, { ref }).then(({ data }) => { + return data; + }), + }; + }, + }, +}; + +export default resolvers; diff --git a/app/assets/javascripts/pipeline_editor/graphql/typedefs.graphql b/app/assets/javascripts/pipeline_editor/graphql/typedefs.graphql new file mode 100644 index 00000000000..f4f65262158 --- /dev/null +++ b/app/assets/javascripts/pipeline_editor/graphql/typedefs.graphql @@ -0,0 +1,7 @@ +type BlobContent { + rawData: String! +} + +extend type Query { + blobContent: BlobContent +} diff --git a/app/assets/javascripts/pipeline_editor/index.js b/app/assets/javascripts/pipeline_editor/index.js new file mode 100644 index 00000000000..ccd7b74064f --- /dev/null +++ b/app/assets/javascripts/pipeline_editor/index.js @@ -0,0 +1,34 @@ +import Vue from 'vue'; + +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import typeDefs from './graphql/typedefs.graphql'; +import { resolvers } from './graphql/resolvers'; + +import PipelineEditorApp from './pipeline_editor_app.vue'; + +export const initPipelineEditor = (selector = '#js-pipeline-editor') => { + const el = document.querySelector(selector); + + const { projectPath, defaultBranch, ciConfigPath } = el?.dataset; + + Vue.use(VueApollo); + + const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(resolvers, { typeDefs }), + }); + + return new Vue({ + el, + apolloProvider, + render(h) { + return h(PipelineEditorApp, { + props: { + projectPath, + defaultBranch, + ciConfigPath, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue new file mode 100644 index 00000000000..50b946af456 --- /dev/null +++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue @@ -0,0 +1,108 @@ +<script> +import { GlLoadingIcon, GlAlert, GlTabs, GlTab } from '@gitlab/ui'; +import { __, s__, sprintf } from '~/locale'; + +import TextEditor from './components/text_editor.vue'; +import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue'; + +import getBlobContent from './graphql/queries/blob_content.graphql'; + +export default { + components: { + GlLoadingIcon, + GlAlert, + GlTabs, + GlTab, + TextEditor, + PipelineGraph, + }, + props: { + projectPath: { + type: String, + required: true, + }, + defaultBranch: { + type: String, + required: false, + default: null, + }, + ciConfigPath: { + type: String, + required: true, + }, + }, + data() { + return { + error: null, + content: '', + editorIsReady: false, + }; + }, + apollo: { + content: { + query: getBlobContent, + variables() { + return { + projectPath: this.projectPath, + path: this.ciConfigPath, + ref: this.defaultBranch, + }; + }, + update(data) { + return data?.blobContent?.rawData; + }, + error(error) { + this.error = error; + }, + }, + }, + computed: { + loading() { + return this.$apollo.queries.content.loading; + }, + errorMessage() { + const { message: generalReason, networkError } = this.error ?? {}; + + const { data } = networkError?.response ?? {}; + // 404 for missing file uses `message` + // 400 for a missing ref uses `error` + const networkReason = data?.message ?? data?.error; + + const reason = networkReason ?? generalReason ?? this.$options.i18n.unknownError; + return sprintf(this.$options.i18n.errorMessageWithReason, { reason }); + }, + pipelineData() { + // Note data will loaded as part of https://gitlab.com/gitlab-org/gitlab/-/issues/263141 + return {}; + }, + }, + i18n: { + unknownError: __('Unknown Error'), + errorMessageWithReason: s__('Pipelines|CI file could not be loaded: %{reason}'), + tabEdit: s__('Pipelines|Write pipeline configuration'), + tabGraph: s__('Pipelines|Visualize'), + }, +}; +</script> + +<template> + <div class="gl-mt-4"> + <gl-alert v-if="error" :dismissible="false" variant="danger">{{ errorMessage }}</gl-alert> + <div class="gl-mt-4"> + <gl-loading-icon v-if="loading" size="lg" /> + <div v-else class="file-editor"> + <gl-tabs> + <!-- editor should be mounted when its tab is visible, so the container has a size --> + <gl-tab :title="$options.i18n.tabEdit" :lazy="!editorIsReady"> + <!-- editor should be mounted only once, when the tab is displayed --> + <text-editor v-model="content" @editor-ready="editorIsReady = true" /> + </gl-tab> + + <gl-tab :title="$options.i18n.tabGraph"> + <pipeline-graph :pipeline-data="pipelineData" /> + </gl-tab> + </gl-tabs> + </div> + </div> + </div> +</template> |