Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-06-23 03:07:43 +00:00
parent ec8587780b
commit 0fb62bd3e1
38 changed files with 308 additions and 231 deletions

View File

@ -720,7 +720,18 @@ export const searchBy = (query = '', searchSpace = {}) => {
* @param {Object} label
* @returns Boolean
*/
export const isScopedLabel = ({ title = '' }) => title.indexOf('::') !== -1;
export const isScopedLabel = ({ title = '' } = {}) => title.indexOf('::') !== -1;
/**
* Returns the base value of the scoped label
*
* Expected Label to be an Object with `title` as a key:
* { title: 'LabelTitle', ...otherProperties };
*
* @param {Object} label
* @returns String
*/
export const scopedLabelKey = ({ title = '' }) => isScopedLabel({ title }) && title.split('::')[0];
// Methods to set and get Cookie
export const setCookie = (name, value) => Cookies.set(name, value, { expires: 365 });

View File

@ -19,6 +19,11 @@ export default {
type: String,
required: true,
},
tabQueryParamValue: {
type: String,
required: false,
default: '',
},
},
computed: {
...mapState({
@ -55,6 +60,6 @@ export default {
errorMessage
}}</gl-alert>
<filter-sort-container />
<members-table />
<members-table :tab-query-param-value="tabQueryParamValue" />
</div>
</template>

View File

@ -5,7 +5,11 @@ import { getParameterByName } from '~/lib/utils/common_utils';
// eslint-disable-next-line import/no-deprecated
import { setUrlParams, urlParamsToObject } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import { SEARCH_TOKEN_TYPE, SORT_PARAM } from '~/members/constants';
import {
SEARCH_TOKEN_TYPE,
SORT_QUERY_PARAM_NAME,
ACTIVE_TAB_QUERY_PARAM_NAME,
} from '~/members/constants';
import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
@ -118,10 +122,15 @@ export default {
return accumulator;
}, {});
const sortParam = getParameterByName(SORT_PARAM);
const sortParamValue = getParameterByName(SORT_QUERY_PARAM_NAME);
const activeTabParamValue = getParameterByName(ACTIVE_TAB_QUERY_PARAM_NAME);
window.location.href = setUrlParams(
{ ...params, ...(sortParam && { sort: sortParam }) },
{
...params,
...(sortParamValue && { [SORT_QUERY_PARAM_NAME]: sortParamValue }),
...(activeTabParamValue && { [ACTIVE_TAB_QUERY_PARAM_NAME]: activeTabParamValue }),
},
window.location.href,
true,
);

View File

@ -4,14 +4,15 @@ import { mapState } from 'vuex';
// eslint-disable-next-line import/no-deprecated
import { urlParamsToObject } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import { MEMBER_TYPES } from '../constants';
import { MEMBER_TYPES, TAB_QUERY_PARAM_VALUES, ACTIVE_TAB_QUERY_PARAM_NAME } from '../constants';
import MembersApp from './app.vue';
const countComputed = (state, namespace) => state[namespace]?.pagination?.totalItems || 0;
export default {
name: 'MembersTabs',
tabs: [
ACTIVE_TAB_QUERY_PARAM_NAME,
TABS: [
{
namespace: MEMBER_TYPES.user,
title: __('Members'),
@ -20,19 +21,21 @@ export default {
namespace: MEMBER_TYPES.group,
title: __('Groups'),
attrs: { 'data-qa-selector': 'groups_list_tab' },
queryParamValue: TAB_QUERY_PARAM_VALUES.group,
},
{
namespace: MEMBER_TYPES.invite,
title: __('Invited'),
canManageMembersPermissionsRequired: true,
queryParamValue: TAB_QUERY_PARAM_VALUES.invite,
},
{
namespace: MEMBER_TYPES.accessRequest,
title: __('Access requests'),
canManageMembersPermissionsRequired: true,
queryParamValue: TAB_QUERY_PARAM_VALUES.accessRequest,
},
],
urlParams: [],
components: { MembersApp, GlTabs, GlTab, GlBadge },
inject: ['canManageMembers'],
data() {
@ -60,29 +63,18 @@ export default {
return Object.keys(urlParamsToObject(window.location.search));
},
activeTabIndexCalculatedFromUrlParams() {
return this.$options.tabs.findIndex(({ namespace }) => {
return this.$options.TABS.findIndex(({ namespace }) => {
return this.getTabUrlParams(namespace).some((urlParam) =>
this.urlParams.includes(urlParam),
);
});
},
},
created() {
if (this.activeTabIndexCalculatedFromUrlParams === -1) {
return;
}
this.selectedTabIndex = this.activeTabIndexCalculatedFromUrlParams;
},
methods: {
getTabUrlParams(namespace) {
const state = this.$store.state[namespace];
const urlParams = [];
if (state?.pagination?.paramName) {
urlParams.push(state.pagination.paramName);
}
if (state?.filteredSearchBar?.searchParam) {
urlParams.push(state.filteredSearchBar.searchParam);
}
@ -112,14 +104,23 @@ export default {
</script>
<template>
<gl-tabs v-model="selectedTabIndex">
<template v-for="(tab, index) in $options.tabs">
<gl-tab v-if="showTab(tab, index)" :key="tab.namespace" :title-link-attributes="tab.attrs">
<gl-tabs
v-model="selectedTabIndex"
sync-active-tab-with-query-params
:query-param-name="$options.ACTIVE_TAB_QUERY_PARAM_NAME"
>
<template v-for="(tab, index) in $options.TABS">
<gl-tab
v-if="showTab(tab, index)"
:key="tab.namespace"
:title-link-attributes="tab.attrs"
:query-param-value="tab.queryParamValue"
>
<template slot="title">
<span>{{ tab.title }}</span>
<gl-badge size="sm" class="gl-tab-counter-badge">{{ getTabCount(tab) }}</gl-badge>
</template>
<members-app :namespace="tab.namespace" />
<members-app :namespace="tab.namespace" :tab-query-param-value="tab.queryParamValue" />
</gl-tab>
</template>
</gl-tabs>

View File

@ -5,7 +5,7 @@ import MembersTableCell from 'ee_else_ce/members/components/table/members_table_
import { canOverride, canRemove, canResend, canUpdate } from 'ee_else_ce/members/utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import initUserPopovers from '~/user_popovers';
import { FIELDS } from '../../constants';
import { FIELDS, ACTIVE_TAB_QUERY_PARAM_NAME } from '../../constants';
import RemoveGroupLinkModal from '../modals/remove_group_link_modal.vue';
import CreatedAt from './created_at.vue';
import ExpirationDatepicker from './expiration_datepicker.vue';
@ -34,6 +34,13 @@ export default {
import('ee_component/members/components/ldap/ldap_override_confirmation_modal.vue'),
},
inject: ['namespace', 'currentUserId'],
props: {
tabQueryParamValue: {
type: String,
required: false,
default: '',
},
},
computed: {
...mapState({
members(state) {
@ -112,7 +119,15 @@ export default {
paginationLinkGenerator(page) {
const { params = {}, paramName } = this.pagination;
return mergeUrlParams({ ...params, [paramName]: page }, window.location.href);
return mergeUrlParams(
{
...params,
[ACTIVE_TAB_QUERY_PARAM_NAME]:
this.tabQueryParamValue !== '' ? this.tabQueryParamValue : null,
[paramName]: page,
},
window.location.href,
);
},
},
};

View File

@ -89,6 +89,12 @@ export const MEMBER_TYPES = {
accessRequest: 'accessRequest',
};
export const TAB_QUERY_PARAM_VALUES = {
group: 'groups',
invite: 'invited',
accessRequest: 'access_requests',
};
export const DAYS_TO_EXPIRE_SOON = 7;
export const LEAVE_MODAL_ID = 'member-leave-modal';
@ -97,7 +103,8 @@ export const REMOVE_GROUP_LINK_MODAL_ID = 'remove-group-link-modal-id';
export const SEARCH_TOKEN_TYPE = 'filtered-search-term';
export const SORT_PARAM = 'sort';
export const SORT_QUERY_PARAM_NAME = 'sort';
export const ACTIVE_TAB_QUERY_PARAM_NAME = 'tab';
export const MEMBER_ACCESS_LEVEL_PROPERTY_NAME = 'access_level';

View File

@ -1,3 +1,4 @@
import { isScopedLabel, scopedLabelKey } from '~/lib/utils/common_utils';
import { DropdownVariant } from '../constants';
import * as types from './mutation_types';
@ -66,5 +67,16 @@ export default {
candidateLabel.touched = true;
candidateLabel.set = !candidateLabel.set;
}
if (isScopedLabel(candidateLabel)) {
const scopedBase = scopedLabelKey(candidateLabel);
const currentActiveScopedLabel = state.labels.find(({ title }) => {
return title.startsWith(scopedBase) && title !== '' && title !== candidateLabel.title;
});
if (currentActiveScopedLabel) {
currentActiveScopedLabel.set = false;
}
}
},
};

View File

@ -1,3 +1,4 @@
import { isScopedLabel, scopedLabelKey } from '~/lib/utils/common_utils';
import { DropdownVariant } from '../constants';
import * as types from './mutation_types';
@ -55,5 +56,16 @@ export default {
candidateLabel.touched = true;
candidateLabel.set = !candidateLabel.set;
}
if (isScopedLabel(candidateLabel)) {
const scopedBase = scopedLabelKey(candidateLabel);
const currentActiveScopedLabel = state.labels.find(
({ title }) => title.indexOf(scopedBase) === 0 && title !== candidateLabel.title,
);
if (currentActiveScopedLabel) {
currentActiveScopedLabel.set = false;
}
}
},
};

View File

@ -1,8 +0,0 @@
---
name: ci_workflow_rules_variables
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52085
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300997
milestone: '13.11'
type: development
group: group::pipeline authoring
default_enabled: true

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true
Redis::Client.prepend(Gitlab::Instrumentation::RedisInterceptor)
# Make sure we initialize a Redis connection pool before multi-threaded
# execution starts by
# 1. Sidekiq

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddRunningContainerScanningMaxSizeToPlanLimits < ActiveRecord::Migration[6.0]
def change
add_column :plan_limits, :ci_max_artifact_size_running_container_scanning, :integer, null: false, default: 0
end
end

View File

@ -0,0 +1 @@
5dc0a4f91dc35b7720b20e89fa77374a0337c380b9234704e7d4143639e512f0

View File

@ -16299,7 +16299,8 @@ CREATE TABLE plan_limits (
ci_registered_group_runners integer DEFAULT 1000 NOT NULL,
ci_registered_project_runners integer DEFAULT 1000 NOT NULL,
web_hook_calls integer DEFAULT 0 NOT NULL,
ci_daily_pipeline_schedule_triggers integer DEFAULT 0 NOT NULL
ci_daily_pipeline_schedule_triggers integer DEFAULT 0 NOT NULL,
ci_max_artifact_size_running_container_scanning integer DEFAULT 0 NOT NULL
);
CREATE SEQUENCE plan_limits_id_seq

View File

@ -169,7 +169,7 @@ Read more about [Gitaly concurrency limits](gitaly/configure_gitaly.md#limit-rpc
There's a limit to the number of comments that can be submitted on an issue,
merge request, or commit. When the limit is reached, system notes can still be
added so that the history of events is not lost, but the user-submitted
added so that the history of events is not lost, but the user-submitted
comment fails.
- **Max limit**: 5,000 comments.
@ -443,33 +443,34 @@ setting is used:
| Artifact limit name | Default value |
|---------------------------------------------|---------------|
| `ci_max_artifact_size_accessibility` | 0 |
| `ci_max_artifact_size_api_fuzzing` | 0 |
| `ci_max_artifact_size_archive` | 0 |
| `ci_max_artifact_size_browser_performance` | 0 |
| `ci_max_artifact_size_cluster_applications` | 0 |
| `ci_max_artifact_size_cobertura` | 0 |
| `ci_max_artifact_size_codequality` | 0 |
| `ci_max_artifact_size_container_scanning` | 0 |
| `ci_max_artifact_size_coverage_fuzzing` | 0 |
| `ci_max_artifact_size_dast` | 0 |
| `ci_max_artifact_size_dependency_scanning` | 0 |
| `ci_max_artifact_size_dotenv` | 0 |
| `ci_max_artifact_size_junit` | 0 |
| `ci_max_artifact_size_license_management` | 0 |
| `ci_max_artifact_size_license_scanning` | 0 |
| `ci_max_artifact_size_load_performance` | 0 |
| `ci_max_artifact_size_lsif` | 100 MB ([Introduced at 20 MB](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37226) in GitLab 13.3 and [raised to 100 MB](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46980) in GitLab 13.6.) |
| `ci_max_artifact_size_metadata` | 0 |
| `ci_max_artifact_size_metrics_referee` | 0 |
| `ci_max_artifact_size_metrics` | 0 |
| `ci_max_artifact_size_network_referee` | 0 |
| `ci_max_artifact_size_performance` | 0 |
| `ci_max_artifact_size_requirements` | 0 |
| `ci_max_artifact_size_sast` | 0 |
| `ci_max_artifact_size_secret_detection` | 0 |
| `ci_max_artifact_size_terraform` | 5 MB ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37018) in GitLab 13.3) |
| `ci_max_artifact_size_trace` | 0 |
| `ci_max_artifact_size_accessibility` | 0 |
| `ci_max_artifact_size_api_fuzzing` | 0 |
| `ci_max_artifact_size_archive` | 0 |
| `ci_max_artifact_size_browser_performance` | 0 |
| `ci_max_artifact_size_cluster_applications` | 0 |
| `ci_max_artifact_size_cobertura` | 0 |
| `ci_max_artifact_size_codequality` | 0 |
| `ci_max_artifact_size_container_scanning` | 0 |
| `ci_max_artifact_size_coverage_fuzzing` | 0 |
| `ci_max_artifact_size_dast` | 0 |
| `ci_max_artifact_size_dependency_scanning` | 0 |
| `ci_max_artifact_size_dotenv` | 0 |
| `ci_max_artifact_size_junit` | 0 |
| `ci_max_artifact_size_license_management` | 0 |
| `ci_max_artifact_size_license_scanning` | 0 |
| `ci_max_artifact_size_load_performance` | 0 |
| `ci_max_artifact_size_lsif` | 100 MB ([Introduced at 20 MB](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37226) in GitLab 13.3 and [raised to 100 MB](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46980) in GitLab 13.6.) |
| `ci_max_artifact_size_metadata` | 0 |
| `ci_max_artifact_size_metrics_referee` | 0 |
| `ci_max_artifact_size_metrics` | 0 |
| `ci_max_artifact_size_network_referee` | 0 |
| `ci_max_artifact_size_performance` | 0 |
| `ci_max_artifact_size_requirements` | 0 |
| `ci_max_artifact_size_running_container_scanning` | 0 |
| `ci_max_artifact_size_sast` | 0 |
| `ci_max_artifact_size_secret_detection` | 0 |
| `ci_max_artifact_size_terraform` | 5 MB ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37018) in GitLab 13.3) |
| `ci_max_artifact_size_trace` | 0 |
For example, to set the `ci_max_artifact_size_junit` limit to 10 MB on a self-managed
installation, run the following in the [GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
@ -607,7 +608,7 @@ Reports that go over the 20 MB limit won't be loaded. Affected reports:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8638) in GitLab 13.3.
You can set a limit on the content of repository files that are indexed in
Elasticsearch. Any files larger than this limit is neither indexed
Elasticsearch. Any files larger than this limit is neither indexed
nor searchable.
Setting a limit helps reduce the memory usage of the indexing processes and

View File

@ -98,6 +98,8 @@ The following metrics are available:
| `gitlab_transaction_db_write_count_total` | Counter | 13.1 | Counter for total number of write SQL calls | `controller`, `action` |
| `gitlab_transaction_db_cached_count_total` | Counter | 13.1 | Counter for total number of cached SQL calls | `controller`, `action` |
| `gitlab_transaction_db_<role>_cached_count_total` | Counter | 13.1 | Counter for total number of cached SQL calls, grouped by database roles (primary/replica) | `controller`, `action` |
| `gitlab_transaction_db_<role>_wal_count_total` | Counter | 14.0 | Counter for total number of WAL (write ahead log location) queries, grouped by database roles (primary/replica) | `controller`, `action` |
| `gitlab_transaction_db_<role>_wal_cached_count_total` | Counter | 14.1 | Counter for total number of cached WAL (write ahead log location) queries, grouped by database roles (primary/replica)| `controller`, `action` |
| `http_elasticsearch_requests_duration_seconds` **(PREMIUM)** | Histogram | 13.1 | Elasticsearch requests duration during web transactions | `controller`, `action` |
| `http_elasticsearch_requests_total` **(PREMIUM)** | Counter | 13.1 | Elasticsearch requests count during web transactions | `controller`, `action` |
| `pipelines_created_total` | Counter | 9.4 | Counter of pipelines created | |

View File

@ -233,15 +233,7 @@ If your rules match both branch pipelines and merge request pipelines,
#### `workflow:rules:variables`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/294232) in GitLab 13.11.
> - [Deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/300997) in GitLab 13.12.
> - Enabled on GitLab.com.
> - Recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-workflowrulesvariables). **(FREE SELF)**
There can be
[risks when disabling released features](../../user/feature_flags.md#risks-when-disabling-released-features).
Refer to this feature's version history for more details.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/300997) in GitLab 14.1.
You can use [`variables`](#variables) in `workflow:rules:` to define variables for specific pipeline conditions.
@ -294,25 +286,6 @@ When the branch is something else:
- job1's `DEPLOY_VARIABLE` is `job1-default-deploy`.
- job2's `DEPLOY_VARIABLE` is `default-deploy`.
##### Enable or disable workflow:rules:variables **(FREE SELF)**
workflow:rules:variables is under development but ready for production use.
It is deployed behind a feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
can opt to disable it.
To enable it:
```ruby
Feature.enable(:ci_workflow_rules_variables)
```
To disable it:
```ruby
Feature.disable(:ci_workflow_rules_variables)
```
#### `workflow:rules` templates
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217732) in GitLab 13.0.

View File

@ -95,8 +95,6 @@ renaming. For example
class RenameUsersUpdatedAtToUpdatedAtTimestamp < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
@ -291,8 +289,6 @@ any remaining rows. For example:
class MigrateRemainingIssuesClosedAt < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
class Issue < ActiveRecord::Base

View File

@ -58,9 +58,9 @@ The **Time** metrics near the top of the page are measured as follows:
## How the stages are measured
Value Stream Analytics uses start events and stop events to measure the time that an issue or merge request spends in each stage.
Value Stream Analytics uses start events and end events to measure the time that an issue or merge request spends in each stage.
For example, a stage might start when one label is added to an issue and end when another label is added.
Items aren't included in the stage time calculation if they have not reached the stop event.
Items aren't included in the stage time calculation if they have not reached the end event.
| Stage | Description |
|---------|---------------|

View File

@ -96,9 +96,9 @@ You can learn more about these metrics in our [analytics definitions](../../anal
## How the stages are measured
Value Stream Analytics measures each stage from its start event to its stop event.
Value Stream Analytics measures each stage from its start event to its end event.
For example, a stage might start when one label is added to an issue, and end when another label is added.
Value Stream Analytics excludes work in progress, meaning it ignores any items that have not reached the stop event.
Value Stream Analytics excludes work in progress, meaning it ignores any items that have not reached the end event.
Each stage of Value Stream Analytics is further described in the table below.

View File

@ -10,10 +10,7 @@ module Gitlab
def perform!
raise ArgumentError, 'missing YAML processor result' unless @command.yaml_processor_result
if ::Feature.enabled?(:ci_workflow_rules_variables, pipeline.project, default_enabled: :yaml)
raise ArgumentError, 'missing workflow rules result' unless @command.workflow_rules_result
end
raise ArgumentError, 'missing workflow rules result' unless @command.workflow_rules_result
# Allocate next IID. This operation must be outside of transactions of pipeline creations.
pipeline.ensure_project_iid!
@ -51,13 +48,9 @@ module Gitlab
end
def root_variables
if ::Feature.enabled?(:ci_workflow_rules_variables, pipeline.project, default_enabled: :yaml)
::Gitlab::Ci::Variables::Helpers.merge_variables(
@command.yaml_processor_result.root_variables, @command.workflow_rules_result.variables
)
else
@command.yaml_processor_result.root_variables
end
::Gitlab::Ci::Variables::Helpers.merge_variables(
@command.yaml_processor_result.root_variables, @command.workflow_rules_result.variables
)
end
end
end

View File

@ -213,8 +213,6 @@ module Gitlab
end
def recalculate_yaml_variables!
return unless ::Feature.enabled?(:ci_workflow_rules_variables, @pipeline.project, default_enabled: :yaml)
@seed_attributes[:yaml_variables] = Gitlab::Ci::Variables::Helpers.inherit_yaml_variables(
from: @context.root_variables, to: @job_variables, inheritance: @root_variables_inheritance
)

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true
require 'redis'
module Gitlab
module Instrumentation
module RedisInterceptor
@ -99,7 +97,3 @@ module Gitlab
end
end
end
class ::Redis::Client
prepend ::Gitlab::Instrumentation::RedisInterceptor
end

View File

@ -15,8 +15,8 @@ module Gitlab
TRANSACTION_DURATION_BUCKET = [0.1, 0.25, 1].freeze
DB_LOAD_BALANCING_COUNTERS = %i{
db_replica_count db_replica_cached_count db_replica_wal_count
db_primary_count db_primary_cached_count db_primary_wal_count
db_replica_count db_replica_cached_count db_replica_wal_count db_replica_wal_cached_count
db_primary_count db_primary_cached_count db_primary_wal_count db_primary_wal_cached_count
}.freeze
DB_LOAD_BALANCING_DURATIONS = %i{db_primary_duration_s db_replica_duration_s}.freeze
@ -91,9 +91,14 @@ module Gitlab
end
def increment_db_role_counters(db_role, payload)
cached = cached_query?(payload)
increment("db_#{db_role}_count".to_sym)
increment("db_#{db_role}_cached_count".to_sym) if cached_query?(payload)
increment("db_#{db_role}_wal_count".to_sym) if !cached_query?(payload) && wal_command?(payload)
increment("db_#{db_role}_cached_count".to_sym) if cached
if wal_command?(payload)
increment("db_#{db_role}_wal_count".to_sym)
increment("db_#{db_role}_wal_cached_count".to_sym) if cached
end
end
def observe_db_role_duration(db_role, event)

View File

@ -5,7 +5,8 @@ import Vuex from 'vuex';
import * as commonUtils from '~/lib/utils/common_utils';
import MembersApp from '~/members/components/app.vue';
import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue';
import { MEMBER_TYPES } from '~/members/constants';
import MembersTable from '~/members/components/table/members_table.vue';
import { MEMBER_TYPES, TAB_QUERY_PARAM_VALUES } from '~/members/constants';
import { RECEIVE_MEMBER_ROLE_ERROR, HIDE_ERROR } from '~/members/store/mutation_types';
import mutations from '~/members/store/mutations';
@ -19,7 +20,7 @@ describe('MembersApp', () => {
const createComponent = (state = {}, options = {}) => {
store = new Vuex.Store({
modules: {
[MEMBER_TYPES.user]: {
[MEMBER_TYPES.group]: {
namespaced: true,
state: {
showError: true,
@ -34,7 +35,8 @@ describe('MembersApp', () => {
wrapper = shallowMount(MembersApp, {
localVue,
propsData: {
namespace: MEMBER_TYPES.user,
namespace: MEMBER_TYPES.group,
tabQueryParamValue: TAB_QUERY_PARAM_VALUES.group,
},
store,
...options,
@ -57,7 +59,7 @@ describe('MembersApp', () => {
it('renders and scrolls to error alert', async () => {
createComponent({ showError: false, errorMessage: '' });
store.commit(`${MEMBER_TYPES.user}/${RECEIVE_MEMBER_ROLE_ERROR}`, {
store.commit(`${MEMBER_TYPES.group}/${RECEIVE_MEMBER_ROLE_ERROR}`, {
error: new Error('Network Error'),
});
@ -77,7 +79,7 @@ describe('MembersApp', () => {
it('does not render and scroll to error alert', async () => {
createComponent();
store.commit(`${MEMBER_TYPES.user}/${HIDE_ERROR}`);
store.commit(`${MEMBER_TYPES.group}/${HIDE_ERROR}`);
await nextTick();
@ -103,4 +105,13 @@ describe('MembersApp', () => {
expect(findFilterSortContainer().exists()).toBe(true);
});
it('renders `MembersTable` component and passes `tabQueryParamValue` prop', () => {
createComponent();
const membersTableComponent = wrapper.findComponent(MembersTable);
expect(membersTableComponent.exists()).toBe(true);
expect(membersTableComponent.props('tabQueryParamValue')).toBe(TAB_QUERY_PARAM_VALUES.group);
});
});

View File

@ -216,5 +216,17 @@ describe('MembersFilteredSearchBar', () => {
'https://localhost/?two_factor=enabled&search=foobar&sort=name_asc',
);
});
it('adds active tab query param', () => {
window.location.search = '?tab=invited';
createComponent();
findFilteredSearchBar().vm.$emit('onFilter', [
{ type: 'filtered-search-term', value: { data: 'foobar' } },
]);
expect(window.location.href).toBe('https://localhost/?search=foobar&tab=invited');
});
});
});

View File

@ -1,9 +1,14 @@
import { GlTabs } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import MembersApp from '~/members/components/app.vue';
import MembersTabs from '~/members/components/members_tabs.vue';
import { MEMBER_TYPES } from '~/members/constants';
import {
MEMBER_TYPES,
TAB_QUERY_PARAM_VALUES,
ACTIVE_TAB_QUERY_PARAM_NAME,
} from '~/members/constants';
import { pagination } from '../mock_data';
describe('MembersTabs', () => {
@ -93,6 +98,18 @@ describe('MembersTabs', () => {
wrapper.destroy();
});
it('renders `GlTabs` with `syncActiveTabWithQueryParams` and `queryParamName` props set', async () => {
await createComponent();
const glTabsComponent = wrapper.findComponent(GlTabs);
expect(glTabsComponent.exists()).toBe(true);
expect(glTabsComponent.props()).toMatchObject({
syncActiveTabWithQueryParams: true,
queryParamName: ACTIVE_TAB_QUERY_PARAM_NAME,
});
});
describe('when tabs have a count', () => {
it('renders tabs with count', async () => {
await createComponent();
@ -106,7 +123,7 @@ describe('MembersTabs', () => {
expect(findActiveTab().text()).toContain('Members');
});
it('renders `MembersApp` and passes `namespace` prop', async () => {
it('renders `MembersApp` and passes `namespace` and `tabQueryParamValue` props', async () => {
await createComponent();
const membersApps = wrapper.findAllComponents(MembersApp).wrappers;
@ -115,6 +132,10 @@ describe('MembersTabs', () => {
expect(membersApps[1].props('namespace')).toBe(MEMBER_TYPES.group);
expect(membersApps[2].props('namespace')).toBe(MEMBER_TYPES.invite);
expect(membersApps[3].props('namespace')).toBe(MEMBER_TYPES.accessRequest);
expect(membersApps[1].props('tabQueryParamValue')).toBe(TAB_QUERY_PARAM_VALUES.group);
expect(membersApps[2].props('tabQueryParamValue')).toBe(TAB_QUERY_PARAM_VALUES.invite);
expect(membersApps[3].props('tabQueryParamValue')).toBe(TAB_QUERY_PARAM_VALUES.accessRequest);
});
});
@ -127,56 +148,16 @@ describe('MembersTabs', () => {
expect(findTabByText('Invited')).toBeUndefined();
expect(findTabByText('Access requests')).toBeUndefined();
});
});
describe('when url param matches `filteredSearchBar.searchParam`', () => {
beforeEach(() => {
window.location.search = '?search_groups=foo+bar';
});
const expectGroupsTabActive = () => {
expect(findActiveTab().text()).toContain('Groups');
};
describe('when tab has a count', () => {
it('sets tab that corresponds to search param as active tab', async () => {
await createComponent();
expectGroupsTabActive();
describe('when url param matches `filteredSearchBar.searchParam`', () => {
beforeEach(() => {
window.location.search = '?search_groups=foo+bar';
});
});
describe('when tab does not have a count', () => {
it('sets tab that corresponds to search param as active tab', async () => {
it('shows tab that corresponds to search param', async () => {
await createComponent({ totalItems: 0 });
expectGroupsTabActive();
});
});
});
describe('when url param matches `pagination.paramName`', () => {
beforeEach(() => {
window.location.search = '?invited_page=2';
});
const expectInvitedTabActive = () => {
expect(findActiveTab().text()).toContain('Invited');
};
describe('when tab has a count', () => {
it('sets tab that corresponds to pagination param as active tab', async () => {
await createComponent();
expectInvitedTabActive();
});
});
describe('when tab does not have a count', () => {
it('sets tab that corresponds to pagination param as active tab', async () => {
await createComponent({ totalItems: 0 });
expectInvitedTabActive();
expect(findTabByText('Groups')).not.toBeUndefined();
});
});
});

View File

@ -15,7 +15,7 @@ import MemberAvatar from '~/members/components/table/member_avatar.vue';
import MemberSource from '~/members/components/table/member_source.vue';
import MembersTable from '~/members/components/table/members_table.vue';
import RoleDropdown from '~/members/components/table/role_dropdown.vue';
import { MEMBER_TYPES } from '~/members/constants';
import { MEMBER_TYPES, TAB_QUERY_PARAM_VALUES } from '~/members/constants';
import * as initUserPopovers from '~/user_popovers';
import {
member as memberMock,
@ -34,7 +34,7 @@ describe('MembersTable', () => {
const createStore = (state = {}) => {
return new Vuex.Store({
modules: {
[MEMBER_TYPES.user]: {
[MEMBER_TYPES.invite]: {
namespaced: true,
state: {
members: [],
@ -54,11 +54,14 @@ describe('MembersTable', () => {
const createComponent = (state, provide = {}) => {
wrapper = mount(MembersTable, {
localVue,
propsData: {
tabQueryParamValue: TAB_QUERY_PARAM_VALUES.invite,
},
store: createStore(state),
provide: {
sourceId: 1,
currentUserId: 1,
namespace: MEMBER_TYPES.user,
namespace: MEMBER_TYPES.invite,
...provide,
},
stubs: [
@ -74,7 +77,7 @@ describe('MembersTable', () => {
});
};
const url = 'https://localhost/foo-bar/-/project_members';
const url = 'https://localhost/foo-bar/-/project_members?tab=invited';
const getByText = (text, options) =>
createWrapper(getByTextHelper(wrapper.element, text, options));
@ -92,7 +95,7 @@ describe('MembersTable', () => {
const expectCorrectLinkToPage2 = () => {
expect(findPagination().findByText('2', { selector: 'a' }).attributes('href')).toBe(
`${url}?page=2`,
`${url}&invited_members_page=2`,
);
};
@ -271,7 +274,7 @@ describe('MembersTable', () => {
currentPage: 1,
perPage: 5,
totalItems: 10,
paramName: 'page',
paramName: 'invited_members_page',
},
});
@ -279,14 +282,14 @@ describe('MembersTable', () => {
});
it('removes any url params defined as `null` in the `params` attribute', () => {
window.location = new URL(`${url}?search_groups=foo`);
window.location = new URL(`${url}&search_groups=foo`);
createComponent({
pagination: {
currentPage: 1,
perPage: 5,
totalItems: 10,
paramName: 'page',
paramName: 'invited_members_page',
params: { search_groups: null },
},
});

View File

@ -153,7 +153,16 @@ describe('LabelsSelect Mutations', () => {
});
describe(`${types.UPDATE_SELECTED_LABELS}`, () => {
const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
let labels;
beforeEach(() => {
labels = [
{ id: 1, title: 'scoped::test', set: true },
{ id: 2, set: false, title: 'scoped::one' },
{ id: 3, title: '' },
{ id: 4, title: '' },
];
});
it('updates `state.labels` to include `touched` and `set` props based on provided `labels` param', () => {
const updatedLabelIds = [2];
@ -169,5 +178,23 @@ describe('LabelsSelect Mutations', () => {
}
});
});
describe('when label is scoped', () => {
it('unsets the currently selected scoped label and sets the current label', () => {
const state = {
labels,
};
mutations[types.UPDATE_SELECTED_LABELS](state, {
labels: [{ id: 2, title: 'scoped::one' }],
});
expect(state.labels).toEqual([
{ id: 1, title: 'scoped::test', set: false },
{ id: 2, set: true, title: 'scoped::one', touched: true },
{ id: 3, title: '' },
{ id: 4, title: '' },
]);
});
});
});
});

View File

@ -120,7 +120,16 @@ describe('LabelsSelect Mutations', () => {
});
describe(`${types.UPDATE_SELECTED_LABELS}`, () => {
const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
let labels;
beforeEach(() => {
labels = [
{ id: 1, title: 'scoped::test', set: true },
{ id: 2, set: false, title: 'scoped::one' },
{ id: 3, title: '' },
{ id: 4, title: '' },
];
});
it('updates `state.labels` to include `touched` and `set` props based on provided `labels` param', () => {
const updatedLabelIds = [2];
@ -136,5 +145,23 @@ describe('LabelsSelect Mutations', () => {
}
});
});
describe('when label is scoped', () => {
it('unsets the currently selected scoped label and sets the current label', () => {
const state = {
labels,
};
mutations[types.UPDATE_SELECTED_LABELS](state, {
labels: [{ id: 2, title: 'scoped::one' }],
});
expect(state.labels).toEqual([
{ id: 1, title: 'scoped::test', set: false },
{ id: 2, set: true, title: 'scoped::one', touched: true },
{ id: 3, title: '' },
{ id: 4, title: '' },
]);
});
});
});
});

View File

@ -200,6 +200,8 @@ RSpec.describe 'lograge', type: :request do
%w[
db_primary_wal_count
db_replica_wal_count
db_primary_wal_cached_count
db_replica_wal_cached_count
db_replica_count
db_replica_cached_count
db_primary_count

View File

@ -203,18 +203,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
expect(rspec_variables['VAR1']).to eq('overridden var 1')
end
context 'when the FF ci_workflow_rules_variables is disabled' do
before do
stub_feature_flags(ci_workflow_rules_variables: false)
end
it 'sends root variable' do
run_chain
expect(rspec_variables['VAR1']).to eq('var 1')
end
end
end
context 'N+1 queries' do

View File

@ -250,19 +250,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
{ key: 'VAR4', value: 'new var pipeline 4', public: true }]
)
end
context 'when FF ci_workflow_rules_variables is disabled' do
before do
stub_feature_flags(ci_workflow_rules_variables: false)
end
it 'returns existing yaml variables' do
expect(subject[:yaml_variables]).to match_array(
[{ key: 'VAR2', value: 'var 2', public: true },
{ key: 'VAR3', value: 'var 3', public: true }]
)
end
end
end
context 'when root_variables_inheritance is false' do

View File

@ -133,7 +133,9 @@ RSpec.describe Gitlab::InstrumentationHelper do
db_primary_count: 0,
db_primary_cached_count: 0,
db_primary_wal_count: 0,
db_replica_wal_count: 0)
db_replica_wal_count: 0,
db_primary_wal_cached_count: 0,
db_replica_wal_cached_count: 0)
end
end
@ -150,7 +152,9 @@ RSpec.describe Gitlab::InstrumentationHelper do
db_primary_count: 0,
db_primary_cached_count: 0,
db_primary_wal_count: 0,
db_replica_wal_count: 0)
db_replica_wal_count: 0,
db_primary_wal_cached_count: 0,
db_replica_wal_cached_count: 0)
end
end
end

View File

@ -183,6 +183,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
'SQL' | 'UPDATE users SET admin = true WHERE id = 10' | true | true | false | false
'SQL' | 'SELECT pg_current_wal_insert_lsn()::text AS location' | true | false | false | true
'SQL' | 'SELECT pg_last_wal_replay_lsn()::text AS location' | true | false | false | true
'CACHE' | 'SELECT pg_current_wal_insert_lsn()::text AS location' | true | false | true | true
'CACHE' | 'SELECT pg_last_wal_replay_lsn()::text AS location' | true | false | true | true
'CACHE' | 'SELECT * FROM users WHERE id = 10' | true | false | true | false
'SCHEMA' | "SELECT attr.attname FROM pg_attribute attr INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey) WHERE cons.contype = 'p' AND cons.conrelid = '\"projects\"'::regclass" | false | false | false | false
nil | 'BEGIN' | false | false | false | false

View File

@ -314,7 +314,9 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
'db_primary_cached_count' => 0,
'db_primary_wal_count' => 0,
'db_primary_duration_s' => a_value > 0,
"db_primary_#{dbname}_duration_s" => a_value > 0
"db_primary_#{dbname}_duration_s" => a_value > 0,
'db_primary_wal_cached_count' => 0,
'db_replica_wal_cached_count' => 0
)
end
@ -336,6 +338,8 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
'db_primary_count' => 0,
'db_primary_cached_count' => 0,
'db_primary_wal_count' => 0,
'db_primary_wal_cached_count' => 0,
'db_replica_wal_cached_count' => 0,
'db_primary_duration_s' => 0
)
end

View File

@ -184,6 +184,7 @@ RSpec.describe PlanLimits do
ci_max_artifact_size_junit
ci_max_artifact_size_sast
ci_max_artifact_size_dast
ci_max_artifact_size_running_container_scanning
ci_max_artifact_size_codequality
ci_max_artifact_size_license_management
ci_max_artifact_size_performance

View File

@ -230,22 +230,6 @@ RSpec.describe Ci::CreatePipelineService do
[nil, nil, nil, 'job var 4', nil, nil, 'overridden var 7']
)
end
context 'when FF ci_workflow_rules_variables is disabled' do
before do
stub_feature_flags(ci_workflow_rules_variables: false)
end
it 'does not affect workflow variables but job variables' do
expect(job1.scoped_variables.to_hash.values_at(*variable_keys)).to eq(
['overridden var 1', 'job var 2', nil, 'workflow var 4', 'job var 5', nil, 'workflow var 7']
)
expect(job2.scoped_variables.to_hash.values_at(*variable_keys)).to eq(
[nil, nil, nil, 'job var 4', nil, nil, 'overridden var 7']
)
end
end
end
context 'when matching to the second rule' do

View File

@ -19,6 +19,8 @@ RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
db_replica_count: 0,
db_replica_duration_s: 0.0,
db_primary_wal_count: record_wal_query ? 1 : 0,
db_primary_wal_cached_count: record_wal_query && record_cached_query ? 1 : 0,
db_replica_wal_cached_count: 0,
db_replica_wal_count: 0
}
expected[:"db_primary_#{::Gitlab::Database.dbname(connection)}_duration_s"] = 0.002 if record_query
@ -34,6 +36,8 @@ RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role|
db_replica_count: record_query ? 1 : 0,
db_replica_duration_s: record_query ? 0.002 : 0,
db_replica_wal_count: record_wal_query ? 1 : 0,
db_replica_wal_cached_count: record_wal_query && record_cached_query ? 1 : 0,
db_primary_wal_cached_count: 0,
db_primary_wal_count: 0
}
expected[:"db_replica_#{::Gitlab::Database.dbname(connection)}_duration_s"] = 0.002 if record_query
@ -91,7 +95,10 @@ RSpec.shared_examples 'record ActiveRecord metrics in a metrics transaction' do
end
if record_wal_query
expect(transaction).to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1) if db_role
if db_role
expect(transaction).to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1)
expect(transaction).to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_cached_count_total".to_sym, 1) if record_cached_query
end
else
expect(transaction).not_to receive(:increment).with("gitlab_transaction_db_#{db_role}_wal_count_total".to_sym, 1) if db_role
end