Skip to content
Snippets Groups Projects

Load refs for the "Run Pipeline" form on demand, limit to 100 results per ref type

Merged Miguel Rincon requested to merge 321790-load-refs-on-demand into master
Files
11
@@ -9,10 +9,6 @@ import {
GlFormSelect,
GlFormTextarea,
GlLink,
GlDropdown,
GlDropdownItem,
GlDropdownSectionHeader,
GlSearchBoxByType,
GlSprintf,
GlLoadingIcon,
GlSafeHtmlDirective as SafeHtml,
@@ -26,19 +22,26 @@ import httpStatusCodes from '~/lib/utils/http_status';
import { redirectTo } from '~/lib/utils/url_utility';
import { s__, __, n__ } from '~/locale';
import { VARIABLE_TYPE, FILE_TYPE, CONFIG_VARIABLES_TIMEOUT } from '../constants';
import RefsDropdown from './refs_dropdown.vue';
const i18n = {
variablesDescription: s__(
'Pipeline|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used by default.',
),
defaultError: __('Something went wrong on our end. Please try again.'),
refsLoadingErrorTitle: s__('Pipeline|Branches or tags could not be loaded.'),
submitErrorTitle: s__('Pipeline|Pipeline cannot be run.'),
warningTitle: __('The form contains the following warning:'),
maxWarningsSummary: __('%{total} warnings found: showing first %{warningsDisplayed}'),
};
export default {
typeOptions: [
{ value: VARIABLE_TYPE, text: __('Variable') },
{ value: FILE_TYPE, text: __('File') },
],
variablesDescription: s__(
'Pipeline|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used by default.',
),
i18n,
formElementClasses: 'gl-mr-3 gl-mb-3 gl-flex-basis-quarter gl-flex-shrink-0 gl-flex-grow-0',
errorTitle: __('Pipeline cannot be run.'),
warningTitle: __('The form contains the following warning:'),
maxWarningsSummary: __('%{total} warnings found: showing first %{warningsDisplayed}'),
// this height value is used inline on the textarea to match the input field height
// it's used to prevent the overwrite if 'gl-h-7' or 'gl-h-7!' were used
textAreaStyle: { height: '32px' },
@@ -52,12 +55,9 @@ export default {
GlFormSelect,
GlFormTextarea,
GlLink,
GlDropdown,
GlDropdownItem,
GlDropdownSectionHeader,
GlSearchBoxByType,
GlSprintf,
GlLoadingIcon,
RefsDropdown,
},
directives: { SafeHtml },
props: {
@@ -77,14 +77,6 @@ export default {
type: String,
required: true,
},
branches: {
type: Array,
required: true,
},
tags: {
type: Array,
required: true,
},
settingsLink: {
type: String,
required: true,
@@ -111,11 +103,11 @@ export default {
},
data() {
return {
searchTerm: '',
refValue: {
shortName: this.refParam,
},
form: {},
errorTitle: null,
error: null,
warnings: [],
totalWarnings: 0,
@@ -125,22 +117,6 @@ export default {
};
},
computed: {
lowerCasedSearchTerm() {
return this.searchTerm.toLowerCase();
},
filteredBranches() {
return this.branches.filter((branch) =>
branch.shortName.toLowerCase().includes(this.lowerCasedSearchTerm),
);
},
filteredTags() {
return this.tags.filter((tag) =>
tag.shortName.toLowerCase().includes(this.lowerCasedSearchTerm),
);
},
hasTags() {
return this.tags.length > 0;
},
overMaxWarningsLimit() {
return this.totalWarnings > this.maxWarnings;
},
@@ -148,7 +124,7 @@ export default {
return n__('%d warning found:', '%d warnings found:', this.warnings.length);
},
summaryMessage() {
return this.overMaxWarningsLimit ? this.$options.maxWarningsSummary : this.warningsSummary;
return this.overMaxWarningsLimit ? i18n.maxWarningsSummary : this.warningsSummary;
},
shouldShowWarning() {
return this.warnings.length > 0 && !this.isWarningDismissed;
@@ -166,6 +142,11 @@ export default {
return this.form[this.refFullName]?.descriptions ?? {};
},
},
watch: {
refValue() {
this.loadConfigVariablesForm();
},
},
created() {
// this is needed until we add support for ref type in url query strings
// ensure default branch is called with full ref on load
@@ -174,7 +155,7 @@ export default {
this.refValue.fullName = `refs/heads/${this.refValue.shortName}`;
}
this.setRefSelected(this.refValue);
this.loadConfigVariablesForm();
},
methods: {
addEmptyVariable(refValue) {
@@ -213,49 +194,47 @@ export default {
this.setVariable(refValue, type, key, value);
});
},
setRefSelected(refValue) {
this.refValue = refValue;
if (!this.form[this.refFullName]) {
this.fetchConfigVariables(this.refFullName || this.refShortName)
.then(({ descriptions, params }) => {
Vue.set(this.form, this.refFullName, {
variables: [],
descriptions,
});
// Add default variables from yml
this.setVariableParams(this.refFullName, VARIABLE_TYPE, params);
})
.catch(() => {
Vue.set(this.form, this.refFullName, {
variables: [],
descriptions: {},
});
})
.finally(() => {
// Add/update variables, e.g. from query string
if (this.variableParams) {
this.setVariableParams(this.refFullName, VARIABLE_TYPE, this.variableParams);
}
if (this.fileParams) {
this.setVariableParams(this.refFullName, FILE_TYPE, this.fileParams);
}
// Adds empty var at the end of the form
this.addEmptyVariable(this.refFullName);
});
}
},
isSelected(ref) {
return ref.fullName === this.refValue.fullName;
},
removeVariable(index) {
this.variables.splice(index, 1);
},
canRemove(index) {
return index < this.variables.length - 1;
},
loadConfigVariablesForm() {
// Skip when variables already cached in `form`
if (this.form[this.refFullName]) {
return;
}
this.fetchConfigVariables(this.refFullName || this.refShortName)
.then(({ descriptions, params }) => {
Vue.set(this.form, this.refFullName, {
variables: [],
descriptions,
});
// Add default variables from yml
this.setVariableParams(this.refFullName, VARIABLE_TYPE, params);
})
.catch(() => {
Vue.set(this.form, this.refFullName, {
variables: [],
descriptions: {},
});
})
.finally(() => {
// Add/update variables, e.g. from query string
if (this.variableParams) {
this.setVariableParams(this.refFullName, VARIABLE_TYPE, this.variableParams);
}
if (this.fileParams) {
this.setVariableParams(this.refFullName, FILE_TYPE, this.fileParams);
}
// Adds empty var at the end of the form
this.addEmptyVariable(this.refFullName);
});
},
fetchConfigVariables(refValue) {
this.isLoading = true;
@@ -330,11 +309,25 @@ export default {
} = err?.response?.data;
const [error] = errors;
this.error = error;
this.warnings = warnings;
this.totalWarnings = totalWarnings;
this.reportError({
title: i18n.submitErrorTitle,
error,
warnings,
totalWarnings,
});
});
},
onRefsLoadingError(error) {
this.reportError({ title: i18n.refsLoadingErrorTitle });
Sentry.captureException(error);
},
reportError({ title = null, error = i18n.defaultError, warnings = [], totalWarnings = 0 }) {
this.errorTitle = title;
this.error = error;
this.warnings = warnings;
this.totalWarnings = totalWarnings;
},
},
};
</script>
@@ -343,7 +336,7 @@ export default {
<gl-form @submit.prevent="createPipeline">
<gl-alert
v-if="error"
:title="$options.errorTitle"
:title="errorTitle"
:dismissible="false"
variant="danger"
class="gl-mb-4"
@@ -353,7 +346,7 @@ export default {
</gl-alert>
<gl-alert
v-if="shouldShowWarning"
:title="$options.warningTitle"
:title="$options.i18n.warningTitle"
variant="warning"
class="gl-mb-4"
data-testid="run-pipeline-warning-alert"
@@ -380,31 +373,7 @@ export default {
</details>
</gl-alert>
<gl-form-group :label="s__('Pipeline|Run for branch name or tag')">
<gl-dropdown :text="refShortName" block>
<gl-search-box-by-type v-model.trim="searchTerm" :placeholder="__('Search refs')" />
<gl-dropdown-section-header>{{ __('Branches') }}</gl-dropdown-section-header>
<gl-dropdown-item
v-for="branch in filteredBranches"
:key="branch.fullName"
class="gl-font-monospace"
is-check-item
:is-checked="isSelected(branch)"
@click="setRefSelected(branch)"
>
{{ branch.shortName }}
</gl-dropdown-item>
<gl-dropdown-section-header v-if="hasTags">{{ __('Tags') }}</gl-dropdown-section-header>
<gl-dropdown-item
v-for="tag in filteredTags"
:key="tag.fullName"
class="gl-font-monospace"
is-check-item
:is-checked="isSelected(tag)"
@click="setRefSelected(tag)"
>
{{ tag.shortName }}
</gl-dropdown-item>
</gl-dropdown>
<refs-dropdown v-model="refValue" @loadingError="onRefsLoadingError" />
</gl-form-group>
<gl-loading-icon v-if="isLoading" class="gl-mb-5" size="lg" />
@@ -465,7 +434,7 @@ export default {
</div>
<template #description
><gl-sprintf :message="$options.variablesDescription">
><gl-sprintf :message="$options.i18n.variablesDescription">
<template #link="{ content }">
<gl-link :href="settingsLink">{{ content }}</gl-link>
</template>
Loading