Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
187727e248
commit
146284d119
34 changed files with 210 additions and 730 deletions
|
@ -30,10 +30,11 @@ cache-workhorse:
|
|||
WEBPACK_REPORT: "false"
|
||||
script:
|
||||
- !reference [.yarn-install, script]
|
||||
- export GITLAB_ASSETS_HASH=$(bundle exec rake gitlab:assets:hash_sum | tee cached-assets-hash.txt)
|
||||
- export GITLAB_ASSETS_HASH=$(bundle exec rake gitlab:assets:hash_sum)
|
||||
- source scripts/gitlab_component_helpers.sh
|
||||
- 'gitlab_assets_archive_doesnt_exist || { echoinfo "INFO: Exiting early as package exists."; exit 0; }'
|
||||
- run_timed_command "bin/rake gitlab:assets:compile"
|
||||
- echo -n "${GITLAB_ASSETS_HASH}" > "cached-assets-hash.txt"
|
||||
- run_timed_command "create_gitlab_assets_package"
|
||||
- run_timed_command "upload_gitlab_assets_package"
|
||||
|
||||
|
|
|
@ -182,6 +182,7 @@
|
|||
- "GITLAB_WORKHORSE_VERSION"
|
||||
- "workhorse/**/*"
|
||||
- ".gitlab/ci/workhorse.gitlab-ci.yml"
|
||||
- "spec/support/gitlab-git-test.git/**/*"
|
||||
|
||||
.yaml-lint-patterns: &yaml-lint-patterns
|
||||
- "*.yml"
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
getValueStreamStageCounts,
|
||||
} from '~/api/analytics_api';
|
||||
import { normalizeHeaders, parseIntPagination } from '~/lib/utils/common_utils';
|
||||
import createFlash from '~/flash';
|
||||
import { createAlert } from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
import { DEFAULT_VALUE_STREAM, I18N_VSA_ERROR_STAGE_MEDIAN } from '../constants';
|
||||
import * as types from './mutation_types';
|
||||
|
@ -97,7 +97,7 @@ export const fetchStageMedians = ({
|
|||
.then((data) => commit(types.RECEIVE_STAGE_MEDIANS_SUCCESS, data))
|
||||
.catch((error) => {
|
||||
commit(types.RECEIVE_STAGE_MEDIANS_ERROR, error);
|
||||
createFlash({ message: I18N_VSA_ERROR_STAGE_MEDIAN });
|
||||
createAlert({ message: I18N_VSA_ERROR_STAGE_MEDIAN });
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -126,7 +126,7 @@ export const fetchStageCountValues = ({
|
|||
.then((data) => commit(types.RECEIVE_STAGE_COUNTS_SUCCESS, data))
|
||||
.catch((error) => {
|
||||
commit(types.RECEIVE_STAGE_COUNTS_ERROR, error);
|
||||
createFlash({
|
||||
createAlert({
|
||||
message: __('There was an error fetching stage total counts'),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Api from '~/api';
|
||||
import createFlash from '~/flash';
|
||||
import { createAlert } from '~/flash';
|
||||
import { logError } from '~/lib/logger';
|
||||
import { __ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
@ -27,7 +27,7 @@ const receiveFreezePeriod = (store, request) => {
|
|||
dispatch('fetchFreezePeriods');
|
||||
})
|
||||
.catch((error) => {
|
||||
createFlash({
|
||||
createAlert({
|
||||
message: __('Error: Unable to create deploy freeze'),
|
||||
});
|
||||
dispatch('receiveFreezePeriodError', error);
|
||||
|
@ -59,7 +59,7 @@ export const deleteFreezePeriod = ({ state, commit }, { id }) => {
|
|||
return Api.deleteFreezePeriod(state.projectId, id)
|
||||
.then(() => commit(types.RECEIVE_DELETE_FREEZE_PERIOD_SUCCESS, id))
|
||||
.catch((e) => {
|
||||
createFlash({
|
||||
createAlert({
|
||||
message: __('Error: Unable to delete deploy freeze'),
|
||||
});
|
||||
commit(types.RECEIVE_DELETE_FREEZE_PERIOD_ERROR, id);
|
||||
|
@ -76,7 +76,7 @@ export const fetchFreezePeriods = ({ commit, state }) => {
|
|||
commit(types.RECEIVE_FREEZE_PERIODS_SUCCESS, data);
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash({
|
||||
createAlert({
|
||||
message: __('There was an error fetching the deploy freezes.'),
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
|
||||
import createFlash from '~/flash';
|
||||
import { createAlert } from '~/flash';
|
||||
import { s__ } from '~/locale';
|
||||
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
|
||||
import eventHub from '../eventhub';
|
||||
|
@ -93,7 +93,7 @@ export default {
|
|||
.catch(() => {
|
||||
this.isLoading = false;
|
||||
this.store.keys = {};
|
||||
return createFlash({
|
||||
return createAlert({
|
||||
message: s__('DeployKeys|Error getting deploy keys'),
|
||||
});
|
||||
});
|
||||
|
@ -103,7 +103,7 @@ export default {
|
|||
.enableKey(deployKey.id)
|
||||
.then(this.fetchKeys)
|
||||
.catch(() =>
|
||||
createFlash({
|
||||
createAlert({
|
||||
message: s__('DeployKeys|Error enabling deploy key'),
|
||||
}),
|
||||
);
|
||||
|
@ -119,7 +119,7 @@ export default {
|
|||
.then(this.fetchKeys)
|
||||
.then(hideModal)
|
||||
.catch(() =>
|
||||
createFlash({
|
||||
createAlert({
|
||||
message: s__('DeployKeys|Error removing deploy key'),
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { GlButton, GlLink, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { ApolloMutation } from 'vue-apollo';
|
||||
import createFlash from '~/flash';
|
||||
import { createAlert } from '~/flash';
|
||||
import { s__ } from '~/locale';
|
||||
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
|
||||
import { updateGlobalTodoCount } from '~/vue_shared/components/sidebar/todo_toggle/utils';
|
||||
|
@ -155,7 +155,7 @@ export default {
|
|||
methods: {
|
||||
onDone({ data: { createNote } }) {
|
||||
if (hasErrors(createNote)) {
|
||||
createFlash({ message: ADD_DISCUSSION_COMMENT_ERROR });
|
||||
createAlert({ message: ADD_DISCUSSION_COMMENT_ERROR });
|
||||
}
|
||||
this.discussionComment = '';
|
||||
this.hideForm();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { propertyOf } from 'lodash';
|
||||
import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql';
|
||||
import createFlash, { FLASH_TYPES } from '~/flash';
|
||||
import { createAlert, VARIANT_WARNING } from '~/flash';
|
||||
import { s__ } from '~/locale';
|
||||
import { DESIGNS_ROUTE_NAME } from '../router/constants';
|
||||
import allVersionsMixin from './all_versions';
|
||||
|
@ -36,7 +36,7 @@ export default {
|
|||
},
|
||||
result() {
|
||||
if (this.$route.query.version && !this.hasValidVersion) {
|
||||
createFlash({
|
||||
createAlert({
|
||||
message: s__(
|
||||
'DesignManagement|Requested design version does not exist. Showing latest version instead',
|
||||
),
|
||||
|
@ -44,11 +44,11 @@ export default {
|
|||
this.$router.replace({ name: DESIGNS_ROUTE_NAME, query: { version: undefined } });
|
||||
}
|
||||
if (this.designCollection.copyState === 'ERROR') {
|
||||
createFlash({
|
||||
createAlert({
|
||||
message: s__(
|
||||
'DesignManagement|There was an error moving your designs. Please upload your designs below.',
|
||||
),
|
||||
type: FLASH_TYPES.WARNING,
|
||||
variant: VARIANT_WARNING,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -4,7 +4,7 @@ import { isNull } from 'lodash';
|
|||
import Mousetrap from 'mousetrap';
|
||||
import { ApolloMutation } from 'vue-apollo';
|
||||
import { keysFor, ISSUE_CLOSE_DESIGN } from '~/behaviors/shortcuts/keybindings';
|
||||
import createFlash from '~/flash';
|
||||
import { createAlert } from '~/flash';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import { updateGlobalTodoCount } from '~/vue_shared/components/sidebar/todo_toggle/utils';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
@ -250,7 +250,7 @@ export default {
|
|||
onQueryError(message) {
|
||||
// because we redirect user to /designs (the issue page),
|
||||
// we want to create these flashes on the issue page
|
||||
createFlash({ message });
|
||||
createAlert({ message });
|
||||
this.$router.push({ name: this.$options.DESIGNS_ROUTE_NAME });
|
||||
},
|
||||
onError(message, e) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import produce from 'immer';
|
||||
import { differenceBy } from 'lodash';
|
||||
import createFlash from '~/flash';
|
||||
import { createAlert } from '~/flash';
|
||||
import { extractCurrentDiscussion, extractDesign, extractDesigns } from './design_management_utils';
|
||||
import {
|
||||
ADD_IMAGE_DIFF_NOTE_ERROR,
|
||||
|
@ -234,7 +234,7 @@ export const deletePendingTodoFromStore = (store, todoMarkDone, query, queryVari
|
|||
};
|
||||
|
||||
const onError = (data, message) => {
|
||||
createFlash({ message });
|
||||
createAlert({ message });
|
||||
throw new Error(data.errors);
|
||||
};
|
||||
|
||||
|
@ -283,7 +283,7 @@ export const updateStoreAfterUploadDesign = (store, data, query) => {
|
|||
|
||||
export const updateDesignsOnStoreAfterReorder = (store, data, query) => {
|
||||
if (hasErrors(data)) {
|
||||
createFlash({ message: data.errors[0] });
|
||||
createAlert({ message: data.errors[0] });
|
||||
} else {
|
||||
moveDesignInStore(store, data, query);
|
||||
}
|
||||
|
|
|
@ -412,7 +412,7 @@ export default {
|
|||
<div
|
||||
v-if="showMultiLineComment"
|
||||
data-testid="multiline-comment"
|
||||
class="gl-mb-5 gl-text-gray-500 gl-border-gray-100 gl-border-b-solid gl-border-b-1 gl-pb-4"
|
||||
class="gl-text-gray-500 gl-border-gray-100 gl-border-b-solid gl-border-b-1 gl-px-5 gl-py-3"
|
||||
>
|
||||
<gl-sprintf :message="__('Comment on lines %{startLine} to %{endLine}')">
|
||||
<template #startLine>
|
||||
|
|
|
@ -401,7 +401,7 @@ export default {
|
|||
<div
|
||||
v-for="(variable, index) in variables"
|
||||
:key="variable.uniqueId"
|
||||
class="gl-mb-3 gl-ml-n3 gl-pb-2"
|
||||
class="gl-mb-3 gl-pb-2"
|
||||
data-testid="ci-variable-row"
|
||||
data-qa-selector="ci_variable_row_container"
|
||||
>
|
||||
|
|
|
@ -401,7 +401,7 @@ export default {
|
|||
<div
|
||||
v-for="(variable, index) in variables"
|
||||
:key="variable.uniqueId"
|
||||
class="gl-mb-3 gl-ml-n3 gl-pb-2"
|
||||
class="gl-mb-3 gl-pb-2"
|
||||
data-testid="ci-variable-row"
|
||||
data-qa-selector="ci_variable_row_container"
|
||||
>
|
||||
|
|
|
@ -551,6 +551,11 @@ $system-note-svg-size: 1rem;
|
|||
.timeline-icon {
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.timeline-entry-inner .timeline-icon {
|
||||
margin-top: $grid-size;
|
||||
margin-left: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -242,11 +242,6 @@ class SearchController < ApplicationController
|
|||
def search_type
|
||||
'basic'
|
||||
end
|
||||
|
||||
before_action do
|
||||
# Prefer to scope it per project or user e.g.
|
||||
push_frontend_feature_flag(:search_page_vertical_nav, current_user)
|
||||
end
|
||||
end
|
||||
|
||||
SearchController.prepend_mod_with('SearchController')
|
||||
|
|
|
@ -380,42 +380,6 @@ module SearchHelper
|
|||
end
|
||||
end
|
||||
|
||||
def search_filter_link_json(scope, label, data, search)
|
||||
search_params = params.merge(search).merge({ scope: scope }).permit(SEARCH_GENERIC_PARAMS)
|
||||
active_scope = @scope == scope
|
||||
|
||||
result = { label: label, scope: scope, data: data, link: search_path(search_params), active: active_scope }
|
||||
result[:count] = @search_results.formatted_count(scope) if active_scope && !@timeout
|
||||
result[:count_link] = search_count_path(search_params) unless active_scope
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# search page scope navigation
|
||||
def search_navigation
|
||||
{
|
||||
projects: { label: _("Projects"), data: { qa_selector: 'projects_tab' }, condition: @project.nil? },
|
||||
blobs: { label: _("Code"), data: { qa_selector: 'code_tab' }, condition: project_search_tabs?(:blobs) || search_service.show_elasticsearch_tabs? || feature_flag_tab_enabled?(:global_search_code_tab) },
|
||||
epics: { label: _("Epics"), condition: @project.nil? && search_service.show_epics? },
|
||||
issues: { label: _("Issues"), condition: project_search_tabs?(:issues) || feature_flag_tab_enabled?(:global_search_issues_tab) },
|
||||
merge_requests: { label: _("Merge requests"), condition: project_search_tabs?(:merge_requests) || feature_flag_tab_enabled?(:global_search_merge_requests_tab) },
|
||||
wiki_blobs: { label: _("Wiki"), condition: project_search_tabs?(:wiki) || search_service.show_elasticsearch_tabs? },
|
||||
commits: { label: _("Commits"), condition: project_search_tabs?(:commits) || (search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_commits_tab)) },
|
||||
notes: { label: _("Comments"), condition: project_search_tabs?(:notes) || search_service.show_elasticsearch_tabs? },
|
||||
milestones: { label: _("Milestones"), condition: project_search_tabs?(:milestones) || @project.nil? },
|
||||
users: { label: _("Users"), condition: show_user_search_tab? },
|
||||
snippet_titles: { label: _("Titles and Descriptions"), search: { snippets: true, group_id: nil, project_id: nil }, condition: @show_snippets.present? && @project.nil? }
|
||||
}
|
||||
end
|
||||
|
||||
def search_navigation_json
|
||||
result = {}
|
||||
search_navigation.each do |scope, nav|
|
||||
result[scope] = search_filter_link_json(scope.to_s, nav[:label], nav[:data], nav[:search]) if nav[:condition]
|
||||
end
|
||||
result.to_json
|
||||
end
|
||||
|
||||
def search_filter_input_options(type, placeholder = _('Search or filter results...'))
|
||||
opts =
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@ module Integrations
|
|||
end
|
||||
|
||||
def help
|
||||
s_("HarborIntegration|After the Harbor integration is activated, global variables $HARBOR_USERNAME, $HARBOR_HOST, $HARBOR_OCI, $HARBOR_PASSWORD, $HARBOR_URL and $HARBOR_PROJECT will be created for CI/CD use.")
|
||||
s_("HarborIntegration|After the Harbor integration is activated, global variables `$HARBOR_USERNAME`, `$HARBOR_HOST`, `$HARBOR_OCI`, `$HARBOR_PASSWORD`, `$HARBOR_URL` and `$HARBOR_PROJECT` will be created for CI/CD use.")
|
||||
end
|
||||
|
||||
def hostname
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
= form_for [@project, @protected_tag], html: { class: 'new-protected-tag js-new-protected-tag' } do |f|
|
||||
%input{ type: 'hidden', name: 'update_section', value: 'js-protected-tags-settings' }
|
||||
.card
|
||||
.card-header
|
||||
= render Pajamas::CardComponent.new do |c|
|
||||
- c.header do
|
||||
= _('Protect a tag')
|
||||
.card-body
|
||||
- c.body do
|
||||
= form_errors(@protected_tag)
|
||||
.form-group.row
|
||||
= f.label :name, _('Tag:'), class: 'col-md-2 text-left text-md-right'
|
||||
|
@ -19,5 +19,5 @@
|
|||
.create_access_levels-container
|
||||
= yield :create_access_levels
|
||||
|
||||
.card-footer
|
||||
- c.footer do
|
||||
= f.submit _('Protect'), class: 'gl-button btn btn-confirm', disabled: true, data: { qa_selector: 'protect_tag_button' }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
%hr
|
||||
= render Pajamas::CardComponent.new(card_options: { id: 'webhooks-index' }) do |c|
|
||||
= render Pajamas::CardComponent.new(card_options: { id: 'webhooks-index' }, body_options: { class: 'gl-py-0'}) do |c|
|
||||
- c.header do
|
||||
= hook_class.underscore.humanize.titleize.pluralize
|
||||
(#{hooks.size})
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: search_page_vertical_nav
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97784
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342621
|
||||
milestone: '15.5'
|
||||
type: development
|
||||
group: group::global search
|
||||
default_enabled: false
|
|
@ -20,4 +20,5 @@ tier:
|
|||
performance_indicator_type:
|
||||
- gmau
|
||||
- paid_gmau
|
||||
- smau
|
||||
milestone: "<13.9"
|
||||
|
|
|
@ -7,9 +7,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# OpenID Connect OmniAuth provider **(FREE SELF)**
|
||||
|
||||
GitLab can use [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html) as an OmniAuth provider.
|
||||
GitLab can use [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html)
|
||||
as an OmniAuth provider.
|
||||
|
||||
To enable the OpenID Connect OmniAuth provider, you must register your application with an OpenID Connect provider.
|
||||
To enable the OpenID Connect OmniAuth provider, you must register your application
|
||||
with an OpenID Connect provider.
|
||||
The OpenID Connect provides you with a client's details and secret for you to use.
|
||||
|
||||
1. On your GitLab server, open the configuration file.
|
||||
|
@ -27,7 +29,7 @@ The OpenID Connect provides you with a client's details and secret for you to us
|
|||
sudo -u git -H editor config/gitlab.yml
|
||||
```
|
||||
|
||||
See [Configure initial settings](../../integration/omniauth.md#configure-initial-settings) for initial settings.
|
||||
1. [Configure initial settings](../../integration/omniauth.md#configure-initial-settings).
|
||||
|
||||
1. Add the provider configuration.
|
||||
|
||||
|
@ -83,32 +85,49 @@ The OpenID Connect provides you with a client's details and secret for you to us
|
|||
```
|
||||
|
||||
NOTE:
|
||||
For more information on each configuration option refer to the [OmniAuth OpenID Connect usage documentation](https://github.com/m0n9oose/omniauth_openid_connect#usage)
|
||||
and the [OpenID Connect Core 1.0 specification](https://openid.net/specs/openid-connect-core-1_0.html).
|
||||
For more information on each configuration option, refer to the:
|
||||
|
||||
- [OmniAuth OpenID Connect usage documentation](https://github.com/m0n9oose/omniauth_openid_connect#usage).
|
||||
- [OpenID Connect Core 1.0 specification](https://openid.net/specs/openid-connect-core-1_0.html).
|
||||
|
||||
1. For the provider configuration, change the values for the provider to match your
|
||||
OpenID Connect client setup. Use the following as a guide:
|
||||
|
||||
1. For the configuration above, change the values for the provider to match your OpenID Connect client setup. Use the following as a guide:
|
||||
- `<your_oidc_label>` is the label that appears on the login page.
|
||||
- `<custom_provider_icon>` (optional) is the icon that appears on the login page. Icons for the major social login platforms are built-in into GitLab,
|
||||
but can be overridden by specifying this parameter. Both local paths and absolute URLs are accepted.
|
||||
- `<your_oidc_url>` (optional) is the URL that points to the OpenID Connect provider. For example, `https://example.com/auth/realms/your-realm`.
|
||||
If this value is not provided, the URL is constructed from the `client_options` in the following format: `<client_options.scheme>://<client_options.host>:<client_options.port>`.
|
||||
- If `discovery` is set to `true`, the OpenID Connect provider attempts to auto discover the client options using `<your_oidc_url>/.well-known/openid-configuration`. Defaults to `false`.
|
||||
- `client_auth_method` (optional) specifies the method used for authenticating the client with the OpenID Connect provider.
|
||||
- `<custom_provider_icon>` (optional) is the icon that appears on the login page.
|
||||
Icons for the major social login platforms are built into GitLab,
|
||||
but you can override these icons by specifying this parameter. GitLab accepts both
|
||||
local paths and absolute URLs.
|
||||
- `<your_oidc_url>` (optional) is the URL that points to the OpenID Connect
|
||||
provider (for example, `https://example.com/auth/realms/your-realm`).
|
||||
If this value is not provided, the URL is constructed from `client_options`
|
||||
in the following format: `<client_options.scheme>://<client_options.host>:<client_options.port>`.
|
||||
- If `discovery` is set to `true`, the OpenID Connect provider attempts to automatically
|
||||
discover the client options using `<your_oidc_url>/.well-known/openid-configuration`.
|
||||
Defaults to `false`.
|
||||
- `client_auth_method` (optional) specifies the method used for authenticating
|
||||
the client with the OpenID Connect provider.
|
||||
- Supported values are:
|
||||
- `basic` - HTTP Basic Authentication
|
||||
- `jwt_bearer` - JWT based authentication (private key and client secret signing)
|
||||
- `mtls` - Mutual TLS or X.509 certificate validation
|
||||
- Any other value POSTs the client ID and secret in the request body
|
||||
- If not specified, defaults to `basic`.
|
||||
- `<uid_field>` (optional) is the field name from the `user_info.raw_attributes` that defines the value for `uid`. For example, `preferred_username`.
|
||||
If this value is not provided or the field with the configured value is missing from the `user_info.raw_attributes` details, the `uid` uses the `sub` field.
|
||||
- `send_scope_to_token_endpoint` is `true` by default. In other words, the `scope` parameter is normally included in requests to the token endpoint.
|
||||
However, if your OpenID Connect provider does not accept the `scope` parameter in such requests, set this to `false`.
|
||||
- `basic` - HTTP Basic Authentication.
|
||||
- `jwt_bearer` - JWT-based authentication (private key and client secret signing).
|
||||
- `mtls` - Mutual TLS or X.509 certificate validation.
|
||||
- Any other value posts the client ID and secret in the request body.
|
||||
- If not specified, this value defaults to `basic`.
|
||||
- `<uid_field>` (optional) is the field name from `user_info.raw_attributes`
|
||||
that defines the value for `uid` (for example, `preferred_username`).
|
||||
If you do not provide this value, or the field with the configured value is missing
|
||||
from the `user_info.raw_attributes` details, `uid` uses the `sub` field.
|
||||
- `send_scope_to_token_endpoint` is `true` by default, so the `scope` parameter
|
||||
is normally included in requests to the token endpoint.
|
||||
However, if your OpenID Connect provider does not accept the `scope` parameter
|
||||
in such requests, set this to `false`.
|
||||
- `client_options` are the OpenID Connect client-specific options. Specifically:
|
||||
- `identifier` is the client identifier as configured in the OpenID Connect service provider.
|
||||
- `secret` is the client secret as configured in the OpenID Connect service provider.
|
||||
- `redirect_uri` is the GitLab URL to redirect the user to after successful login. For example, `http://example.com/users/auth/openid_connect/callback`.
|
||||
- `end_session_endpoint` (optional) is the URL to the endpoint that end the session (logout). Can be provided if auto-discovery disabled or unsuccessful.
|
||||
- `redirect_uri` is the GitLab URL to redirect the user to after successful login
|
||||
(for example, `http://example.com/users/auth/openid_connect/callback`).
|
||||
- `end_session_endpoint` (optional) is the URL to the endpoint that ends the
|
||||
session. You can provide this URL if auto-discovery is disabled or unsuccessful.
|
||||
- The following `client_options` are optional unless auto-discovery is disabled or unsuccessful:
|
||||
- `authorization_endpoint` is the URL to the endpoint that authorizes the end user.
|
||||
- `token_endpoint` is the URL to the endpoint that provides Access Token.
|
||||
|
@ -116,19 +135,22 @@ The OpenID Connect provides you with a client's details and secret for you to us
|
|||
- `jwks_uri` is the URL to the endpoint where the Token signer publishes its keys.
|
||||
|
||||
1. Save the configuration file.
|
||||
1. For changes to take effect, [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) if you installed GitLab with Omnibus, or [restart GitLab](../restart_gitlab.md#installations-from-source) if you installed GitLab from the source.
|
||||
1. For changes to take effect, if you installed GitLab:
|
||||
|
||||
On the sign in page, there should now be an OpenID Connect icon below the regular sign in form.
|
||||
Select the icon to begin the authentication process. The OpenID Connect provider asks the user to
|
||||
sign in and authorize the GitLab application (if confirmation required by the client). If everything goes well, the user
|
||||
is redirected to GitLab and signed in.
|
||||
- With Omnibus, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||
- From source, [restart GitLab](../restart_gitlab.md#installations-from-source).
|
||||
|
||||
On the sign in page, you have an OpenID Connect option below the regular sign in form.
|
||||
Select this option to begin the authentication process. The OpenID Connect provider
|
||||
asks you to sign in and authorize the GitLab application if confirmation is required
|
||||
by the client. You are redirected to GitLab and signed in.
|
||||
|
||||
## Example configurations
|
||||
|
||||
The following configurations illustrate how to set up OpenID with
|
||||
different providers with Omnibus GitLab.
|
||||
|
||||
### Google
|
||||
### Configure Google
|
||||
|
||||
See the [Google documentation](https://developers.google.com/identity/protocols/oauth2/openid-connect)
|
||||
for more details:
|
||||
|
@ -156,13 +178,13 @@ gitlab_rails['omniauth_providers'] = [
|
|||
]
|
||||
```
|
||||
|
||||
### Microsoft Azure
|
||||
### Configure Microsoft Azure
|
||||
|
||||
The OpenID Connect (OIDC) protocol for Microsoft Azure uses the [Microsoft identity platform (v2) endpoints](https://learn.microsoft.com/en-us/azure/active-directory/azuread-dev/azure-ad-endpoint-comparison).
|
||||
To get started, sign in to the [Azure Portal](https://portal.azure.com). For your app, you need the
|
||||
following information:
|
||||
To get started, sign in to the [Azure Portal](https://portal.azure.com). For your app,
|
||||
you need the following information:
|
||||
|
||||
- A tenant ID. You may already have one. For more information, review the
|
||||
- A tenant ID. You may already have one. For more information, see the
|
||||
[Microsoft Azure Tenant](https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-create-new-tenant) documentation.
|
||||
- A client ID and a client secret. Follow the instructions in the
|
||||
[Microsoft Quickstart Register an Application](https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) documentation
|
||||
|
@ -195,10 +217,10 @@ gitlab_rails['omniauth_providers'] = [
|
|||
|
||||
Microsoft has documented how its platform works with [the OIDC protocol](https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc).
|
||||
|
||||
### Microsoft Azure Active Directory B2C
|
||||
### Configure Microsoft Azure Active Directory B2C
|
||||
|
||||
While GitLab works with [Azure Active Directory B2C](https://learn.microsoft.com/en-us/azure/active-directory-b2c/overview), it requires special
|
||||
configuration to work. To get started, sign in to the [Azure Portal](https://portal.azure.com).
|
||||
GitLab requires special
|
||||
configuration to work with [Azure Active Directory B2C](https://learn.microsoft.com/en-us/azure/active-directory-b2c/overview). To get started, sign in to the [Azure Portal](https://portal.azure.com).
|
||||
For your app, you need the following information from Azure:
|
||||
|
||||
- A tenant ID. You may already have one. For more information, review the
|
||||
|
@ -208,16 +230,17 @@ For your app, you need the following information from Azure:
|
|||
client ID and client secret for your app.
|
||||
- The user flow or policy name. Follow the instructions in the [Microsoft tutorial](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-user-flow).
|
||||
|
||||
If your GitLab domain is `gitlab.example.com`, ensure the app has the following `Redirect URI`:
|
||||
Configure the app:
|
||||
|
||||
`https://gitlab.example.com/users/auth/openid_connect/callback`
|
||||
1. Set the app `Redirect URI`. For example, If your GitLab domain is `gitlab.example.com`,
|
||||
set the app `Redirect URI` to `https://gitlab.example.com/users/auth/openid_connect/callback`.
|
||||
|
||||
In addition, ensure that [ID tokens are enabled](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications?tabs=app-reg-ga#enable-id-token-implicit-grant).
|
||||
1. [Enable the ID tokens](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-register-applications?tabs=app-reg-ga#enable-id-token-implicit-grant).
|
||||
|
||||
Add the following API permissions to the app:
|
||||
1. Add the following API permissions to the app:
|
||||
|
||||
- `openid`
|
||||
- `offline_access`
|
||||
- `openid`
|
||||
- `offline_access`
|
||||
|
||||
#### Configure custom policies
|
||||
|
||||
|
@ -226,16 +249,17 @@ Azure B2C [offers two ways of defining the business logic for logging in a user]
|
|||
- [User flows](https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-overview#user-flows)
|
||||
- [Custom policies](https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-overview#custom-policies)
|
||||
|
||||
While cumbersome to configure, custom policies are required because
|
||||
standard Azure B2C user flows [do not send the OpenID `email` claim](https://github.com/MicrosoftDocs/azure-docs/issues/16566). In
|
||||
other words, they do not work with the [`allow_single_sign_on` or `auto_link_user` parameters](../../integration/omniauth.md#configure-initial-settings).
|
||||
Custom policies are required because standard Azure B2C user flows
|
||||
[do not send the OpenID `email` claim](https://github.com/MicrosoftDocs/azure-docs/issues/16566).
|
||||
Therefore, the standard user flows do not work with the
|
||||
[`allow_single_sign_on` or `auto_link_user` parameters](../../integration/omniauth.md#configure-initial-settings).
|
||||
With a standard Azure B2C policy, GitLab cannot create a new account or
|
||||
link to an existing one with an email address.
|
||||
link to an existing account with an email address.
|
||||
|
||||
Carefully follow the instructions for [creating a custom policy](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy).
|
||||
First, [create a custom policy](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy).
|
||||
|
||||
The Microsoft instructions use `SocialAndLocalAccounts` in the [custom policy starter pack](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy#custom-policy-starter-pack),
|
||||
but `LocalAccounts` works for authenticating against local, Active Directory accounts. Before you follow the instructions to [upload the polices](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy#upload-the-policies), do the following:
|
||||
but `LocalAccounts` authenticates against local Active Directory accounts. Before you [upload the polices](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy#upload-the-policies), do the following:
|
||||
|
||||
1. To export the `email` claim, modify the `SignUpOrSignin.xml`. Replace the following line:
|
||||
|
||||
|
@ -249,7 +273,7 @@ but `LocalAccounts` works for authenticating against local, Active Directory acc
|
|||
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />
|
||||
```
|
||||
|
||||
1. For OIDC discovery to work with B2C, the policy must be configured with an issuer compatible with the
|
||||
1. For OIDC discovery to work with B2C, configure the policy with an issuer compatible with the
|
||||
[OIDC specification](https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.4.3).
|
||||
See the [token compatibility settings](https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-tokens?pivots=b2c-custom-policy#token-compatibility-settings).
|
||||
In `TrustFrameworkBase.xml` under `JwtIssuer`, set `IssuanceClaimPattern` to `AuthorityWithTfp`:
|
||||
|
@ -267,10 +291,10 @@ but `LocalAccounts` works for authenticating against local, Active Directory acc
|
|||
...
|
||||
```
|
||||
|
||||
1. Now [upload the policy](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy#upload-the-policies). Overwrite
|
||||
1. [Upload the policy](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy#upload-the-policies). Overwrite
|
||||
the existing files if you are updating an existing policy.
|
||||
|
||||
1. Determine the issuer URL using the sign-in policy. The issuer URL is in the form:
|
||||
1. To determine the issuer URL, use the sign-in policy. The issuer URL is in the form:
|
||||
|
||||
```markdown
|
||||
https://<YOUR-DOMAIN>/tfp/<YOUR-TENANT-ID>/<YOUR-SIGN-IN-POLICY-NAME>/v2.0/
|
||||
|
@ -279,10 +303,10 @@ but `LocalAccounts` works for authenticating against local, Active Directory acc
|
|||
The policy name is lowercase in the URL. For example, `B2C_1A_signup_signin`
|
||||
policy appears as `b2c_1a_signup_sigin`.
|
||||
|
||||
The trailing forward slash is required.
|
||||
Ensure you include the trailing forward slash.
|
||||
|
||||
1. Verify the operation of the OIDC discovery URL and issuer URL, append `.well-known/openid-configuration`
|
||||
to the issuer URL:
|
||||
1. Verify the operation of the OIDC discovery URL and issuer URL and append
|
||||
`.well-known/openid-configuration` to the issuer URL:
|
||||
|
||||
```markdown
|
||||
https://<YOUR-DOMAIN>/tfp/<YOUR-TENANT-ID>/<YOUR-SIGN-IN-POLICY-NAME>/v2.0/.well-known/openid-configuration
|
||||
|
@ -327,32 +351,34 @@ The trailing forward slash is required.
|
|||
- Ensure all occurrences of `yourtenant.onmicrosoft.com`, `ProxyIdentityExperienceFrameworkAppId`, and `IdentityExperienceFrameworkAppId` match your B2C tenant hostname and
|
||||
the respective client IDs in the XML policy files.
|
||||
- Add `https://jwt.ms` as a redirect URI to the app, and use the [custom policy tester](https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-custom-policy#test-the-custom-policy).
|
||||
Make sure the payload includes `email` that matches the user's email access.
|
||||
- After you enable the custom policy, users might see "Invalid username or password" after they try to sign in. This might be a configuration
|
||||
issue with the `IdentityExperienceFramework` app. See [this Microsoft comment](https://learn.microsoft.com/en-us/answers/questions/50355/unable-to-sign-on-using-custom-policy.html?childToView=122370#comment-122370)
|
||||
that suggests checking that the app manifest contains these settings:
|
||||
Ensure the payload includes `email` that matches the user's email access.
|
||||
- After you enable the custom policy, users might see `Invalid username or password`
|
||||
after they try to sign in. This might be a configuration issue with the `IdentityExperienceFramework`
|
||||
app. See [this Microsoft comment](https://learn.microsoft.com/en-us/answers/questions/50355/unable-to-sign-on-using-custom-policy.html?childToView=122370#comment-122370) that suggests you check that the app manifest
|
||||
contains these settings:
|
||||
|
||||
- `"accessTokenAcceptedVersion": null`
|
||||
- `"signInAudience": "AzureADMyOrg"`
|
||||
|
||||
This configuration corresponds with the `Supported account types` setting used when
|
||||
creating the `IdentityExperienceFramework` app.
|
||||
creating the `IdentityExperienceFramework` app.
|
||||
|
||||
### Keycloak
|
||||
### Configure Keycloak
|
||||
|
||||
GitLab works with OpenID providers that use HTTPS. Although a Keycloak
|
||||
server can be set up using HTTP, GitLab can only communicate
|
||||
with a Keycloak server that uses HTTPS.
|
||||
GitLab works with OpenID providers that use HTTPS. Although you can set up a
|
||||
Keycloak server that uses HTTP, GitLab can only communicate with a Keycloak server
|
||||
that uses HTTPS.
|
||||
|
||||
We highly recommend configuring Keycloak to use public key encryption algorithms (for example,
|
||||
RSA256, RSA512, and so on) instead of symmetric key encryption algorithms (for example, HS256 or HS358) to
|
||||
sign tokens. Public key encryption algorithms are:
|
||||
Configure Keycloak to use public key encryption algorithms (for example,
|
||||
RSA256 or RSA512) instead of symmetric key encryption algorithms (for example,
|
||||
HS256 or HS358) to sign tokens. Public key encryption algorithms are:
|
||||
|
||||
- Easier to configure.
|
||||
- More secure because leaking the private key has severe security consequences.
|
||||
|
||||
The signature algorithm can be configured in the Keycloak administration console under
|
||||
**Realm Settings > Tokens > Default Signature Algorithm**.
|
||||
1. Open the Keycloak administration console.
|
||||
1. Select **Realm Settings > Tokens > Default Signature Algorithm**.
|
||||
1. Configure the signature algorithm.
|
||||
|
||||
Example Omnibus configuration block:
|
||||
|
||||
|
@ -384,37 +410,39 @@ gitlab_rails['omniauth_providers'] = [
|
|||
> Introduced in GitLab 14.2.
|
||||
|
||||
WARNING:
|
||||
The instructions below are included for completeness, but symmetric key
|
||||
encryption should only be used when absolutely necessary.
|
||||
The following instructions are included for completeness, but only use symmetric key
|
||||
encryption if absolutely necessary.
|
||||
|
||||
To use symmetric key encryption:
|
||||
|
||||
1. Extract the secret key from the Keycloak database. Keycloak doesn't expose this value in the Web
|
||||
interface. The client secret seen in the Web interface is the OAuth2 client secret, which is
|
||||
different from the secret used to sign JSON Web Tokens.
|
||||
1. Extract the secret key from the Keycloak database. Keycloak does not expose this
|
||||
value in the web interface. The client secret seen in the web interface is the
|
||||
OAuth 2.0 client secret, which is different from the secret used to sign JSON Web Tokens.
|
||||
|
||||
For example, if you're using PostgreSQL as the backend database for Keycloak, log in to the
|
||||
database console and extract the key via this SQL query:
|
||||
For example, if you use PostgreSQL as the backend database for Keycloak:
|
||||
|
||||
```sql
|
||||
$ psql -U keycloak
|
||||
psql (13.3 (Debian 13.3-1.pgdg100+1))
|
||||
Type "help" for help.
|
||||
- Sign into the database console.
|
||||
- Run the following SQL query to extract the key:
|
||||
|
||||
keycloak=# SELECT c.name, value FROM component_config CC INNER JOIN component C ON(CC.component_id = C.id) WHERE C.realm_id = 'master' and provider_id = 'hmac-generated' AND CC.name = 'secret';
|
||||
-[ RECORD 1 ]---------------------------------------------------------------------------------
|
||||
name | hmac-generated
|
||||
value | lo6cqjD6Ika8pk7qc3fpFx9ysrhf7E62-sqGc8drp3XW-wr93zru8PFsQokHZZuJJbaUXvmiOftCZM3C4KW3-g
|
||||
-[ RECORD 2 ]---------------------------------------------------------------------------------
|
||||
name | fallback-HS384
|
||||
value | UfVqmIs--U61UYsRH-NYBH3_mlluLONpg_zN7CXEwkJcO9xdRNlzZfmfDLPtf2xSTMvqu08R2VhLr-8G-oZ47A
|
||||
```
|
||||
```sql
|
||||
$ psql -U keycloak
|
||||
psql (13.3 (Debian 13.3-1.pgdg100+1))
|
||||
Type "help" for help.
|
||||
|
||||
In this example, there are two private keys: one for HS256 (`hmac-generated`), and another for
|
||||
HS384 (`fallback-HS384`). We use the first `value` to configure GitLab.
|
||||
keycloak=# SELECT c.name, value FROM component_config CC INNER JOIN component C ON(CC.component_id = C.id) WHERE C.realm_id = 'master' and provider_id = 'hmac-generated' AND CC.name = 'secret';
|
||||
-[ RECORD 1 ]---------------------------------------------------------------------------------
|
||||
name | hmac-generated
|
||||
value | lo6cqjD6Ika8pk7qc3fpFx9ysrhf7E62-sqGc8drp3XW-wr93zru8PFsQokHZZuJJbaUXvmiOftCZM3C4KW3-g
|
||||
-[ RECORD 2 ]---------------------------------------------------------------------------------
|
||||
name | fallback-HS384
|
||||
value | UfVqmIs--U61UYsRH-NYBH3_mlluLONpg_zN7CXEwkJcO9xdRNlzZfmfDLPtf2xSTMvqu08R2VhLr-8G-oZ47A
|
||||
```
|
||||
|
||||
1. Convert `value` to standard base64. As [discussed in the post](https://keycloak.discourse.group/t/invalid-signature-with-hs256-token/3228/9),
|
||||
`value` is encoded in ["Base 64 Encoding with URL and Filename Safe Alphabet" in RFC 4648](https://datatracker.ietf.org/doc/html/rfc4648#section-5).
|
||||
In this example, there are two private keys: one for HS256 (`hmac-generated`)
|
||||
and another for HS384 (`fallback-HS384`). We use the first `value` to configure GitLab.
|
||||
|
||||
1. Convert `value` to standard base64. As discussed in the [**Invalid signature with HS256 token** post](https://keycloak.discourse.group/t/invalid-signature-with-hs256-token/3228/9),
|
||||
`value` is encoded in the [**Base 64 Encoding with URL and Filename Safe Alphabet** section](https://datatracker.ietf.org/doc/html/rfc4648#section-5) of RFC 4648.
|
||||
This must be converted to [standard base64 as defined in RFC 2045](https://datatracker.ietf.org/doc/html/rfc2045).
|
||||
The following Ruby script does this:
|
||||
|
||||
|
@ -457,17 +485,19 @@ To use symmetric key encryption:
|
|||
]
|
||||
```
|
||||
|
||||
If after reconfiguring, you see the error `JSON::JWS::VerificationFailed` error message, this means
|
||||
the incorrect secret was specified.
|
||||
If you see a `JSON::JWS::VerificationFailed` error,
|
||||
you have specified the wrong secret.
|
||||
|
||||
### Casdoor
|
||||
|
||||
GitLab works with OpenID providers that use HTTPS. To connect to GitLab using OpenID with Casdoor, use HTTPS instead of HTTP.
|
||||
GitLab works with OpenID providers that use HTTPS. Use HTTPS to connect to GitLab
|
||||
through OpenID with Casdoor.
|
||||
|
||||
For your app, complete the following steps on Casdoor:
|
||||
|
||||
1. Get a client ID and a client secret.
|
||||
1. Add your GitLab redirect URL. For example, if your GitLab domain is `gitlab.example.com`, ensure the Casdoor app has the following
|
||||
1. Add your GitLab redirect URL. For example, if your GitLab domain is `gitlab.example.com`,
|
||||
ensure the Casdoor app has the following
|
||||
`Redirect URI`: `https://gitlab.example.com/users/auth/openid_connect/callback`.
|
||||
|
||||
See the [Casdoor documentation](https://casdoor.org/docs/integration/gitlab) for more details.
|
||||
|
@ -519,23 +549,21 @@ Example installations from source configuration (file path: `config/gitlab.yml`)
|
|||
}
|
||||
```
|
||||
|
||||
## General troubleshooting
|
||||
## Troubleshooting
|
||||
|
||||
If you're having trouble, here are some tips:
|
||||
|
||||
1. Ensure `discovery` is set to `true`. Setting it to `false` requires
|
||||
specifying all the URLs and keys required to make OpenID work.
|
||||
1. Ensure `discovery` is set to `true`. If you set it to `false`, you must
|
||||
specify all the URLs and keys required to make OpenID work.
|
||||
|
||||
1. Check your system clock to ensure the time is synchronized properly.
|
||||
|
||||
1. As mentioned in [the documentation](https://github.com/m0n9oose/omniauth_openid_connect),
|
||||
1. As mentioned in [the OmniAuth OpenID Connect documentation](https://github.com/m0n9oose/omniauth_openid_connect),
|
||||
make sure `issuer` corresponds to the base URL of the Discovery URL. For
|
||||
example, `https://accounts.google.com` is used for the URL
|
||||
`https://accounts.google.com/.well-known/openid-configuration`.
|
||||
|
||||
1. The OpenID Connect client uses HTTP Basic Authentication to send the
|
||||
OAuth2 access token if `client_auth_method` is not defined or if set to `basic`.
|
||||
If you are seeing 401 errors upon retrieving the `userinfo` endpoint, you may
|
||||
want to check your OpenID Web server configuration. For example, for
|
||||
[`oauth2-server-php`](https://github.com/bshaffer/oauth2-server-php), you may need to
|
||||
OAuth 2.0 access token if `client_auth_method` is not defined or if set to `basic`.
|
||||
If you see 401 errors when retrieving the `userinfo` endpoint, check
|
||||
your OpenID web server configuration. For example, for
|
||||
[`oauth2-server-php`](https://github.com/bshaffer/oauth2-server-php), you may have to
|
||||
[add a configuration parameter to Apache](https://github.com/bshaffer/oauth2-server-php/issues/926#issuecomment-387502778).
|
||||
|
|
|
@ -23021,6 +23021,7 @@ Field that are available while modifying the custom mapping attributes for an HT
|
|||
| <a id="boardissueinputconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter by confidentiality. |
|
||||
| <a id="boardissueinputepicid"></a>`epicId` | [`EpicID`](#epicid) | Filter by epic ID. Incompatible with epicWildcardId. |
|
||||
| <a id="boardissueinputepicwildcardid"></a>`epicWildcardId` | [`EpicWildcardId`](#epicwildcardid) | Filter by epic ID wildcard. Incompatible with epicId. |
|
||||
| <a id="boardissueinputhealthstatusfilter"></a>`healthStatusFilter` | [`HealthStatusFilter`](#healthstatusfilter) | Health status of the issue, "none" and "any" values are supported. |
|
||||
| <a id="boardissueinputiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of issues. For example `["1", "2"]`. |
|
||||
| <a id="boardissueinputiterationcadenceid"></a>`iterationCadenceId` | [`[IterationsCadenceID!]`](#iterationscadenceid) | Filter by a list of iteration cadence IDs. |
|
||||
| <a id="boardissueinputiterationid"></a>`iterationId` | [`[IterationID!]`](#iterationid) | Filter by a list of iteration IDs. Incompatible with iterationWildcardId. |
|
||||
|
|
|
@ -13,7 +13,7 @@ or the provider that uses the v2.0 endpoint.
|
|||
|
||||
NOTE:
|
||||
For new projects, Microsoft suggests you use the
|
||||
[OpenID Connect protocol](../administration/auth/oidc.md#microsoft-azure),
|
||||
[OpenID Connect protocol](../administration/auth/oidc.md#configure-microsoft-azure),
|
||||
which uses the Microsoft identity platform (v2.0) endpoint.
|
||||
|
||||
## Register an Azure application
|
||||
|
|
|
@ -183,7 +183,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def search_query(str:, type:, include_collaborations: true, include_orgs: true)
|
||||
query = "#{str} in:#{type} is:public,private user:#{octokit.user.login}"
|
||||
query = "#{str} in:#{type} is:public,private user:#{octokit.user.to_h[:login]}"
|
||||
|
||||
query = [query, collaborations_subquery].join(' ') if include_collaborations
|
||||
query = [query, organizations_subquery].join(' ') if include_orgs
|
||||
|
@ -274,13 +274,13 @@ module Gitlab
|
|||
|
||||
def collaborations_subquery
|
||||
each_object(:repos, nil, { affiliation: 'collaborator' })
|
||||
.map { |repo| "repo:#{repo.full_name}" }
|
||||
.map { |repo| "repo:#{repo[:full_name]}" }
|
||||
.join(' ')
|
||||
end
|
||||
|
||||
def organizations_subquery
|
||||
each_object(:organizations)
|
||||
.map { |org| "org:#{org.login}" }
|
||||
.map { |org| "org:#{org[:login]}" }
|
||||
.join(' ')
|
||||
end
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ module Gitlab
|
|||
CATEGORIES_FOR_TOTALS = %w[
|
||||
analytics
|
||||
compliance
|
||||
epics_usage
|
||||
error_tracking
|
||||
ide_edit
|
||||
incident_management
|
||||
|
|
|
@ -1,227 +0,0 @@
|
|||
# Epic events
|
||||
#
|
||||
# We are using the same slot of issue events 'project_management' for
|
||||
# epic events to allow data aggregation.
|
||||
# More information in: https://gitlab.com/gitlab-org/gitlab/-/issues/322405
|
||||
- name: g_project_management_epic_created
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
# content change events
|
||||
|
||||
- name: project_management_users_unchecking_epic_task
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: project_management_users_checking_epic_task
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_updating_epic_titles
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_updating_epic_descriptions
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
# epic notes
|
||||
|
||||
- name: g_project_management_users_creating_epic_notes
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_updating_epic_notes
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_destroying_epic_notes
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
# emoji
|
||||
|
||||
- name: g_project_management_users_awarding_epic_emoji
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_removing_epic_emoji
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
# start date events
|
||||
|
||||
- name: g_project_management_users_setting_epic_start_date_as_fixed
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_updating_fixed_epic_start_date
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_setting_epic_start_date_as_inherited
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
# due date events
|
||||
|
||||
- name: g_project_management_users_setting_epic_due_date_as_fixed
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_updating_fixed_epic_due_date
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_setting_epic_due_date_as_inherited
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
# relationships
|
||||
|
||||
- name: g_project_management_epic_issue_added
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_issue_removed
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_issue_moved_from_project
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_updating_epic_parent
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_closed
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_reopened
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: 'g_project_management_issue_promoted_to_epic'
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_setting_epic_confidential
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_setting_epic_visible
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_users_changing_labels
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_destroyed
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_cross_referenced
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_users_epic_issue_added_from_epic
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_related_added
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_related_removed
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_blocking_added
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_blocking_removed
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_blocked_added
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
||||
- name: g_project_management_epic_blocked_removed
|
||||
category: epics_usage
|
||||
redis_slot: project_management
|
||||
aggregation: daily
|
||||
feature_flag: track_epics_activity
|
||||
|
|
@ -3458,9 +3458,21 @@ msgstr ""
|
|||
msgid "AdvancedSearch|Elasticsearch version not compatible"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdvancedSearch|Introduced in GitLab 13.1, before using %{reindexing_link_start}zero-downtime reindexing%{link_end} and %{migrations_link_start}Advanced Search migrations%{link_end}, you need to %{recreate_link_start}recreate your index%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdvancedSearch|Pause indexing and upgrade Elasticsearch to a supported version."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdvancedSearch|Reindex recommended"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdvancedSearch|Reindex required"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdvancedSearch|You are using outdated code search mappings. To improve code search quality, we recommend you use %{reindexing_link_start}zero-downtime reindexing%{link_end} or %{recreate_link_start}re-create your index%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "After a successful password update you will be redirected to login screen."
|
||||
msgstr ""
|
||||
|
||||
|
@ -19470,7 +19482,7 @@ msgstr ""
|
|||
msgid "Harbor Registry"
|
||||
msgstr ""
|
||||
|
||||
msgid "HarborIntegration|After the Harbor integration is activated, global variables $HARBOR_USERNAME, $HARBOR_HOST, $HARBOR_OCI, $HARBOR_PASSWORD, $HARBOR_URL and $HARBOR_PROJECT will be created for CI/CD use."
|
||||
msgid "HarborIntegration|After the Harbor integration is activated, global variables `$HARBOR_USERNAME`, `$HARBOR_HOST`, `$HARBOR_OCI`, `$HARBOR_PASSWORD`, `$HARBOR_URL` and `$HARBOR_PROJECT` will be created for CI/CD use."
|
||||
msgstr ""
|
||||
|
||||
msgid "HarborIntegration|Base URL of the Harbor instance."
|
||||
|
@ -21838,9 +21850,6 @@ msgstr ""
|
|||
msgid "Interval Pattern"
|
||||
msgstr ""
|
||||
|
||||
msgid "Introduced in GitLab 13.1, before using %{reindexing_link_start}zero-downtime reindexing%{link_end} and %{migrations_link_start}Advanced Search migrations%{link_end}, you need to %{recreate_link_start}recreate your index%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Introducing Your DevOps Reports"
|
||||
msgstr ""
|
||||
|
||||
|
@ -29025,9 +29034,6 @@ msgstr ""
|
|||
msgid "Pause"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pause indexing and upgrade Elasticsearch to a supported version."
|
||||
msgstr ""
|
||||
|
||||
msgid "Pause time (ms)"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -218,7 +218,7 @@ RSpec.describe Import::GithubController do
|
|||
|
||||
it 'makes request to github search api' do
|
||||
expect_next_instance_of(Octokit::Client) do |client|
|
||||
expect(client).to receive(:user).and_return(double(login: user_login))
|
||||
expect(client).to receive(:user).and_return({ login: user_login })
|
||||
expect(client).to receive(:search_repositories).with(search_query, { page: 1, per_page: 25 }).and_return({ items: [].to_enum })
|
||||
end
|
||||
|
||||
|
@ -234,7 +234,7 @@ RSpec.describe Import::GithubController do
|
|||
context 'when no page is specified' do
|
||||
it 'requests first page' do
|
||||
expect_next_instance_of(Octokit::Client) do |client|
|
||||
expect(client).to receive(:user).and_return(double(login: user_login))
|
||||
expect(client).to receive(:user).and_return({ login: user_login })
|
||||
expect(client).to receive(:search_repositories).with(search_query, { page: 1, per_page: 25 }).and_return({ items: [].to_enum })
|
||||
end
|
||||
|
||||
|
@ -250,7 +250,7 @@ RSpec.describe Import::GithubController do
|
|||
context 'when page is specified' do
|
||||
it 'requests repos with specified page' do
|
||||
expect_next_instance_of(Octokit::Client) do |client|
|
||||
expect(client).to receive(:user).and_return(double(login: user_login))
|
||||
expect(client).to receive(:user).and_return({ login: user_login })
|
||||
expect(client).to receive(:search_repositories).with(search_query, { page: 2, per_page: 25 }).and_return({ items: [].to_enum })
|
||||
end
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
DESIGN_SNOWPLOW_EVENT_TYPES,
|
||||
DESIGN_SERVICE_PING_EVENT_TYPES,
|
||||
} from '~/design_management/utils/tracking';
|
||||
import createFlash from '~/flash';
|
||||
import { createAlert } from '~/flash';
|
||||
import mockAllVersions from '../../mock_data/all_versions';
|
||||
import design from '../../mock_data/design';
|
||||
import mockResponseWithDesigns from '../../mock_data/designs';
|
||||
|
@ -301,8 +301,8 @@ describe('Design management design index page', () => {
|
|||
|
||||
wrapper.vm.onDesignQueryResult({ data: mockResponseNoDesigns, loading: false });
|
||||
await nextTick();
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith({ message: DESIGN_NOT_FOUND_ERROR });
|
||||
expect(createAlert).toHaveBeenCalledTimes(1);
|
||||
expect(createAlert).toHaveBeenCalledWith({ message: DESIGN_NOT_FOUND_ERROR });
|
||||
expect(router.push).toHaveBeenCalledTimes(1);
|
||||
expect(router.push).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME });
|
||||
});
|
||||
|
@ -323,8 +323,8 @@ describe('Design management design index page', () => {
|
|||
|
||||
wrapper.vm.onDesignQueryResult({ data: mockResponseWithDesigns, loading: false });
|
||||
await nextTick();
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith({ message: DESIGN_VERSION_NOT_EXIST_ERROR });
|
||||
expect(createAlert).toHaveBeenCalledTimes(1);
|
||||
expect(createAlert).toHaveBeenCalledWith({ message: DESIGN_VERSION_NOT_EXIST_ERROR });
|
||||
expect(router.push).toHaveBeenCalledTimes(1);
|
||||
expect(router.push).toHaveBeenCalledWith({ name: DESIGNS_ROUTE_NAME });
|
||||
});
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
DESIGN_TRACKING_PAGE_NAME,
|
||||
DESIGN_SNOWPLOW_EVENT_TYPES,
|
||||
} from '~/design_management/utils/tracking';
|
||||
import createFlash from '~/flash';
|
||||
import { createAlert } from '~/flash';
|
||||
import DesignDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
|
||||
import {
|
||||
designListQueryResponse,
|
||||
|
@ -808,7 +808,7 @@ describe('Design management index page', () => {
|
|||
await moveDesigns(wrapper);
|
||||
await waitForPromises();
|
||||
|
||||
expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
|
||||
expect(createAlert).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
|
||||
});
|
||||
|
||||
it('displays alert if mutation had a non-recoverable error', async () => {
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
ADD_IMAGE_DIFF_NOTE_ERROR,
|
||||
UPDATE_IMAGE_DIFF_NOTE_ERROR,
|
||||
} from '~/design_management/utils/error_messages';
|
||||
import createFlash from '~/flash';
|
||||
import { createAlert } from '~/flash';
|
||||
import design from '../mock_data/design';
|
||||
|
||||
jest.mock('~/flash.js');
|
||||
|
@ -32,10 +32,10 @@ describe('Design Management cache update', () => {
|
|||
${'updateStoreAfterUploadDesign'} | ${updateStoreAfterUploadDesign} | ${mockErrors[0]} | ${[]}
|
||||
${'updateStoreAfterUpdateImageDiffNote'} | ${updateStoreAfterRepositionImageDiffNote} | ${UPDATE_IMAGE_DIFF_NOTE_ERROR} | ${[]}
|
||||
`('$fnName handles errors in response', ({ subject, extraArgs, errorMessage }) => {
|
||||
expect(createFlash).not.toHaveBeenCalled();
|
||||
expect(createAlert).not.toHaveBeenCalled();
|
||||
expect(() => subject(mockStore, { errors: mockErrors }, {}, ...extraArgs)).toThrow();
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith({ message: errorMessage });
|
||||
expect(createAlert).toHaveBeenCalledTimes(1);
|
||||
expect(createAlert).toHaveBeenCalledWith({ message: errorMessage });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -848,289 +848,4 @@ RSpec.describe SearchHelper do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.search_navigation' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
let(:user) { build(:user) }
|
||||
let(:project) { build(:project) }
|
||||
|
||||
before do
|
||||
stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
|
||||
allow(self).to receive(:current_user).and_return(user)
|
||||
allow(self).to receive(:can?).and_return(true)
|
||||
allow(self).to receive(:project_search_tabs?).and_return(false)
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).and_return(false)
|
||||
end
|
||||
|
||||
context 'projects' do
|
||||
where(:global_project, :condition) do
|
||||
nil | true
|
||||
ref(:project) | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
|
||||
expect(search_navigation[:projects][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'code' do
|
||||
where(:feature_flag_tab_enabled, :show_elasticsearch_tabs, :project_search_tabs, :condition) do
|
||||
false | false | false | false
|
||||
true | true | true | true
|
||||
true | false | false | true
|
||||
false | true | false | true
|
||||
false | false | true | true
|
||||
true | false | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_code_tab).and_return(feature_flag_tab_enabled)
|
||||
allow(self).to receive(:project_search_tabs?).with(:blobs).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:blobs][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'epics' do
|
||||
where(:global_project, :show_epics, :condition) do
|
||||
nil | false | false
|
||||
ref(:project) | true | false
|
||||
ref(:project) | false | false
|
||||
nil | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
allow(search_service).to receive(:show_epics?).and_return(show_epics)
|
||||
|
||||
expect(search_navigation[:epics][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'issues' do
|
||||
where(:feature_flag_tab_enabled, :project_search_tabs, :condition) do
|
||||
false | false | false
|
||||
true | true | true
|
||||
true | false | true
|
||||
false | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_issues_tab).and_return(feature_flag_tab_enabled)
|
||||
allow(self).to receive(:project_search_tabs?).with(:issues).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:issues][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'merge requests' do
|
||||
where(:feature_flag_tab_enabled, :project_search_tabs, :condition) do
|
||||
false | false | false
|
||||
true | true | true
|
||||
true | false | true
|
||||
false | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_merge_requests_tab).and_return(feature_flag_tab_enabled)
|
||||
allow(self).to receive(:project_search_tabs?).with(:merge_requests).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:merge_requests][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'wiki' do
|
||||
where(:project_search_tabs, :show_elasticsearch_tabs, :condition) do
|
||||
false | false | false
|
||||
true | true | true
|
||||
true | false | true
|
||||
false | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
|
||||
allow(self).to receive(:project_search_tabs?).with(:wiki).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:wiki_blobs][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'commits' do
|
||||
where(:feature_flag_tab_enabled, :show_elasticsearch_tabs, :project_search_tabs, :condition) do
|
||||
false | false | false | false
|
||||
true | true | true | true
|
||||
true | false | false | false
|
||||
false | true | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_commits_tab).and_return(feature_flag_tab_enabled)
|
||||
allow(self).to receive(:project_search_tabs?).with(:commits).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:commits][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'comments' do
|
||||
where(:show_elasticsearch_tabs, :project_search_tabs, :condition) do
|
||||
true | true | true
|
||||
false | false | false
|
||||
true | false | true
|
||||
false | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
|
||||
allow(self).to receive(:project_search_tabs?).with(:notes).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:notes][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'milestones' do
|
||||
where(:global_project, :project_search_tabs, :condition) do
|
||||
ref(:project) | true | true
|
||||
nil | false | true
|
||||
ref(:project) | false | false
|
||||
nil | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
allow(self).to receive(:project_search_tabs?).with(:milestones).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:milestones][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'users' do
|
||||
where(:show_user_search_tab, :condition) do
|
||||
true | true
|
||||
false | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(self).to receive(:show_user_search_tab?).and_return(show_user_search_tab)
|
||||
|
||||
expect(search_navigation[:users][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'snippet_titles' do
|
||||
where(:global_project, :global_show_snippets, :condition) do
|
||||
ref(:project) | true | false
|
||||
nil | false | false
|
||||
ref(:project) | false | false
|
||||
nil | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@show_snippets = global_show_snippets
|
||||
@project = global_project
|
||||
|
||||
expect(search_navigation[:snippet_titles][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.search_navigation_json' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
context 'data' do
|
||||
example_data_1 = {
|
||||
projects: { label: _("Projects"), condition: true },
|
||||
blobs: { label: _("Code"), condition: false }
|
||||
}
|
||||
|
||||
example_data_2 = {
|
||||
projects: { label: _("Projects"), condition: false },
|
||||
blobs: { label: _("Code"), condition: false }
|
||||
}
|
||||
|
||||
example_data_3 = {
|
||||
projects: { label: _("Projects"), condition: true },
|
||||
blobs: { label: _("Code"), condition: true },
|
||||
epics: { label: _("Epics"), condition: true }
|
||||
}
|
||||
|
||||
where(:data, :matcher) do
|
||||
example_data_1 | -> { include("projects") }
|
||||
example_data_2 | -> { eq("{}") }
|
||||
example_data_3 | -> { include("projects", "blobs", "epics") }
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'converts correctly' do
|
||||
allow(self).to receive(:search_navigation).with(no_args).and_return(data)
|
||||
expect(search_navigation_json).to instance_exec(&matcher)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.search_filter_link_json' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
context 'data' do
|
||||
where(:scope, :label, :data, :search, :active_scope) do
|
||||
"projects" | "Projects" | { qa_selector: 'projects_tab' } | nil | "projects"
|
||||
"snippet_titles" | "Titles and Descriptions" | nil | { snippets: "test" } | "code"
|
||||
"projects" | "Projects" | { qa_selector: 'projects_tab' } | nil | "issue"
|
||||
"snippet_titles" | "Titles and Descriptions" | nil | { snippets: "test" } | "snippet_titles"
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'converts correctly' do
|
||||
@timeout = false
|
||||
@scope = active_scope
|
||||
@search_results = double
|
||||
dummy_count = 1000
|
||||
allow(self).to receive(:search_path).with(any_args).and_return("link test")
|
||||
|
||||
allow(@search_results).to receive(:formatted_count).with(scope).and_return(dummy_count)
|
||||
allow(self).to receive(:search_count_path).with(any_args).and_return("test count link")
|
||||
|
||||
current_scope = scope == active_scope
|
||||
|
||||
expected = {
|
||||
label: label,
|
||||
scope: scope,
|
||||
data: data,
|
||||
link: "link test",
|
||||
active: current_scope
|
||||
}
|
||||
|
||||
expected[:count] = dummy_count if current_scope
|
||||
expected[:count_link] = "test count link" unless current_scope
|
||||
|
||||
expect(search_filter_link_json(scope, label, data, search)).to eq(expected)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -591,11 +591,11 @@ RSpec.describe Gitlab::GithubImport::Client do
|
|||
|
||||
describe 'search' do
|
||||
let(:client) { described_class.new('foo') }
|
||||
let(:user) { double(:user, login: 'user') }
|
||||
let(:org1) { double(:org, login: 'org1') }
|
||||
let(:org2) { double(:org, login: 'org2') }
|
||||
let(:repo1) { double(:repo, full_name: 'repo1') }
|
||||
let(:repo2) { double(:repo, full_name: 'repo2') }
|
||||
let(:user) { { login: 'user' } }
|
||||
let(:org1) { { login: 'org1' } }
|
||||
let(:org2) { { login: 'org2' } }
|
||||
let(:repo1) { { full_name: 'repo1' } }
|
||||
let(:repo2) { { full_name: 'repo2' } }
|
||||
|
||||
before do
|
||||
allow(client)
|
||||
|
|
|
@ -105,7 +105,6 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
|
|||
'ci_templates',
|
||||
'quickactions',
|
||||
'pipeline_authoring',
|
||||
'epics_usage',
|
||||
'secure',
|
||||
'importer',
|
||||
'geo',
|
||||
|
|
Loading…
Reference in a new issue