Commit f97e5042 authored by Jacques Erasmus's avatar Jacques Erasmus Committed by Mike Greiling

Add project management dropdown filter

Added a project management dropdown filter
parent d864efd9
...@@ -2,6 +2,8 @@ import $ from 'jquery'; ...@@ -2,6 +2,8 @@ import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { joinPaths } from './lib/utils/url_utility'; import { joinPaths } from './lib/utils/url_utility';
import flash from '~/flash';
import { __ } from '~/locale';
const Api = { const Api = {
groupsPath: '/api/:version/groups.json', groupsPath: '/api/:version/groups.json',
...@@ -29,6 +31,7 @@ const Api = { ...@@ -29,6 +31,7 @@ const Api = {
usersPath: '/api/:version/users.json', usersPath: '/api/:version/users.json',
userPath: '/api/:version/users/:id', userPath: '/api/:version/users/:id',
userStatusPath: '/api/:version/users/:id/status', userStatusPath: '/api/:version/users/:id/status',
userProjectsPath: '/api/:version/users/:id/projects',
userPostStatusPath: '/api/:version/user/status', userPostStatusPath: '/api/:version/user/status',
commitPath: '/api/:version/projects/:id/repository/commits', commitPath: '/api/:version/projects/:id/repository/commits',
applySuggestionPath: '/api/:version/suggestions/:id/apply', applySuggestionPath: '/api/:version/suggestions/:id/apply',
...@@ -239,7 +242,8 @@ const Api = { ...@@ -239,7 +242,8 @@ const Api = {
.get(url, { .get(url, {
params: Object.assign({}, defaults, options), params: Object.assign({}, defaults, options),
}) })
.then(({ data }) => callback(data)); .then(({ data }) => callback(data))
.catch(() => flash(__('Something went wrong while fetching projects')));
}, },
commitMultiple(id, data) { commitMultiple(id, data) {
...@@ -348,6 +352,20 @@ const Api = { ...@@ -348,6 +352,20 @@ const Api = {
}); });
}, },
userProjects(userId, query, options, callback) {
const url = Api.buildUrl(Api.userProjectsPath).replace(':id', userId);
const defaults = {
search: query,
per_page: 20,
};
return axios
.get(url, {
params: Object.assign({}, defaults, options),
})
.then(({ data }) => callback(data))
.catch(() => flash(__('Something went wrong while fetching projects')));
},
branches(id, query = '', options = {}) { branches(id, query = '', options = {}) {
const url = Api.buildUrl(this.createBranchPath).replace(':id', encodeURIComponent(id)); const url = Api.buildUrl(this.createBranchPath).replace(':id', encodeURIComponent(id));
......
...@@ -13,6 +13,7 @@ import ClustersService from './services/clusters_service'; ...@@ -13,6 +13,7 @@ import ClustersService from './services/clusters_service';
import ClustersStore from './stores/clusters_store'; import ClustersStore from './stores/clusters_store';
import Applications from './components/applications.vue'; import Applications from './components/applications.vue';
import setupToggleButtons from '../toggle_buttons'; import setupToggleButtons from '../toggle_buttons';
import initProjectSelectDropdown from '~/project_select';
const Environments = () => import('ee_component/clusters/components/environments.vue'); const Environments = () => import('ee_component/clusters/components/environments.vue');
...@@ -110,8 +111,10 @@ export default class Clusters { ...@@ -110,8 +111,10 @@ export default class Clusters {
this.ingressDomainHelpText && this.ingressDomainHelpText &&
this.ingressDomainHelpText.querySelector('.js-ingress-domain-snippet'); this.ingressDomainHelpText.querySelector('.js-ingress-domain-snippet');
initProjectSelectDropdown();
Clusters.initDismissableCallout(); Clusters.initDismissableCallout();
initSettingsPanels(); initSettingsPanels();
const toggleButtonsContainer = document.querySelector('.js-cluster-enable-toggle-area'); const toggleButtonsContainer = document.querySelector('.js-cluster-enable-toggle-area');
if (toggleButtonsContainer) { if (toggleButtonsContainer) {
setupToggleButtons(toggleButtonsContainer); setupToggleButtons(toggleButtonsContainer);
......
...@@ -9,7 +9,9 @@ const projectSelect = () => { ...@@ -9,7 +9,9 @@ const projectSelect = () => {
$('.ajax-project-select').each(function(i, select) { $('.ajax-project-select').each(function(i, select) {
var placeholder; var placeholder;
const simpleFilter = $(select).data('simpleFilter') || false; const simpleFilter = $(select).data('simpleFilter') || false;
const isInstantiated = $(select).data('select2');
this.groupId = $(select).data('groupId'); this.groupId = $(select).data('groupId');
this.userId = $(select).data('userId');
this.includeGroups = $(select).data('includeGroups'); this.includeGroups = $(select).data('includeGroups');
this.allProjects = $(select).data('allProjects') || false; this.allProjects = $(select).data('allProjects') || false;
this.orderBy = $(select).data('orderBy') || 'id'; this.orderBy = $(select).data('orderBy') || 'id';
...@@ -63,6 +65,18 @@ const projectSelect = () => { ...@@ -63,6 +65,18 @@ const projectSelect = () => {
}, },
projectsCallback, projectsCallback,
); );
} else if (_this.userId) {
return Api.userProjects(
_this.userId,
query.term,
{
with_issues_enabled: _this.withIssuesEnabled,
with_merge_requests_enabled: _this.withMergeRequestsEnabled,
with_shared: _this.withShared,
include_subgroups: _this.includeProjectsInSubgroups,
},
projectsCallback,
);
} else { } else {
return Api.projects( return Api.projects(
query.term, query.term,
...@@ -96,7 +110,7 @@ const projectSelect = () => { ...@@ -96,7 +110,7 @@ const projectSelect = () => {
dropdownCssClass: 'ajax-project-dropdown', dropdownCssClass: 'ajax-project-dropdown',
}); });
if (simpleFilter) return select; if (isInstantiated || simpleFilter) return select;
return new ProjectSelectComboButton(select); return new ProjectSelectComboButton(select);
}); });
}; };
......
- group_id = @cluster.group.id if @cluster.group_type?
- if @cluster.project_type?
- group_id = @cluster.project.group.id if @cluster.project.group
- user_id = @cluster.project.namespace.owner_id unless group_id
- if can?(current_user, :admin_cluster, @cluster) - if can?(current_user, :admin_cluster, @cluster)
- unless @cluster.provided_by_user? - unless @cluster.provided_by_user?
.append-bottom-20 .append-bottom-20
...@@ -7,6 +13,21 @@ ...@@ -7,6 +13,21 @@
- link_gke = link_to(s_('ClusterIntegration|Google Kubernetes Engine'), @cluster.gke_cluster_url, target: '_blank', rel: 'noopener noreferrer') - link_gke = link_to(s_('ClusterIntegration|Google Kubernetes Engine'), @cluster.gke_cluster_url, target: '_blank', rel: 'noopener noreferrer')
= s_('ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}').html_safe % { link_gke: link_gke } = s_('ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}').html_safe % { link_gke: link_gke }
= form_for @cluster, url: clusterable.cluster_path(@cluster), as: :cluster, html: { class: 'cluster_management_form' } do |field|
%h5
= s_('ClusterIntegration|Cluster management project (alpha)')
.form-group
.form-text.text-muted
= project_select_tag('cluster[management_project_id]', class: 'hidden-filter-value', toggle_class: 'js-project-search js-project-filter js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
placeholder: _('Select project'), idAttribute: 'id', data: { order_by: 'last_activity_at', idattribute: 'id', simple_filter: true, allow_clear: true, include_groups: false, include_projects_in_subgroups: true, group_id: group_id, user_id: user_id }, value: @cluster.management_project_id)
.text-muted
= s_('ClusterIntegration|A cluster management project can be used to run deployment jobs with Kubernetes <code>cluster-admin</code> privileges.').html_safe
= link_to _('More information'), help_page_path('user/clusters/management_project.md'), target: '_blank'
.form-group
= field.submit _('Save changes'), class: 'btn btn-success qa-save-domain'
.sub-section.form-group .sub-section.form-group
%h4.text-danger %h4.text-danger
= s_('ClusterIntegration|Remove Kubernetes cluster integration') = s_('ClusterIntegration|Remove Kubernetes cluster integration')
......
---
title: Add ability to select a Cluster management project
merge_request: 18928
author:
type: added
...@@ -3399,6 +3399,9 @@ msgstr "" ...@@ -3399,6 +3399,9 @@ msgstr ""
msgid "ClusterIntegration|%{title} updated successfully." msgid "ClusterIntegration|%{title} updated successfully."
msgstr "" msgstr ""
msgid "ClusterIntegration|A cluster management project can be used to run deployment jobs with Kubernetes <code>cluster-admin</code> privileges."
msgstr ""
msgid "ClusterIntegration|A service token scoped to %{code}kube-system%{end_code} with %{code}cluster-admin%{end_code} privileges." msgid "ClusterIntegration|A service token scoped to %{code}kube-system%{end_code} with %{code}cluster-admin%{end_code} privileges."
msgstr "" msgstr ""
...@@ -3507,6 +3510,9 @@ msgstr "" ...@@ -3507,6 +3510,9 @@ msgstr ""
msgid "ClusterIntegration|Cluster health" msgid "ClusterIntegration|Cluster health"
msgstr "" msgstr ""
msgid "ClusterIntegration|Cluster management project (alpha)"
msgstr ""
msgid "ClusterIntegration|Cluster name is required." msgid "ClusterIntegration|Cluster name is required."
msgstr "" msgstr ""
...@@ -15587,6 +15593,9 @@ msgstr "" ...@@ -15587,6 +15593,9 @@ msgstr ""
msgid "Something went wrong while fetching latest comments." msgid "Something went wrong while fetching latest comments."
msgstr "" msgstr ""
msgid "Something went wrong while fetching projects"
msgstr ""
msgid "Something went wrong while fetching related merge requests." msgid "Something went wrong while fetching related merge requests."
msgstr "" msgstr ""
......
...@@ -467,6 +467,26 @@ describe('Api', () => { ...@@ -467,6 +467,26 @@ describe('Api', () => {
}); });
}); });
describe('user projects', () => {
it('fetches all projects that belong to a particular user', done => {
const query = 'dummy query';
const options = { unused: 'option' };
const userId = '123456';
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/users/${userId}/projects`;
mock.onGet(expectedUrl).reply(200, [
{
name: 'test',
},
]);
Api.userProjects(userId, query, options, response => {
expect(response.length).toBe(1);
expect(response[0].name).toBe('test');
done();
});
});
});
describe('commitPipelines', () => { describe('commitPipelines', () => {
it('fetches pipelines for a given commit', done => { it('fetches pipelines for a given commit', done => {
const projectId = 'example/foobar'; const projectId = 'example/foobar';
......
...@@ -10,8 +10,10 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -10,8 +10,10 @@ import axios from '~/lib/utils/axios_utils';
import { loadHTMLFixture } from 'helpers/fixtures'; import { loadHTMLFixture } from 'helpers/fixtures';
import { setTestTimeout } from 'helpers/timeout'; import { setTestTimeout } from 'helpers/timeout';
import $ from 'jquery'; import $ from 'jquery';
import initProjectSelectDropdown from '~/project_select';
jest.mock('~/lib/utils/poll'); jest.mock('~/lib/utils/poll');
jest.mock('~/project_select');
const { INSTALLING, INSTALLABLE, INSTALLED, UNINSTALLING } = APPLICATION_STATUS; const { INSTALLING, INSTALLABLE, INSTALLED, UNINSTALLING } = APPLICATION_STATUS;
...@@ -44,6 +46,7 @@ describe('Clusters', () => { ...@@ -44,6 +46,7 @@ describe('Clusters', () => {
afterEach(() => { afterEach(() => {
cluster.destroy(); cluster.destroy();
mock.restore(); mock.restore();
jest.clearAllMocks();
}); });
describe('class constructor', () => { describe('class constructor', () => {
...@@ -55,6 +58,10 @@ describe('Clusters', () => { ...@@ -55,6 +58,10 @@ describe('Clusters', () => {
it('should call initPolling on construct', () => { it('should call initPolling on construct', () => {
expect(cluster.initPolling).toHaveBeenCalled(); expect(cluster.initPolling).toHaveBeenCalled();
}); });
it('should call initProjectSelectDropdown on construct', () => {
expect(initProjectSelectDropdown).toHaveBeenCalled();
});
}); });
describe('toggle', () => { describe('toggle', () => {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment