Skip to content
Snippets Groups Projects
Unverified Commit d8fad86f authored by Balasankar C's avatar Balasankar C
Browse files

Add option to filter pipelines based on source in the UI


Allow users to filter pipelines in the pipelines index page based on the
source due to which pipelines were started.

Signed-off-by: default avatarBalasankar "Balu" C <balasankar@gitlab.com>
parent bfd7e162
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -4,6 +4,7 @@ import { map } from 'lodash';
import { s__ } from '~/locale';
import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
import PipelineBranchNameToken from './tokens/pipeline_branch_name_token.vue';
import PipelineSourceToken from './tokens/pipeline_source_token.vue';
import PipelineStatusToken from './tokens/pipeline_status_token.vue';
import PipelineTagNameToken from './tokens/pipeline_tag_name_token.vue';
import PipelineTriggerAuthorToken from './tokens/pipeline_trigger_author_token.vue';
Loading
Loading
@@ -13,6 +14,7 @@ export default {
branchType: 'ref',
tagType: 'tag',
statusType: 'status',
sourceType: 'source',
defaultTokensLength: 1,
components: {
GlFilteredSearch,
Loading
Loading
@@ -37,7 +39,7 @@ export default {
return this.value.map((i) => i.type);
},
tokens() {
return [
const tokens = [
{
type: this.$options.userType,
icon: 'user',
Loading
Loading
@@ -76,6 +78,19 @@ export default {
operators: OPERATOR_IS_ONLY,
},
];
if (gon.features.pipelineSourceFilter) {
tokens.push({
type: this.$options.sourceType,
icon: 'trigger-source',
title: s__('Pipeline|Source'),
unique: true,
token: PipelineSourceToken,
operators: OPERATOR_IS_ONLY,
});
}
return tokens;
},
parsedParams() {
return map(this.params, (val, key) => ({
Loading
Loading
<script>
import { GlFilteredSearchToken, GlFilteredSearchSuggestion } from '@gitlab/ui';
import { s__ } from '~/locale';
export default {
components: {
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
},
props: {
config: {
type: Object,
required: true,
},
value: {
type: Object,
required: true,
},
},
computed: {
sources() {
return [
{
text: s__('Pipeline|Source|Push'),
value: 'push',
},
{
text: s__('Pipeline|Source|Web'),
value: 'web',
},
{
text: s__('Pipeline|Source|Trigger'),
value: 'trigger',
},
{
text: s__('Pipeline|Source|Schedule'),
value: 'schedule',
},
{
text: s__('Pipeline|Source|API'),
value: 'api',
},
{
text: s__('Pipeline|Source|External'),
value: 'external',
},
{
text: s__('Pipeline|Source|Pipeline'),
value: 'pipeline',
},
{
text: s__('Pipeline|Source|Chat'),
value: 'chat',
},
{
text: s__('Pipeline|Source|Web IDE'),
value: 'webide',
},
{
text: s__('Pipeline|Source|Merge Request'),
value: 'merge_request_event',
},
{
text: s__('Pipeline|Source|External Pull Request'),
value: 'external_pull_request_event',
},
{
text: s__('Pipeline|Source|Parent Pipeline'),
value: 'parent_pipeline',
},
{
text: s__('Pipeline|Source|On-Demand DAST Scan'),
value: 'ondemand_dast_scan',
},
{
text: s__('Pipeline|Source|On-Demand DAST Validation'),
value: 'ondemand_dast_validation',
},
];
},
findActiveSource() {
return this.sources.find((source) => source.value === this.value.data);
},
},
};
</script>
<template>
<gl-filtered-search-token v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
<template #view>
<div class="gl-display-flex gl-align-items-center">
<span>{{ findActiveSource.text }}</span>
</div>
</template>
<template #suggestions>
<gl-filtered-search-suggestion
v-for="source in sources"
:key="source.value"
:value="source.value"
>
{{ source.text }}
</gl-filtered-search-suggestion>
</template>
</gl-filtered-search-token>
</template>
Loading
Loading
@@ -4,7 +4,7 @@ export const CANCEL_REQUEST = 'CANCEL_REQUEST';
export const LAYOUT_CHANGE_DELAY = 300;
export const FILTER_PIPELINES_SEARCH_DELAY = 200;
export const ANY_TRIGGER_AUTHOR = 'Any';
export const SUPPORTED_FILTER_PARAMETERS = ['username', 'ref', 'status'];
export const SUPPORTED_FILTER_PARAMETERS = ['username', 'ref', 'status', 'source'];
export const FILTER_TAG_IDENTIFIER = 'tag';
export const SCHEDULE_ORIGIN = 'schedule';
 
Loading
Loading
Loading
Loading
@@ -14,6 +14,10 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action :ensure_pipeline, only: [:show, :downloadable_artifacts]
 
before_action do
push_frontend_feature_flag(:pipeline_source_filter, project, type: :development, default_enabled: :yaml)
end
# Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596
before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? }
 
Loading
Loading
Loading
Loading
@@ -24792,6 +24792,51 @@ msgstr ""
msgid "Pipeline|Skipped"
msgstr ""
 
msgid "Pipeline|Source"
msgstr ""
msgid "Pipeline|Source|API"
msgstr ""
msgid "Pipeline|Source|Chat"
msgstr ""
msgid "Pipeline|Source|External"
msgstr ""
msgid "Pipeline|Source|External Pull Request"
msgstr ""
msgid "Pipeline|Source|Merge Request"
msgstr ""
msgid "Pipeline|Source|On-Demand DAST Scan"
msgstr ""
msgid "Pipeline|Source|On-Demand DAST Validation"
msgstr ""
msgid "Pipeline|Source|Parent Pipeline"
msgstr ""
msgid "Pipeline|Source|Pipeline"
msgstr ""
msgid "Pipeline|Source|Push"
msgstr ""
msgid "Pipeline|Source|Schedule"
msgstr ""
msgid "Pipeline|Source|Trigger"
msgstr ""
msgid "Pipeline|Source|Web"
msgstr ""
msgid "Pipeline|Source|Web IDE"
msgstr ""
msgid "Pipeline|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used by default."
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -20,6 +20,7 @@ describe('Pipelines filtered search', () => {
const findTagToken = () => getSearchToken('tag');
const findUserToken = () => getSearchToken('username');
const findStatusToken = () => getSearchToken('status');
const findSourceToken = () => getSearchToken('source');
 
const createComponent = (params = {}) => {
wrapper = mount(PipelinesFilteredSearch, {
Loading
Loading
@@ -32,6 +33,8 @@ describe('Pipelines filtered search', () => {
};
 
beforeEach(() => {
window.gon = { features: { pipelineSourceFilter: true } };
mock = new MockAdapter(axios);
 
jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
Loading
Loading
@@ -70,6 +73,14 @@ describe('Pipelines filtered search', () => {
operators: OPERATOR_IS_ONLY,
});
 
expect(findSourceToken()).toMatchObject({
type: 'source',
icon: 'trigger-source',
title: 'Source',
unique: true,
operators: OPERATOR_IS_ONLY,
});
expect(findStatusToken()).toMatchObject({
type: 'status',
icon: 'status',
Loading
Loading
Loading
Loading
@@ -105,6 +105,8 @@ describe('Pipelines', () => {
});
 
beforeEach(() => {
window.gon = { features: { pipelineSourceFilter: true } };
mock = new MockAdapter(axios);
 
jest.spyOn(window.history, 'pushState');
Loading
Loading
import { GlFilteredSearchToken, GlFilteredSearchSuggestion } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { stubComponent } from 'helpers/stub_component';
import PipelineSourceToken from '~/pipelines/components/pipelines_list/tokens/pipeline_source_token.vue';
describe('Pipeline Source Token', () => {
let wrapper;
const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
const defaultProps = {
config: {
type: 'source',
icon: 'trigger-source',
title: 'Source',
unique: true,
},
value: {
data: '',
},
};
const createComponent = () => {
wrapper = shallowMount(PipelineSourceToken, {
propsData: {
...defaultProps,
},
stubs: {
GlFilteredSearchToken: stubComponent(GlFilteredSearchToken, {
template: `<div><slot name="suggestions"></slot></div>`,
}),
},
});
};
beforeEach(() => {
createComponent();
});
it('passes config correctly', () => {
expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
});
describe('shows sources correctly', () => {
it('renders all pipeline sources available', () => {
expect(findAllFilteredSearchSuggestions()).toHaveLength(wrapper.vm.sources.length);
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment