Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-09-20 02:18:09 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-09-20 02:18:09 +0300
commit6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde (patch)
treedc4d20fe6064752c0bd323187252c77e0a89144b /app/assets/javascripts/jobs
parent9868dae7fc0655bd7ce4a6887d4e6d487690eeed (diff)
Add latest changes from gitlab-org/gitlab@15-4-stable-eev15.4.0-rc42
Diffstat (limited to 'app/assets/javascripts/jobs')
-rw-r--r--app/assets/javascripts/jobs/components/filtered_search/constants.js13
-rw-r--r--app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue22
-rw-r--r--app/assets/javascripts/jobs/components/filtered_search/utils.js27
-rw-r--r--app/assets/javascripts/jobs/components/job/empty_state.vue (renamed from app/assets/javascripts/jobs/components/empty_state.vue)16
-rw-r--r--app/assets/javascripts/jobs/components/job/environments_block.vue (renamed from app/assets/javascripts/jobs/components/environments_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/erased_block.vue (renamed from app/assets/javascripts/jobs/components/erased_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/job_app.vue (renamed from app/assets/javascripts/jobs/components/job_app.vue)8
-rw-r--r--app/assets/javascripts/jobs/components/job/job_log_controllers.vue (renamed from app/assets/javascripts/jobs/components/job_log_controllers.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/legacy_manual_variables_form.vue (renamed from app/assets/javascripts/jobs/components/manual_variables_form.vue)23
-rw-r--r--app/assets/javascripts/jobs/components/job/manual_variables_form.vue195
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/artifacts_block.vue (renamed from app/assets/javascripts/jobs/components/artifacts_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/commit_block.vue (renamed from app/assets/javascripts/jobs/components/commit_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/job_container_item.vue (renamed from app/assets/javascripts/jobs/components/job_container_item.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue (renamed from app/assets/javascripts/jobs/components/job_retry_forward_deployment_modal.vue)2
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/job_sidebar_retry_button.vue (renamed from app/assets/javascripts/jobs/components/job_sidebar_retry_button.vue)4
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/jobs_container.vue (renamed from app/assets/javascripts/jobs/components/jobs_container.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue99
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar.vue (renamed from app/assets/javascripts/jobs/components/sidebar.vue)96
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue (renamed from app/assets/javascripts/jobs/components/sidebar_detail_row.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue102
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue (renamed from app/assets/javascripts/jobs/components/sidebar_job_details_container.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/stages_dropdown.vue (renamed from app/assets/javascripts/jobs/components/stages_dropdown.vue)4
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue (renamed from app/assets/javascripts/jobs/components/trigger_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/stuck_block.vue (renamed from app/assets/javascripts/jobs/components/stuck_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/unmet_prerequisites_block.vue (renamed from app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql1
-rw-r--r--app/assets/javascripts/jobs/components/table/jobs_table_app.vue24
-rw-r--r--app/assets/javascripts/jobs/constants.js8
-rw-r--r--app/assets/javascripts/jobs/index.js2
29 files changed, 535 insertions, 111 deletions
diff --git a/app/assets/javascripts/jobs/components/filtered_search/constants.js b/app/assets/javascripts/jobs/components/filtered_search/constants.js
new file mode 100644
index 00000000000..0daba892375
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/filtered_search/constants.js
@@ -0,0 +1,13 @@
+export const jobStatusValues = [
+ 'CANCELED',
+ 'CREATED',
+ 'FAILED',
+ 'MANUAL',
+ 'SUCCESS',
+ 'PENDING',
+ 'PREPARING',
+ 'RUNNING',
+ 'SCHEDULED',
+ 'SKIPPED',
+ 'WAITING_FOR_RESOURCE',
+];
diff --git a/app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue b/app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue
index fe7b7428c6e..e498a735898 100644
--- a/app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue
+++ b/app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue
@@ -11,6 +11,13 @@ export default {
components: {
GlFilteredSearch,
},
+ props: {
+ queryString: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ },
computed: {
tokens() {
return [
@@ -24,6 +31,20 @@ export default {
},
];
},
+ filteredSearchValue() {
+ if (this.queryString?.statuses) {
+ return [
+ {
+ type: 'status',
+ value: {
+ data: this.queryString?.statuses,
+ operator: '=',
+ },
+ },
+ ];
+ }
+ return [];
+ },
},
methods: {
onSubmit(filters) {
@@ -37,6 +58,7 @@ export default {
<gl-filtered-search
:placeholder="s__('Jobs|Filter jobs')"
:available-tokens="tokens"
+ :value="filteredSearchValue"
@submit="onSubmit"
/>
</template>
diff --git a/app/assets/javascripts/jobs/components/filtered_search/utils.js b/app/assets/javascripts/jobs/components/filtered_search/utils.js
new file mode 100644
index 00000000000..696cd8d4706
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/filtered_search/utils.js
@@ -0,0 +1,27 @@
+import { jobStatusValues } from './constants';
+
+// validates query string used for filtered search
+// on jobs table to ensure GraphQL query is called correctly
+export const validateQueryString = (queryStringObj) => {
+ // currently only one token is supported `statuses`
+ // this code will need to be expanded as more tokens
+ // are introduced
+
+ const filters = Object.keys(queryStringObj);
+
+ if (filters.includes('statuses')) {
+ const queryStringStatus = {
+ statuses: queryStringObj.statuses.toUpperCase(),
+ };
+
+ const found = jobStatusValues.find((status) => status === queryStringStatus.statuses);
+
+ if (found) {
+ return queryStringStatus;
+ }
+
+ return null;
+ }
+
+ return null;
+};
diff --git a/app/assets/javascripts/jobs/components/empty_state.vue b/app/assets/javascripts/jobs/components/job/empty_state.vue
index e31c13f40b0..65b9600e664 100644
--- a/app/assets/javascripts/jobs/components/empty_state.vue
+++ b/app/assets/javascripts/jobs/components/job/empty_state.vue
@@ -1,12 +1,16 @@
<script>
import { GlLink } from '@gitlab/ui';
-import ManualVariablesForm from './manual_variables_form.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import LegacyManualVariablesForm from '~/jobs/components/job/legacy_manual_variables_form.vue';
+import ManualVariablesForm from '~/jobs/components/job/manual_variables_form.vue';
export default {
components: {
GlLink,
+ LegacyManualVariablesForm,
ManualVariablesForm,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
illustrationPath: {
type: String,
@@ -50,6 +54,9 @@ export default {
},
},
computed: {
+ isGraphQL() {
+ return this.glFeatures?.graphqlJobApp;
+ },
shouldRenderManualVariables() {
return this.playable && !this.scheduled;
},
@@ -70,7 +77,12 @@ export default {
<p v-if="content" data-testid="job-empty-state-content">{{ content }}</p>
</div>
- <manual-variables-form v-if="shouldRenderManualVariables" :action="action" />
+ <template v-if="isGraphQL">
+ <manual-variables-form v-if="shouldRenderManualVariables" :action="action" />
+ </template>
+ <template v-else>
+ <legacy-manual-variables-form v-if="shouldRenderManualVariables" :action="action" />
+ </template>
<div class="text-content">
<div v-if="action && !shouldRenderManualVariables" class="text-center">
<gl-link
diff --git a/app/assets/javascripts/jobs/components/environments_block.vue b/app/assets/javascripts/jobs/components/job/environments_block.vue
index 4046e1ade82..4046e1ade82 100644
--- a/app/assets/javascripts/jobs/components/environments_block.vue
+++ b/app/assets/javascripts/jobs/components/job/environments_block.vue
diff --git a/app/assets/javascripts/jobs/components/erased_block.vue b/app/assets/javascripts/jobs/components/job/erased_block.vue
index a815689659e..a815689659e 100644
--- a/app/assets/javascripts/jobs/components/erased_block.vue
+++ b/app/assets/javascripts/jobs/components/job/erased_block.vue
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job/job_app.vue
index d5ee3423d70..81b65d175a7 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job/job_app.vue
@@ -6,15 +6,15 @@ import { mapGetters, mapState, mapActions } from 'vuex';
import { isScrolledToBottom } from '~/lib/utils/scroll_utils';
import { __, sprintf } from '~/locale';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
-import delayedJobMixin from '../mixins/delayed_job_mixin';
+import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin';
+import Log from '~/jobs/components/log/log.vue';
import EmptyState from './empty_state.vue';
import EnvironmentsBlock from './environments_block.vue';
import ErasedBlock from './erased_block.vue';
import LogTopBar from './job_log_controllers.vue';
-import Log from './log/log.vue';
-import Sidebar from './sidebar.vue';
import StuckBlock from './stuck_block.vue';
import UnmetPrerequisitesBlock from './unmet_prerequisites_block.vue';
+import Sidebar from './sidebar/sidebar.vue';
export default {
name: 'JobPageApp',
@@ -197,7 +197,7 @@ export default {
</script>
<template>
<div>
- <gl-loading-icon v-if="isLoading" size="lg" class="qa-loading-animation gl-mt-6" />
+ <gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-6" />
<template v-else-if="shouldRenderContent">
<div class="build-page" data-testid="job-content">
diff --git a/app/assets/javascripts/jobs/components/job_log_controllers.vue b/app/assets/javascripts/jobs/components/job/job_log_controllers.vue
index e9809ac661b..e9809ac661b 100644
--- a/app/assets/javascripts/jobs/components/job_log_controllers.vue
+++ b/app/assets/javascripts/jobs/components/job/job_log_controllers.vue
diff --git a/app/assets/javascripts/jobs/components/manual_variables_form.vue b/app/assets/javascripts/jobs/components/job/legacy_manual_variables_form.vue
index 07ef4f054b4..1898e02c94e 100644
--- a/app/assets/javascripts/jobs/components/manual_variables_form.vue
+++ b/app/assets/javascripts/jobs/components/job/legacy_manual_variables_form.vue
@@ -77,9 +77,6 @@ export default {
},
methods: {
...mapActions(['triggerManualJob']),
- canRemove(index) {
- return index < this.variables.length - 1;
- },
addEmptyVariable() {
const lastVar = this.variables[this.variables.length - 1];
@@ -93,12 +90,18 @@ export default {
id: uniqueId(),
});
},
+ canRemove(index) {
+ return index < this.variables.length - 1;
+ },
deleteVariable(id) {
this.variables.splice(
this.variables.findIndex((el) => el.id === id),
1,
);
},
+ inputRef(type, id) {
+ return `${this.$options.inputTypes[type]}-${id}`;
+ },
trigger() {
this.triggerBtnDisabled = true;
@@ -125,7 +128,7 @@ export default {
</gl-input-group-text>
</template>
<gl-form-input
- :ref="`${$options.inputTypes.key}-${variable.id}`"
+ :ref="inputRef('key', variable.id)"
v-model="variable.key"
:placeholder="$options.i18n.keyPlaceholder"
data-testid="ci-variable-key"
@@ -140,20 +143,13 @@ export default {
</gl-input-group-text>
</template>
<gl-form-input
- :ref="`${$options.inputTypes.value}-${variable.id}`"
+ :ref="inputRef('value', variable.id)"
v-model="variable.secretValue"
:placeholder="$options.i18n.valuePlaceholder"
data-testid="ci-variable-value"
/>
</gl-form-input-group>
- <!-- delete variable button placeholder to not break flex layout -->
- <div
- v-if="!canRemove(index)"
- class="gl-w-7 gl-mr-3"
- data-testid="delete-variable-btn-placeholder"
- ></div>
-
<gl-button
v-if="canRemove(index)"
class="gl-flex-grow-0 gl-flex-basis-0"
@@ -164,6 +160,9 @@ export default {
data-testid="delete-variable-btn"
@click="deleteVariable(variable.id)"
/>
+
+ <!-- delete variable button placeholder to not break flex layout -->
+ <div v-else class="gl-w-7 gl-mr-3" data-testid="delete-variable-btn-placeholder"></div>
</div>
<div class="gl-text-center gl-mt-5">
diff --git a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
new file mode 100644
index 00000000000..2f97301979c
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
@@ -0,0 +1,195 @@
+<script>
+import {
+ GlFormInputGroup,
+ GlInputGroupText,
+ GlFormInput,
+ GlButton,
+ GlLink,
+ GlSprintf,
+} from '@gitlab/ui';
+import { uniqueId } from 'lodash';
+import { mapActions } from 'vuex';
+import { helpPagePath } from '~/helpers/help_page_helper';
+import { s__ } from '~/locale';
+
+// This component is a port of ~/jobs/components/job/legacy_manual_variables_form.vue
+// It is meant to fetch the job information via GraphQL instead of REST API.
+
+export default {
+ name: 'ManualVariablesForm',
+ components: {
+ GlFormInputGroup,
+ GlInputGroupText,
+ GlFormInput,
+ GlButton,
+ GlLink,
+ GlSprintf,
+ },
+ props: {
+ action: {
+ type: Object,
+ required: false,
+ default: null,
+ validator(value) {
+ return (
+ value === null ||
+ (Object.prototype.hasOwnProperty.call(value, 'path') &&
+ Object.prototype.hasOwnProperty.call(value, 'method') &&
+ Object.prototype.hasOwnProperty.call(value, 'button_title'))
+ );
+ },
+ },
+ },
+ inputTypes: {
+ key: 'key',
+ value: 'value',
+ },
+ i18n: {
+ header: s__('CiVariables|Variables'),
+ keyLabel: s__('CiVariables|Key'),
+ valueLabel: s__('CiVariables|Value'),
+ keyPlaceholder: s__('CiVariables|Input variable key'),
+ valuePlaceholder: s__('CiVariables|Input variable value'),
+ formHelpText: s__(
+ 'CiVariables|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used as default',
+ ),
+ },
+ data() {
+ return {
+ variables: [
+ {
+ key: '',
+ secretValue: '',
+ id: uniqueId(),
+ },
+ ],
+ triggerBtnDisabled: false,
+ };
+ },
+ computed: {
+ variableSettings() {
+ return helpPagePath('ci/variables/index', { anchor: 'add-a-cicd-variable-to-a-project' });
+ },
+ preparedVariables() {
+ // we need to ensure no empty variables are passed to the API
+ // and secretValue should be snake_case when passed to the API
+ return this.variables
+ .filter((variable) => variable.key !== '')
+ .map(({ key, secretValue }) => ({ key, secret_value: secretValue }));
+ },
+ },
+ methods: {
+ ...mapActions(['triggerManualJob']),
+ addEmptyVariable() {
+ const lastVar = this.variables[this.variables.length - 1];
+
+ if (lastVar.key === '') {
+ return;
+ }
+
+ this.variables.push({
+ key: '',
+ secret_value: '',
+ id: uniqueId(),
+ });
+ },
+ canRemove(index) {
+ return index < this.variables.length - 1;
+ },
+ deleteVariable(id) {
+ this.variables.splice(
+ this.variables.findIndex((el) => el.id === id),
+ 1,
+ );
+ },
+ inputRef(type, id) {
+ return `${this.$options.inputTypes[type]}-${id}`;
+ },
+ trigger() {
+ this.triggerBtnDisabled = true;
+
+ this.triggerManualJob(this.preparedVariables);
+ },
+ },
+};
+</script>
+<template>
+ <div class="row gl-justify-content-center">
+ <div class="col-10" data-testid="manual-vars-form">
+ <label>{{ $options.i18n.header }}</label>
+
+ <div
+ v-for="(variable, index) in variables"
+ :key="variable.id"
+ class="gl-display-flex gl-align-items-center gl-mb-4"
+ data-testid="ci-variable-row"
+ >
+ <gl-form-input-group class="gl-mr-4 gl-flex-grow-1">
+ <template #prepend>
+ <gl-input-group-text>
+ {{ $options.i18n.keyLabel }}
+ </gl-input-group-text>
+ </template>
+ <gl-form-input
+ :ref="inputRef('key', variable.id)"
+ v-model="variable.key"
+ :placeholder="$options.i18n.keyPlaceholder"
+ data-testid="ci-variable-key"
+ @change="addEmptyVariable"
+ />
+ </gl-form-input-group>
+
+ <gl-form-input-group class="gl-flex-grow-2">
+ <template #prepend>
+ <gl-input-group-text>
+ {{ $options.i18n.valueLabel }}
+ </gl-input-group-text>
+ </template>
+ <gl-form-input
+ :ref="inputRef('value', variable.id)"
+ v-model="variable.secretValue"
+ :placeholder="$options.i18n.valuePlaceholder"
+ data-testid="ci-variable-value"
+ />
+ </gl-form-input-group>
+
+ <gl-button
+ v-if="canRemove(index)"
+ class="gl-flex-grow-0 gl-flex-basis-0"
+ category="tertiary"
+ variant="danger"
+ icon="clear"
+ :aria-label="__('Delete variable')"
+ data-testid="delete-variable-btn"
+ @click="deleteVariable(variable.id)"
+ />
+
+ <!-- delete variable button placeholder to not break flex layout -->
+ <div v-else class="gl-w-7 gl-mr-3" data-testid="delete-variable-btn-placeholder"></div>
+ </div>
+
+ <div class="gl-text-center gl-mt-5">
+ <gl-sprintf :message="$options.i18n.formHelpText">
+ <template #link="{ content }">
+ <gl-link :href="variableSettings" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+ <div class="gl-display-flex gl-justify-content-center gl-mt-5">
+ <gl-button
+ class="gl-mt-5"
+ variant="confirm"
+ category="primary"
+ :aria-label="__('Trigger manual job')"
+ :disabled="triggerBtnDisabled"
+ data-testid="trigger-manual-job-btn"
+ @click="trigger"
+ >
+ {{ action.button_title }}
+ </gl-button>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/artifacts_block.vue b/app/assets/javascripts/jobs/components/job/sidebar/artifacts_block.vue
index 2018942a7e8..2018942a7e8 100644
--- a/app/assets/javascripts/jobs/components/artifacts_block.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/artifacts_block.vue
diff --git a/app/assets/javascripts/jobs/components/commit_block.vue b/app/assets/javascripts/jobs/components/job/sidebar/commit_block.vue
index 7f25ca8a94d..7f25ca8a94d 100644
--- a/app/assets/javascripts/jobs/components/commit_block.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/commit_block.vue
diff --git a/app/assets/javascripts/jobs/components/job_container_item.vue b/app/assets/javascripts/jobs/components/job/sidebar/job_container_item.vue
index 097ab3b4cf6..097ab3b4cf6 100644
--- a/app/assets/javascripts/jobs/components/job_container_item.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/job_container_item.vue
diff --git a/app/assets/javascripts/jobs/components/job_retry_forward_deployment_modal.vue b/app/assets/javascripts/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue
index e83ed6c6332..913924cc7b1 100644
--- a/app/assets/javascripts/jobs/components/job_retry_forward_deployment_modal.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue
@@ -1,6 +1,6 @@
<script>
import { GlLink, GlModal } from '@gitlab/ui';
-import { JOB_RETRY_FORWARD_DEPLOYMENT_MODAL } from '../constants';
+import { JOB_RETRY_FORWARD_DEPLOYMENT_MODAL } from '~/jobs/constants';
export default {
name: 'JobRetryForwardDeploymentModal',
diff --git a/app/assets/javascripts/jobs/components/job_sidebar_retry_button.vue b/app/assets/javascripts/jobs/components/job/sidebar/job_sidebar_retry_button.vue
index a7bf365d35c..dd620977f0c 100644
--- a/app/assets/javascripts/jobs/components/job_sidebar_retry_button.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/job_sidebar_retry_button.vue
@@ -1,12 +1,12 @@
<script>
import { GlButton, GlModalDirective } from '@gitlab/ui';
import { mapGetters } from 'vuex';
-import { JOB_SIDEBAR } from '../constants';
+import { JOB_SIDEBAR_COPY } from '~/jobs/constants';
export default {
name: 'JobSidebarRetryButton',
i18n: {
- retryLabel: JOB_SIDEBAR.retry,
+ retryLabel: JOB_SIDEBAR_COPY.retry,
},
components: {
GlButton,
diff --git a/app/assets/javascripts/jobs/components/jobs_container.vue b/app/assets/javascripts/jobs/components/job/sidebar/jobs_container.vue
index df64b6422c7..df64b6422c7 100644
--- a/app/assets/javascripts/jobs/components/jobs_container.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/jobs_container.vue
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue b/app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue
new file mode 100644
index 00000000000..263b2d141c9
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue
@@ -0,0 +1,99 @@
+<script>
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { mapActions } from 'vuex';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
+import { JOB_SIDEBAR_COPY, forwardDeploymentFailureModalId } from '~/jobs/constants';
+import JobSidebarRetryButton from './job_sidebar_retry_button.vue';
+
+export default {
+ name: 'LegacySidebarHeader',
+ i18n: {
+ ...JOB_SIDEBAR_COPY,
+ },
+ forwardDeploymentFailureModalId,
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ components: {
+ GlButton,
+ JobSidebarRetryButton,
+ TooltipOnTruncate,
+ },
+ props: {
+ job: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ erasePath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ retryButtonCategory() {
+ return this.job.status && this.job.recoverable ? 'primary' : 'secondary';
+ },
+ },
+ methods: {
+ ...mapActions(['toggleSidebar']),
+ },
+};
+</script>
+
+<template>
+ <div class="gl-py-5 gl-display-flex gl-align-items-center">
+ <tooltip-on-truncate :title="job.name" truncate-target="child"
+ ><h4 class="gl-my-0 gl-mr-3 gl-text-truncate">
+ {{ job.name }}
+ </h4>
+ </tooltip-on-truncate>
+ <div class="gl-flex-grow-1 gl-flex-shrink-0 gl-text-right">
+ <gl-button
+ v-if="erasePath"
+ v-gl-tooltip.left
+ :title="$options.i18n.eraseLogButtonLabel"
+ :aria-label="$options.i18n.eraseLogButtonLabel"
+ :href="erasePath"
+ :data-confirm="$options.i18n.eraseLogConfirmText"
+ class="gl-mr-2"
+ data-testid="job-log-erase-link"
+ data-confirm-btn-variant="danger"
+ data-method="post"
+ icon="remove"
+ />
+ <job-sidebar-retry-button
+ v-if="job.retry_path"
+ v-gl-tooltip.left
+ :title="$options.i18n.retryJobButtonLabel"
+ :aria-label="$options.i18n.retryJobButtonLabel"
+ :category="retryButtonCategory"
+ :href="job.retry_path"
+ :modal-id="$options.forwardDeploymentFailureModalId"
+ variant="confirm"
+ data-qa-selector="retry_button"
+ data-testid="retry-button"
+ />
+ <gl-button
+ v-if="job.cancel_path"
+ v-gl-tooltip.left
+ :title="$options.i18n.cancelJobButtonLabel"
+ :aria-label="$options.i18n.cancelJobButtonLabel"
+ :href="job.cancel_path"
+ variant="danger"
+ icon="cancel"
+ data-method="post"
+ data-testid="cancel-button"
+ rel="nofollow"
+ />
+ <gl-button
+ :aria-label="$options.i18n.toggleSidebar"
+ category="tertiary"
+ class="gl-md-display-none gl-ml-2"
+ icon="chevron-double-lg-right"
+ @click="toggleSidebar"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/sidebar.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar.vue
index a42e45ee7e4..b0db48df01f 100644
--- a/app/assets/javascripts/jobs/components/sidebar.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar.vue
@@ -1,48 +1,40 @@
<script>
-import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { GlButton, GlIcon } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
-import { s__ } from '~/locale';
-import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
-import { JOB_SIDEBAR } from '../constants';
-import ArtifactsBlock from './artifacts_block.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { JOB_SIDEBAR_COPY, forwardDeploymentFailureModalId } from '~/jobs/constants';
import CommitBlock from './commit_block.vue';
-import JobRetryForwardDeploymentModal from './job_retry_forward_deployment_modal.vue';
-import JobSidebarRetryButton from './job_sidebar_retry_button.vue';
import JobsContainer from './jobs_container.vue';
+import JobRetryForwardDeploymentModal from './job_retry_forward_deployment_modal.vue';
import JobSidebarDetailsContainer from './sidebar_job_details_container.vue';
+import ArtifactsBlock from './artifacts_block.vue';
+import LegacySidebarHeader from './legacy_sidebar_header.vue';
+import SidebarHeader from './sidebar_header.vue';
import StagesDropdown from './stages_dropdown.vue';
import TriggerBlock from './trigger_block.vue';
-export const forwardDeploymentFailureModalId = 'forward-deployment-failure';
-
export default {
name: 'JobSidebar',
i18n: {
- eraseLogButtonLabel: s__('Job|Erase job log and artifacts'),
- eraseLogConfirmText: s__('Job|Are you sure you want to erase this job log and artifacts?'),
- cancelJobButtonLabel: s__('Job|Cancel'),
- retryJobButtonLabel: s__('Job|Retry'),
- ...JOB_SIDEBAR,
+ ...JOB_SIDEBAR_COPY,
},
borderTopClass: ['gl-border-t-solid', 'gl-border-t-1', 'gl-border-t-gray-100'],
forwardDeploymentFailureModalId,
- directives: {
- GlTooltip: GlTooltipDirective,
- },
components: {
ArtifactsBlock,
CommitBlock,
GlButton,
GlIcon,
JobsContainer,
- JobSidebarRetryButton,
JobRetryForwardDeploymentModal,
JobSidebarDetailsContainer,
+ LegacySidebarHeader,
+ SidebarHeader,
StagesDropdown,
- TooltipOnTruncate,
TriggerBlock,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
artifactHelpUrl: {
type: String,
@@ -58,9 +50,6 @@ export default {
computed: {
...mapGetters(['hasForwardDeploymentFailure']),
...mapState(['job', 'stages', 'jobs', 'selectedStage']),
- retryButtonCategory() {
- return this.job.status && this.job.recoverable ? 'primary' : 'secondary';
- },
hasArtifact() {
// the artifact object will always have a locked property
return Object.keys(this.job.artifact).length > 1;
@@ -68,8 +57,8 @@ export default {
hasTriggers() {
return !isEmpty(this.job.trigger);
},
- hasStages() {
- return this.job?.pipeline?.stages?.length > 0;
+ isGraphQL() {
+ return this.glFeatures?.graphqlJobApp;
},
commit() {
return this.job?.pipeline?.commit || {};
@@ -79,7 +68,7 @@ export default {
},
},
methods: {
- ...mapActions(['fetchJobsForStage', 'toggleSidebar']),
+ ...mapActions(['fetchJobsForStage']),
},
};
</script>
@@ -87,61 +76,8 @@ export default {
<aside class="right-sidebar build-sidebar" data-offset-top="101" data-spy="affix">
<div class="sidebar-container">
<div class="blocks-container">
- <div class="gl-py-5 gl-display-flex gl-align-items-center">
- <tooltip-on-truncate :title="job.name" truncate-target="child"
- ><h4 class="gl-my-0 gl-mr-3 gl-text-truncate">
- {{ job.name }}
- </h4>
- </tooltip-on-truncate>
- <div class="gl-flex-grow-1 gl-flex-shrink-0 gl-text-right">
- <gl-button
- v-if="erasePath"
- v-gl-tooltip.left
- :title="$options.i18n.eraseLogButtonLabel"
- :aria-label="$options.i18n.eraseLogButtonLabel"
- :href="erasePath"
- :data-confirm="$options.i18n.eraseLogConfirmText"
- class="gl-mr-2"
- data-testid="job-log-erase-link"
- data-confirm-btn-variant="danger"
- data-method="post"
- icon="remove"
- />
- <job-sidebar-retry-button
- v-if="job.retry_path"
- v-gl-tooltip.left
- :title="$options.i18n.retryJobButtonLabel"
- :aria-label="$options.i18n.retryJobButtonLabel"
- :category="retryButtonCategory"
- :href="job.retry_path"
- :modal-id="$options.forwardDeploymentFailureModalId"
- variant="confirm"
- data-qa-selector="retry_button"
- data-testid="retry-button"
- />
- <gl-button
- v-if="job.cancel_path"
- v-gl-tooltip.left
- :title="$options.i18n.cancelJobButtonLabel"
- :aria-label="$options.i18n.cancelJobButtonLabel"
- :href="job.cancel_path"
- variant="danger"
- icon="cancel"
- data-method="post"
- data-testid="cancel-button"
- rel="nofollow"
- />
- </div>
-
- <gl-button
- :aria-label="$options.i18n.toggleSidebar"
- category="tertiary"
- class="gl-md-display-none gl-ml-2"
- icon="chevron-double-lg-right"
- @click="toggleSidebar"
- />
- </div>
-
+ <sidebar-header v-if="isGraphQL" :erase-path="erasePath" :job="job" />
+ <legacy-sidebar-header v-else :erase-path="erasePath" :job="job" />
<div
v-if="job.terminal_path || job.new_issue_path"
class="gl-py-5"
diff --git a/app/assets/javascripts/jobs/components/sidebar_detail_row.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue
index 05567328660..05567328660 100644
--- a/app/assets/javascripts/jobs/components/sidebar_detail_row.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue
new file mode 100644
index 00000000000..523710598bf
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue
@@ -0,0 +1,102 @@
+<script>
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { mapActions } from 'vuex';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
+import { JOB_SIDEBAR_COPY, forwardDeploymentFailureModalId } from '~/jobs/constants';
+import JobSidebarRetryButton from './job_sidebar_retry_button.vue';
+
+// This component is a port of ~/jobs/components/job/sidebar/legacy_sidebar_header.vue
+// It is meant to fetch the job information via GraphQL instead of REST API.
+
+export default {
+ name: 'SidebarHeader',
+ i18n: {
+ ...JOB_SIDEBAR_COPY,
+ },
+ forwardDeploymentFailureModalId,
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ components: {
+ GlButton,
+ JobSidebarRetryButton,
+ TooltipOnTruncate,
+ },
+ props: {
+ job: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ erasePath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ computed: {
+ retryButtonCategory() {
+ return this.job.status && this.job.recoverable ? 'primary' : 'secondary';
+ },
+ },
+ methods: {
+ ...mapActions(['toggleSidebar']),
+ },
+};
+</script>
+
+<template>
+ <div class="gl-py-5 gl-display-flex gl-align-items-center">
+ <tooltip-on-truncate :title="job.name" truncate-target="child"
+ ><h4 class="gl-my-0 gl-mr-3 gl-text-truncate">
+ {{ job.name }}
+ </h4>
+ </tooltip-on-truncate>
+ <div class="gl-flex-grow-1 gl-flex-shrink-0 gl-text-right">
+ <gl-button
+ v-if="erasePath"
+ v-gl-tooltip.left
+ :title="$options.i18n.eraseLogButtonLabel"
+ :aria-label="$options.i18n.eraseLogButtonLabel"
+ :href="erasePath"
+ :data-confirm="$options.i18n.eraseLogConfirmText"
+ class="gl-mr-2"
+ data-testid="job-log-erase-link"
+ data-confirm-btn-variant="danger"
+ data-method="post"
+ icon="remove"
+ />
+ <job-sidebar-retry-button
+ v-if="job.retry_path"
+ v-gl-tooltip.left
+ :title="$options.i18n.retryJobButtonLabel"
+ :aria-label="$options.i18n.retryJobButtonLabel"
+ :category="retryButtonCategory"
+ :href="job.retry_path"
+ :modal-id="$options.forwardDeploymentFailureModalId"
+ variant="confirm"
+ data-qa-selector="retry_button"
+ data-testid="retry-button"
+ />
+ <gl-button
+ v-if="job.cancel_path"
+ v-gl-tooltip.left
+ :title="$options.i18n.cancelJobButtonLabel"
+ :aria-label="$options.i18n.cancelJobButtonLabel"
+ :href="job.cancel_path"
+ variant="danger"
+ icon="cancel"
+ data-method="post"
+ data-testid="cancel-button"
+ rel="nofollow"
+ />
+ <gl-button
+ :aria-label="$options.i18n.toggleSidebar"
+ category="tertiary"
+ class="gl-md-display-none gl-ml-2"
+ icon="chevron-double-lg-right"
+ @click="toggleSidebar"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue
index 3b1509e5be5..3b1509e5be5 100644
--- a/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue
diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/job/sidebar/stages_dropdown.vue
index 7c4811b2d6f..e3afe9b7c67 100644
--- a/app/assets/javascripts/jobs/components/stages_dropdown.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/stages_dropdown.vue
@@ -4,14 +4,14 @@ import { isEmpty } from 'lodash';
import Mousetrap from 'mousetrap';
import { s__ } from '~/locale';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { clickCopyToClipboardButton } from '~/behaviors/copy_to_clipboard';
import { keysFor, MR_COPY_SOURCE_BRANCH_NAME } from '~/behaviors/shortcuts/keybindings';
export default {
components: {
CiIcon,
- clipboardButton,
+ ClipboardButton,
GlDropdown,
GlDropdownItem,
GlLink,
diff --git a/app/assets/javascripts/jobs/components/trigger_block.vue b/app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue
index 1afc1c9a595..1afc1c9a595 100644
--- a/app/assets/javascripts/jobs/components/trigger_block.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue
diff --git a/app/assets/javascripts/jobs/components/stuck_block.vue b/app/assets/javascripts/jobs/components/job/stuck_block.vue
index d7a26d22406..d7a26d22406 100644
--- a/app/assets/javascripts/jobs/components/stuck_block.vue
+++ b/app/assets/javascripts/jobs/components/job/stuck_block.vue
diff --git a/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue b/app/assets/javascripts/jobs/components/job/unmet_prerequisites_block.vue
index c9747ca9f02..c9747ca9f02 100644
--- a/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue
+++ b/app/assets/javascripts/jobs/components/job/unmet_prerequisites_block.vue
diff --git a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql
index 98b51e8c2c4..851be211b25 100644
--- a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql
+++ b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql
@@ -11,6 +11,7 @@ query getJobs($fullPath: ID!, $after: String, $first: Int = 30, $statuses: [CiJo
}
nodes {
artifacts {
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
nodes {
downloadPath
fileType
diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
index c2f460cb647..0a4757d11a8 100644
--- a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
+++ b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
@@ -2,7 +2,9 @@
import { GlAlert, GlSkeletonLoader, GlIntersectionObserver, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import createFlash from '~/flash';
+import { setUrlParams, updateHistory, queryToObject } from '~/lib/utils/url_utility';
import JobsFilteredSearch from '../filtered_search/jobs_filtered_search.vue';
+import { validateQueryString } from '../filtered_search/utils';
import GetJobs from './graphql/queries/get_jobs.query.graphql';
import JobsTable from './jobs_table.vue';
import JobsTableEmptyState from './jobs_table_empty_state.vue';
@@ -37,6 +39,7 @@ export default {
variables() {
return {
fullPath: this.fullPath,
+ ...this.validatedQueryString,
};
},
update(data) {
@@ -95,6 +98,11 @@ export default {
jobsCount() {
return this.jobs.count;
},
+ validatedQueryString() {
+ const queryStringObject = queryToObject(window.location.search);
+
+ return validateQueryString(queryStringObject);
+ },
},
watch: {
// this watcher ensures that the count on the all tab
@@ -133,6 +141,10 @@ export default {
}
if (filter.type === 'status') {
+ updateHistory({
+ url: setUrlParams({ statuses: filter.value.data }, window.location.href, true),
+ });
+
this.$apollo.queries.jobs.refetch({ statuses: filter.value.data });
}
});
@@ -171,12 +183,12 @@ export default {
:loading="loading"
@fetchJobsByStatus="fetchJobsByStatus"
/>
-
- <jobs-filtered-search
- v-if="showFilteredSearch"
- :class="$options.filterSearchBoxStyles"
- @filterJobsBySearch="filterJobsBySearch"
- />
+ <div v-if="showFilteredSearch" :class="$options.filterSearchBoxStyles">
+ <jobs-filtered-search
+ :query-string="validatedQueryString"
+ @filterJobsBySearch="filterJobsBySearch"
+ />
+ </div>
<div v-if="showSkeletonLoader" class="gl-mt-5">
<gl-skeleton-loader :width="1248" :height="73">
diff --git a/app/assets/javascripts/jobs/constants.js b/app/assets/javascripts/jobs/constants.js
index 3040d4e2379..50ee7bd20dd 100644
--- a/app/assets/javascripts/jobs/constants.js
+++ b/app/assets/javascripts/jobs/constants.js
@@ -3,11 +3,17 @@ import { __, s__ } from '~/locale';
const cancel = __('Cancel');
const moreInfo = __('More information');
-export const JOB_SIDEBAR = {
+export const forwardDeploymentFailureModalId = 'forward-deployment-failure';
+
+export const JOB_SIDEBAR_COPY = {
cancel,
+ cancelJobButtonLabel: s__('Job|Cancel'),
debug: __('Debug'),
+ eraseLogButtonLabel: s__('Job|Erase job log and artifacts'),
+ eraseLogConfirmText: s__('Job|Are you sure you want to erase this job log and artifacts?'),
newIssue: __('New issue'),
retry: __('Retry'),
+ retryJobButtonLabel: s__('Job|Retry'),
toggleSidebar: __('Toggle Sidebar'),
};
diff --git a/app/assets/javascripts/jobs/index.js b/app/assets/javascripts/jobs/index.js
index 5c63ad96ad0..9dd47f4046c 100644
--- a/app/assets/javascripts/jobs/index.js
+++ b/app/assets/javascripts/jobs/index.js
@@ -1,6 +1,6 @@
import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
-import JobApp from './components/job_app.vue';
+import JobApp from './components/job/job_app.vue';
import createStore from './store';
Vue.use(GlToast);