Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-07-13 15:07:15 +00:00
parent da104bbfec
commit f62886ebff
62 changed files with 1135 additions and 578 deletions

View File

@ -213,296 +213,165 @@
- *node-modules-cache # We don't push this cache as it's already rebuilt by `update-assets-compile-*-cache`
- *storybook-node-modules-cache-push
.use-pg12:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-12-pgvector-0.4.1
.pg-base-variables:
variables:
POSTGRES_HOST_AUTH_METHOD: trust
.db-services:
services: &db-services
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-${PG_VERSION}-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- name: redis:6.2-alpine
.use-pg12:
extends:
- .pg-base-variables
- .db-services
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "12"
.use-pg13:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-13-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.2-alpine
extends:
- .pg-base-variables
- .db-services
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "13"
.use-pg14:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-14-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.2-alpine
extends:
- .pg-base-variables
- .db-services
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "14"
.use-pg15:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-15-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.2-alpine
extends:
- .pg-base-variables
- .db-services
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "15"
.zoekt-variables:
variables:
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.zoekt-services:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
.es7-base:
extends:
- .pg-base-variables
- .zoekt-variables
services:
- !reference [.db-services, services]
- !reference [.zoekt-services, services]
- name: elasticsearch:7.17.6
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
.use-pg12-es7-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-12-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- name: elasticsearch:7.17.6
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
extends: .es7-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "12"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-pg13-es7-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-13-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.2-alpine
- name: elasticsearch:7.17.6
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
extends: .es7-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "13"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-pg14-es7-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-14-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.2-alpine
- name: elasticsearch:7.17.6
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
extends: .es7-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "14"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-pg15-es7-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-15-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.2-alpine
- name: elasticsearch:7.17.6
command: ["elasticsearch", "-E", "discovery.type=single-node", "-E", "xpack.security.enabled=false"]
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
extends: .es7-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "15"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.es8-base:
extends:
- .pg-base-variables
- .zoekt-variables
services:
- !reference [.db-services, services]
- !reference [.zoekt-services, services]
- name: elasticsearch:8.6.2
variables:
ES_SETTING_DISCOVERY_TYPE: "single-node"
ES_SETTING_XPACK_SECURITY_ENABLED: "false"
.use-pg13-es8-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-13-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- name: elasticsearch:8.6.2
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
extends: .es8-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "13"
ES_SETTING_DISCOVERY_TYPE: "single-node"
ES_SETTING_XPACK_SECURITY_ENABLED: "false"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-pg14-es8-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-14-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- name: elasticsearch:8.6.2
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
extends: .es8-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "14"
ES_SETTING_DISCOVERY_TYPE: "single-node"
ES_SETTING_XPACK_SECURITY_ENABLED: "false"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-pg15-es8-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-15-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- name: elasticsearch:8.6.2
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
extends: .es8-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "15"
ES_SETTING_DISCOVERY_TYPE: "single-node"
ES_SETTING_XPACK_SECURITY_ENABLED: "false"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.os1-base:
extends:
- .pg-base-variables
- .zoekt-variables
services:
- !reference [.db-services, services]
- !reference [.zoekt-services, services]
- name: opensearchproject/opensearch:1.3.5
alias: elasticsearch
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
.use-pg13-opensearch1-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-13-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- name: opensearchproject/opensearch:1.3.5
alias: elasticsearch
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
extends: .os1-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "13"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-pg13-opensearch2-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-13-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- name: opensearchproject/opensearch:2.2.1
alias: elasticsearch
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "13"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-pg14-opensearch1-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-14-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- name: opensearchproject/opensearch:1.3.5
alias: elasticsearch
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
extends: .os1-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "14"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-pg14-opensearch2-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-14-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- name: opensearchproject/opensearch:2.2.1
alias: elasticsearch
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "14"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-pg15-opensearch1-ee:
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-15-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- name: opensearchproject/opensearch:1.3.5
alias: elasticsearch
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
extends: .os1-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "15"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-pg15-opensearch2-ee:
.os2-base:
extends:
- .pg-base-variables
- .zoekt-variables
services:
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:postgres-15-pgvector-0.4.1
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off", "-c", "max_locks_per_transaction=128"]
alias: postgres
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:redis-cluster-6.2.12
alias: rediscluster # configure connections in config/redis.yml
- name: redis:6.0-alpine
- !reference [.db-services, services]
- !reference [.zoekt-services, services]
- name: opensearchproject/opensearch:2.2.1
alias: elasticsearch
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true"]
- name: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:zoekt-ci-image-1.1
alias: zoekt-ci-image
.use-pg13-opensearch2-ee:
extends: .os2-base
variables:
PG_VERSION: "13"
.use-pg14-opensearch2-ee:
extends: .os2-base
variables:
PG_VERSION: "14"
.use-pg15-opensearch2-ee:
extends: .os2-base
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "15"
ZOEKT_INDEX_BASE_URL: http://zoekt-ci-image:6060
ZOEKT_SEARCH_BASE_URL: http://zoekt-ci-image:6070
.use-kaniko:
image:

View File

@ -1,16 +1,20 @@
<script>
import { GlDropdown, GlDropdownItem, GlDropdownDivider } from '@gitlab/ui';
import { GlButtonGroup, GlButton, GlCollapsibleListbox } from '@gitlab/ui';
import { sprintf } from '~/locale';
import { sprintf, __ } from '~/locale';
import { COMMENT_FORM } from '~/notes/i18n';
import * as constants from '../constants';
export default {
i18n: COMMENT_FORM,
name: 'CommentTypeDropdown',
i18n: {
...COMMENT_FORM,
toggleSrText: __('Comment type'),
},
components: {
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
GlButtonGroup,
GlButton,
GlCollapsibleListbox,
},
model: {
prop: 'noteType',
@ -93,56 +97,63 @@ export default {
noteableDisplayName: this.noteableDisplayName,
});
},
dropdownItems() {
return [
{
text: this.dropdownCommentButtonTitle,
description: this.commentDescription,
value: constants.COMMENT,
},
{
text: this.dropdownStartThreadButtonTitle,
description: this.startDiscussionDescription,
value: constants.DISCUSSION,
qaSelector: 'discussion_menu_item',
},
];
},
},
methods: {
handleClick() {
this.$emit('click');
},
setNoteTypeToComment() {
if (this.noteType !== constants.COMMENT) {
this.$emit('change', constants.COMMENT);
}
},
setNoteTypeToDiscussion() {
if (this.noteType !== constants.DISCUSSION) {
this.$emit('change', constants.DISCUSSION);
}
setNoteType(value) {
this.$emit('change', value);
},
},
};
</script>
<template>
<gl-dropdown
split
:text="commentButtonTitle"
class="gl-mr-3 js-comment-button js-comment-submit-button comment-type-dropdown"
category="primary"
variant="confirm"
:disabled="disabled"
data-testid="comment-button"
data-qa-selector="comment_button"
<!--TODO: Replace button-group workaround once `split` option for new dropdowns is implemented.-->
<!-- See issue at https://gitlab.com/gitlab-org/gitlab-ui/-/issues/2263-->
<gl-button-group
class="js-comment-button js-comment-submit-button comment-type-dropdown gl-w-full gl-mb-3 gl-md-w-auto gl-md-mb-0"
:data-track-label="trackingLabel"
data-track-action="click_button"
@click="$emit('click')"
data-testid="comment-button"
data-qa-selector="comment_button"
>
<gl-dropdown-item
is-check-item
:is-checked="isNoteTypeComment"
@click.stop.prevent="setNoteTypeToComment"
<gl-button variant="confirm" :disabled="disabled" @click="handleClick">
{{ commentButtonTitle }}
</gl-button>
<gl-collapsible-listbox
class="split"
toggle-class="gl-rounded-top-left-none! gl-rounded-bottom-left-none! gl-pl-1!"
variant="confirm"
text-sr-only
:toggle-text="$options.i18n.toggleSrText"
:disabled="disabled"
:items="dropdownItems"
:selected="noteType"
@select="setNoteType"
>
<strong>{{ dropdownCommentButtonTitle }}</strong>
<p class="gl-m-0">{{ commentDescription }}</p>
</gl-dropdown-item>
<gl-dropdown-divider />
<gl-dropdown-item
is-check-item
:is-checked="isNoteTypeDiscussion"
data-qa-selector="discussion_menu_item"
@click.stop.prevent="setNoteTypeToDiscussion"
>
<strong>{{ dropdownStartThreadButtonTitle }}</strong>
<p class="gl-m-0">{{ startDiscussionDescription }}</p>
</gl-dropdown-item>
</gl-dropdown>
<template #list-item="{ item }">
<div :data-qa-selector="item.qaSelector">
<strong>{{ item.text }}</strong>
<p class="gl-m-0">{{ item.description }}</p>
</div>
</template>
</gl-collapsible-listbox>
</gl-button-group>
</template>

View File

@ -0,0 +1,41 @@
// import axios from '~/lib/utils/axios_utils';
import * as mockData from './mock_traces.json';
function enableTraces(provisioningUrl) {
console.log(`Enabling tracing - ${provisioningUrl}`); // eslint-disable-line no-console
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 500);
});
}
function isTracingEnabled(provisioningUrl) {
console.log(`Checking status - ${provisioningUrl}`); // eslint-disable-line no-console
return new Promise((resolve) => {
setTimeout(() => {
resolve(false);
}, 1000);
});
}
function fetchTraces(tracingUrl) {
console.log(`Fetching traces from ${tracingUrl}`); // eslint-disable-line no-console
// axios.get(`${this.endpoint}/v1/jaeger/22/api/services`, { credentials: 'include' });
return new Promise((resolve) => {
setTimeout(() => {
resolve(mockData.data);
}, 2000);
});
}
export function buildClient({ provisioningUrl, tracingUrl }) {
return {
enableTraces: () => enableTraces(provisioningUrl),
isTracingEnabled: () => isTracingEnabled(provisioningUrl),
fetchTraces: () => fetchTraces(tracingUrl),
};
}

View File

@ -0,0 +1,86 @@
<script>
import { buildClient } from '../client';
import ObservabilitySkeleton from './skeleton/index.vue';
export default {
components: {
ObservabilitySkeleton,
},
props: {
oauthUrl: {
type: String,
required: true,
},
tracingUrl: {
type: String,
required: true,
},
provisioningUrl: {
type: String,
required: true,
},
},
data() {
return {
observabilityClient: null,
authCompleted: false,
};
},
mounted() {
window.addEventListener('message', this.messageHandler);
// TODO Remove once backend work done - just for testing
// setTimeout(() => {
// this.messageHandler({
// data: { type: 'AUTH_COMPLETION', status: 'success' },
// origin: new URL(this.oauthUrl).origin,
// });
// }, 2000);
},
destroyed() {
window.removeEventListener('message', this.messageHandler);
},
methods: {
messageHandler(e) {
const isExpectedOrigin = e.origin === new URL(this.oauthUrl).origin;
if (!isExpectedOrigin) return;
const { data } = e;
if (data.type === 'AUTH_COMPLETION') {
if (this.authCompleted) return;
const { status, message, statusCode } = data;
if (status === 'success') {
this.observabilityClient = buildClient({
provisioningUrl: this.provisioningUrl,
tracingUrl: this.tracingUrl,
});
this.$refs.observabilitySkeleton?.onContentLoaded();
} else if (status === 'error') {
// eslint-disable-next-line @gitlab/require-i18n-strings,no-console
console.error('GOB auth failed with error:', message, statusCode);
this.$refs.observabilitySkeleton?.onError();
}
this.authCompleted = true;
}
},
},
};
</script>
<template>
<div>
<iframe
v-if="!authCompleted"
sandbox="allow-same-origin allow-forms allow-scripts"
hidden
:src="oauthUrl"
data-testid="observability-oauth-iframe"
></iframe>
<observability-skeleton ref="observabilitySkeleton">
<slot v-if="observabilityClient" :observability-client="observabilityClient"></slot>
</observability-skeleton>
</div>
</template>

View File

@ -61,6 +61,12 @@ export default {
this.hideSkeleton();
},
onError() {
clearTimeout(this.errorTimeout);
clearTimeout(this.loadingTimeout);
this.showError();
},
setLoadingTimeout() {
this.loadingTimeout = setTimeout(() => {
/**
@ -130,7 +136,7 @@ export default {
<transition>
<div
v-show="state === $options.SKELETON_STATE.HIDDEN"
data-testid="observability-wrapper"
data-testid="content-wrapper"
class="gl-flex-grow-1 gl-display-flex gl-flex-direction-column gl-flex-align-items-stretch"
>
<slot></slot>

View File

@ -0,0 +1,147 @@
{
"data": [
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 100,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
},
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 343,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
},
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 343,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
},
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 343,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
},
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 343,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
},
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 343,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
},
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 343,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
},
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 343,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
},
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 343,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
},
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 343,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
},
{
"traceID": "668ec7d464968a87",
"date": "Mon, 03 Jul 2023 14:35:37 GMT",
"service": "HealthCheck",
"operation": "/grpc.health.v1.Health/Check",
"duration": 343,
"method": "GET",
"status": 200,
"spans": [
],
"warnings": null
}
]
}

View File

@ -0,0 +1,4 @@
import { initSimpleApp } from '~/helpers/init_simple_app_helper';
import ListIndex from '~/tracing/list_index.vue';
initSimpleApp('#js-tracing', ListIndex);

View File

@ -0,0 +1,14 @@
<script>
export default {
props: {
observabilityClient: {
required: true,
type: Object,
},
},
};
</script>
<template>
<div></div>
</template>

View File

@ -0,0 +1,37 @@
<script>
import ObservabilityContainer from '~/observability/components/observability_container.vue';
import TracingList from './components/tracing_list.vue';
export default {
components: {
ObservabilityContainer,
TracingList,
},
props: {
oauthUrl: {
type: String,
required: true,
},
tracingUrl: {
type: String,
required: true,
},
provisioningUrl: {
type: String,
required: true,
},
},
};
</script>
<template>
<observability-container
:oauth-url="oauthUrl"
:tracing-url="tracingUrl"
:provisioning-url="provisioningUrl"
>
<template #default="{ observabilityClient }">
<tracing-list :observability-client="observabilityClient" />
</template>
</observability-container>
</template>

View File

@ -549,3 +549,16 @@ li.note {
See https://gitlab.com/gitlab-org/gitlab/issues/36857 for more details.
**/
.gl-line-height-14 { line-height: $gl-line-height-14; }
// TODO: To be removed once `split` option for new dropdowns is implemented.
// See issue at https://gitlab.com/gitlab-org/gitlab-ui/-/issues/2263
.gl-new-dropdown.split:nth-child(n+2) {
.gl-new-dropdown-toggle {
margin-left: 1px;
&.btn-tertiary,
&.disabled {
margin-left: -1px;
}
}
}

View File

@ -8,10 +8,7 @@ module Projects
before_action :check_tracing_enabled
def index
# TODO frontend changes coming separately https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125014
render html: helpers.tag.strong('Tracing')
end
def index; end
private

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
module Projects
module ObservabilityHelper
def observability_tracing_view_model(project)
Gitlab::Json.generate({
tracingUrl: Gitlab::Observability.tracing_url(project),
provisioningUrl: Gitlab::Observability.provisioning_url(project),
oauthUrl: Gitlab::Observability.oauth_url
})
end
end
end

View File

@ -24,7 +24,7 @@ module ProtectedRefDeployKeyAccess
end
def humanize
return "Deploy key" if deploy_key.present?
return deploy_key.title if deploy_key?
super
end

View File

@ -0,0 +1,4 @@
- page_title _('Tracing')
#js-tracing{ data: { view_model: observability_tracing_view_model(@project) } }

View File

@ -4,6 +4,11 @@ gitlab_schemas:
- gitlab_internal
- gitlab_shared
- gitlab_ci
lock_gitlab_schemas:
- gitlab_main
- gitlab_main_clusterwide
- gitlab_main_cell
- gitlab_pm
klass: Ci::ApplicationRecord
# if CI database is not configured, use this database
fallback_database: main

View File

@ -6,6 +6,8 @@ gitlab_schemas:
- gitlab_main
- gitlab_main_cell
- gitlab_pm
lock_gitlab_schemas:
- gitlab_ci
# Note that we use ActiveRecord::Base here and not ApplicationRecord.
# This is deliberate, as:
# - the load balancer must be enabled for _all_ models

View File

@ -7,4 +7,6 @@ feature_categories:
description: Stores information about vulnerable SBoM components
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95622
milestone: '15.4'
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125426
removed_in_milestone: '16.2'
gitlab_schema: gitlab_main

View File

@ -8,4 +8,6 @@ feature_categories:
description: Stores vulnerability advisories
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95622
milestone: '15.4'
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125426
removed_in_milestone: '16.2'
gitlab_schema: gitlab_main

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class RollbackVulnerabilityAdvisoriesForeignKeyOnVulnerableComponentVersions < Gitlab::Database::Migration[2.1]
SOURCE_TABLE = :sbom_vulnerable_component_versions
TARGET_TABLE = :vulnerability_advisories
COLUMN = :vulnerability_advisory_id
disable_ddl_transaction!
def up
# Foreign key is removed when the table is dropped in the next migration.
end
def down
add_concurrent_foreign_key SOURCE_TABLE, TARGET_TABLE, column: COLUMN, on_delete: :cascade
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class RollbackComponentVersionForeignKeyOnVulnerableComponentVersions < Gitlab::Database::Migration[2.1]
SOURCE_TABLE = :sbom_vulnerable_component_versions
TARGET_TABLE = :sbom_component_versions
COLUMN = :sbom_component_version_id
disable_ddl_transaction!
def up
# Foreign key is removed when the table is dropped in the next migration.
end
def down
add_concurrent_foreign_key SOURCE_TABLE, TARGET_TABLE, column: COLUMN, on_delete: :cascade
end
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class DropVulnerableComponentVersions < Gitlab::Database::Migration[2.1]
ADVISORY_INDEX_NAME = "index_vulnerable_component_versions_on_vulnerability_advisory"
SBOM_COMPONENT_INDEX_NAME = "index_vulnerable_component_versions_on_sbom_component_version"
def up
drop_table :sbom_vulnerable_component_versions
end
def down
create_table :sbom_vulnerable_component_versions do |t|
t.references :vulnerability_advisory,
index: { name: ADVISORY_INDEX_NAME }
t.references :sbom_component_version,
index: { name: SBOM_COMPONENT_INDEX_NAME }
t.timestamps_with_timezone null: false
end
end
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
class DropVulnerabilitiesAdvisories < Gitlab::Database::Migration[2.1]
def up
drop_table :vulnerability_advisories
end
def down
create_table :vulnerability_advisories, id: false do |t|
t.uuid :uuid, null: false
t.timestamps_with_timezone null: false
t.primary_key :id
t.date :created_date, null: false
t.date :published_date, null: false
t.text :description, limit: 2048
t.text :title, limit: 2048
t.text :component_name, limit: 2048
t.text :solution, limit: 2048
t.text :not_impacted, limit: 2048
t.text :cvss_v2, limit: 128
t.text :cvss_v3, limit: 128
t.text :affected_range, limit: 32
t.text :identifiers, array: true, default: []
t.text :fixed_versions, array: true, default: []
t.text :urls, array: true, default: []
t.text :links, array: true, default: []
end
end
end

View File

@ -0,0 +1 @@
dafb3395a28180da275eceddb87af4deb0008b2d0793dd0ea3f34d2ae8bd5c10

View File

@ -0,0 +1 @@
2cea22d62a5a08a643b3043bea1e14e4965f57201db559995cab8616d7586f55

View File

@ -0,0 +1 @@
ae094cd61e252b30c1ebe0e5369ff2c061aa96079bbc1addde160003e2263886

View File

@ -0,0 +1 @@
33de9f678eb493070ceaae0e50461cffbcdbb5a542740b9fc595cba2c8c32808

View File

@ -22361,23 +22361,6 @@ CREATE SEQUENCE sbom_sources_id_seq
ALTER SEQUENCE sbom_sources_id_seq OWNED BY sbom_sources.id;
CREATE TABLE sbom_vulnerable_component_versions (
id bigint NOT NULL,
vulnerability_advisory_id bigint,
sbom_component_version_id bigint,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL
);
CREATE SEQUENCE sbom_vulnerable_component_versions_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE sbom_vulnerable_component_versions_id_seq OWNED BY sbom_vulnerable_component_versions.id;
CREATE TABLE scan_result_policies (
id bigint NOT NULL,
security_orchestration_policy_configuration_id bigint NOT NULL,
@ -24116,44 +24099,6 @@ CREATE SEQUENCE vulnerabilities_id_seq
ALTER SEQUENCE vulnerabilities_id_seq OWNED BY vulnerabilities.id;
CREATE TABLE vulnerability_advisories (
uuid uuid NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
id bigint NOT NULL,
created_date date NOT NULL,
published_date date NOT NULL,
description text,
title text,
component_name text,
solution text,
not_impacted text,
cvss_v2 text,
cvss_v3 text,
affected_range text,
identifiers text[] DEFAULT '{}'::text[],
fixed_versions text[] DEFAULT '{}'::text[],
urls text[] DEFAULT '{}'::text[],
links text[] DEFAULT '{}'::text[],
CONSTRAINT check_3ab0544d19 CHECK ((char_length(title) <= 2048)),
CONSTRAINT check_3b57023409 CHECK ((char_length(affected_range) <= 32)),
CONSTRAINT check_4d5cd7be9c CHECK ((char_length(component_name) <= 2048)),
CONSTRAINT check_962f256a51 CHECK ((char_length(solution) <= 2048)),
CONSTRAINT check_aae93955fb CHECK ((char_length(cvss_v3) <= 128)),
CONSTRAINT check_b8a17497f3 CHECK ((char_length(cvss_v2) <= 128)),
CONSTRAINT check_c05a35f418 CHECK ((char_length(not_impacted) <= 2048)),
CONSTRAINT check_ff9f6483b6 CHECK ((char_length(description) <= 2048))
);
CREATE SEQUENCE vulnerability_advisories_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE vulnerability_advisories_id_seq OWNED BY vulnerability_advisories.id;
CREATE TABLE vulnerability_exports (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@ -25935,8 +25880,6 @@ ALTER TABLE ONLY sbom_occurrences ALTER COLUMN id SET DEFAULT nextval('sbom_occu
ALTER TABLE ONLY sbom_sources ALTER COLUMN id SET DEFAULT nextval('sbom_sources_id_seq'::regclass);
ALTER TABLE ONLY sbom_vulnerable_component_versions ALTER COLUMN id SET DEFAULT nextval('sbom_vulnerable_component_versions_id_seq'::regclass);
ALTER TABLE ONLY scan_result_policies ALTER COLUMN id SET DEFAULT nextval('scan_result_policies_id_seq'::regclass);
ALTER TABLE ONLY schema_inconsistencies ALTER COLUMN id SET DEFAULT nextval('schema_inconsistencies_id_seq'::regclass);
@ -26083,8 +26026,6 @@ ALTER TABLE ONLY value_stream_dashboard_counts ALTER COLUMN id SET DEFAULT nextv
ALTER TABLE ONLY vulnerabilities ALTER COLUMN id SET DEFAULT nextval('vulnerabilities_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_advisories ALTER COLUMN id SET DEFAULT nextval('vulnerability_advisories_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_exports ALTER COLUMN id SET DEFAULT nextval('vulnerability_exports_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_external_issue_links ALTER COLUMN id SET DEFAULT nextval('vulnerability_external_issue_links_id_seq'::regclass);
@ -28383,9 +28324,6 @@ ALTER TABLE ONLY sbom_occurrences
ALTER TABLE ONLY sbom_sources
ADD CONSTRAINT sbom_sources_pkey PRIMARY KEY (id);
ALTER TABLE ONLY sbom_vulnerable_component_versions
ADD CONSTRAINT sbom_vulnerable_component_versions_pkey PRIMARY KEY (id);
ALTER TABLE ONLY scan_result_policies
ADD CONSTRAINT scan_result_policies_pkey PRIMARY KEY (id);
@ -28650,9 +28588,6 @@ ALTER TABLE ONLY verification_codes
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT vulnerabilities_pkey PRIMARY KEY (id);
ALTER TABLE ONLY vulnerability_advisories
ADD CONSTRAINT vulnerability_advisories_pkey PRIMARY KEY (id);
ALTER TABLE ONLY vulnerability_exports
ADD CONSTRAINT vulnerability_exports_pkey PRIMARY KEY (id);
@ -33529,10 +33464,6 @@ CREATE UNIQUE INDEX index_vulnerability_statistics_on_unique_project_id ON vulne
CREATE UNIQUE INDEX index_vulnerability_user_mentions_on_note_id ON vulnerability_user_mentions USING btree (note_id) WHERE (note_id IS NOT NULL);
CREATE INDEX index_vulnerable_component_versions_on_sbom_component_version ON sbom_vulnerable_component_versions USING btree (sbom_component_version_id);
CREATE INDEX index_vulnerable_component_versions_on_vulnerability_advisory ON sbom_vulnerable_component_versions USING btree (vulnerability_advisory_id);
CREATE UNIQUE INDEX index_vulns_user_mentions_on_vulnerability_id ON vulnerability_user_mentions USING btree (vulnerability_id) WHERE (note_id IS NULL);
CREATE UNIQUE INDEX index_vulns_user_mentions_on_vulnerability_id_and_note_id ON vulnerability_user_mentions USING btree (vulnerability_id, note_id);
@ -35906,9 +35837,6 @@ ALTER TABLE ONLY issues
ALTER TABLE ONLY ci_build_trace_chunks
ADD CONSTRAINT fk_89e29fa5ee_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY sbom_vulnerable_component_versions
ADD CONSTRAINT fk_8a2a1197f9 FOREIGN KEY (sbom_component_version_id) REFERENCES sbom_component_versions(id) ON DELETE CASCADE;
ALTER TABLE ONLY protected_branch_merge_access_levels
ADD CONSTRAINT fk_8a3072ccb3 FOREIGN KEY (protected_branch_id) REFERENCES protected_branches(id) ON DELETE CASCADE;
@ -36242,9 +36170,6 @@ ALTER TABLE ONLY lists
ALTER TABLE ONLY agent_activity_events
ADD CONSTRAINT fk_d6f785c9fc FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE ONLY sbom_vulnerable_component_versions
ADD CONSTRAINT fk_d720a1959a FOREIGN KEY (vulnerability_advisory_id) REFERENCES vulnerability_advisories(id) ON DELETE CASCADE;
ALTER TABLE ONLY user_achievements
ADD CONSTRAINT fk_d7653ef780 FOREIGN KEY (revoked_by_user_id) REFERENCES users(id) ON DELETE SET NULL;

View File

@ -70,7 +70,7 @@ The **CI/CD** settings contain:
## Security and Compliance settings
- [License compliance settings](../../user/admin_area/settings/security_and_compliance.md#choose-package-registry-metadata-to-sync): Enable or disable synchronization of package metadata by a registry type.
- [License compliance settings](security_and_compliance.md#choose-package-registry-metadata-to-sync): Enable or disable synchronization of package metadata by a registry type.
### Geo **(PREMIUM SELF)**

View File

@ -0,0 +1,25 @@
---
stage: Secure
group: Composition Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
type: howto
---
# Security and Compliance Admin Area settings **(ULTIMATE SELF)**
The settings for package metadata synchronization are located in the [Admin Area](index.md).
## Choose package registry metadata to sync
WARNING:
The full package metadata sync can add up to 30 GB to the PostgreSQL database. Ensure you have provisioned enough disk space for the database before enabling this feature.
We are actively working on reducing this data size in [epic 10415](https://gitlab.com/groups/gitlab-org/-/epics/10415).
To choose the packages you want to synchronize with the GitLab License Database for [License Compliance](../../user/compliance/license_scanning_of_cyclonedx_files/index.md):
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
1. Select **Admin Area**.
1. Select **Settings > Security and Compliance**.
1. Expand **License Compliance**.
1. Select or clear checkboxes for the package registries that you want to sync.
1. Select **Save changes**.

View File

@ -0,0 +1,35 @@
---
stage: none
group: unassigned
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
type: reference
---
# Sidekiq job size limits **(FREE SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68982) in GitLab 14.3.
[Sidekiq](../sidekiq/index.md) jobs get stored in
Redis. To avoid excessive memory for Redis, we:
- Compress job arguments before storing them in Redis.
- Reject jobs that exceed the specified threshold limit after compression.
To access Sidekiq job size limits:
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
1. Select **Admin Area**.
1. Select **Settings > Preferences**.
1. Expand **Sidekiq job size limits**.
1. Adjust the compression threshold or size limit. The compression can
be disabled by selecting the **Track** mode.
## Available settings
| Setting | Default | Description |
|-------------------------------------------|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Limiting mode | Compress | This mode compresses the jobs at the specified threshold and rejects them if they exceed the specified limit after compression. |
| Sidekiq job compression threshold (bytes) | 100 000 (100 KB) | When the size of arguments exceeds this threshold, they are compressed before being stored in Redis. |
| Sidekiq job size limit (bytes) | 0 | The jobs exceeding this size after compression are rejected. This avoids excessive memory usage in Redis leading to instability. Setting it to 0 prevents rejecting jobs. |
After changing these values, [restart Sidekiq](../restart_gitlab.md).

View File

@ -1,25 +1,11 @@
---
stage: Secure
group: Composition Analysis
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
type: howto
redirect_to: '../../../administration/settings/security_and_compliance.md'
remove_date: '2023-10-13'
---
# Security and Compliance Admin Area settings **(ULTIMATE SELF)**
This document was moved to [another location](../../../administration/settings/security_and_compliance.md).
The settings for package metadata synchronization are located in the [Admin Area](index.md).
## Choose package registry metadata to sync
WARNING:
The full package metadata sync can add up to 30 GB to the PostgreSQL database. Ensure you have provisioned enough disk space for the database before enabling this feature.
We are actively working on reducing this data size in [epic 10415](https://gitlab.com/groups/gitlab-org/-/epics/10415).
To choose the packages you want to synchronize with the GitLab License Database for [License Compliance](../../compliance/license_scanning_of_cyclonedx_files/index.md):
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
1. Select **Admin Area**.
1. Select **Settings > Security and Compliance**.
1. Expand **License Compliance**.
1. Select or clear checkboxes for the package registries that you want to sync.
1. Select **Save changes**.
<!-- This redirect file can be deleted after <2023-10-13>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -1,35 +1,11 @@
---
stage: none
group: unassigned
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
type: reference
redirect_to: '../../../administration/settings/sidekiq_job_limits.md'
remove_date: '2023-10-13'
---
# Sidekiq job size limits **(FREE SELF)**
This document was moved to [another location](../../../administration/settings/sidekiq_job_limits.md).
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68982) in GitLab 14.3.
[Sidekiq](../../../administration/sidekiq/index.md) jobs get stored in
Redis. To avoid excessive memory for Redis, we:
- Compress job arguments before storing them in Redis.
- Reject jobs that exceed the specified threshold limit after compression.
To access Sidekiq job size limits:
1. On the left sidebar, expand the top-most chevron (**{chevron-down}**).
1. Select **Admin Area**.
1. Select **Settings > Preferences**.
1. Expand **Sidekiq job size limits**.
1. Adjust the compression threshold or size limit. The compression can
be disabled by selecting the **Track** mode.
## Available settings
| Setting | Default | Description |
|-------------------------------------------|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Limiting mode | Compress | This mode compresses the jobs at the specified threshold and rejects them if they exceed the specified limit after compression. |
| Sidekiq job compression threshold (bytes) | 100 000 (100 KB) | When the size of arguments exceeds this threshold, they are compressed before being stored in Redis. |
| Sidekiq job size limit (bytes) | 0 | The jobs exceeding this size after compression are rejected. This avoids excessive memory usage in Redis leading to instability. Setting it to 0 prevents rejecting jobs. |
After changing these values, [restart Sidekiq](../../../administration/restart_gitlab.md).
<!-- This redirect file can be deleted after <2023-10-13>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -23,7 +23,7 @@ Licenses not in the SPDX list are reported as "Unknown". License information can
Prerequisites:
- On GitLab self-managed only, enable [Synchronization with the GitLab License Database](../../admin_area/settings/security_and_compliance.md#choose-package-registry-metadata-to-sync) in Admin Area for the GitLab instance. On GitLab SaaS this step has already been completed.
- On GitLab self-managed only, enable [Synchronization with the GitLab License Database](../../../administration/settings/security_and_compliance.md#choose-package-registry-metadata-to-sync) in Admin Area for the GitLab instance. On GitLab SaaS this step has already been completed.
- Enable [Dependency Scanning](../../application_security/dependency_scanning/index.md#configuration)
and ensure that its prerequisites are met.

View File

@ -208,10 +208,10 @@ Group items that are migrated to the destination GitLab instance include:
| Boards | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18938) |
| Board lists | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24863) |
| Epics <sup>1</sup> | [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/250281) |
| Group labels | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/292429) |
| Group labels <sup>2</sup> | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/292429) |
| Iterations | [GitLab 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292428) |
| Iteration cadences | [GitLab 15.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96570) |
| Members <sup>2</sup> | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/299415) |
| Members <sup>3</sup> | [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/issues/299415) |
| Group milestones | [GitLab 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/292427) |
| Namespace settings | [GitLab 14.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85128) |
| Release milestones | [GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/339422) |
@ -222,6 +222,8 @@ Group items that are migrated to the destination GitLab instance include:
associations [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62074) in GitLab 13.12, state and
state ID [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28203) in GitLab 13.7, and system note
metadata [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63551) in GitLab 14.0.
1. Group Labels cannot retain any associated Label Priorities during import. These labels will need to be re-prioritized manually
once the relevant Project is migrated to the destination instance.
1. Group members are associated with the imported group if the user:
- Already exists in the destination GitLab instance.
- Has a public email in the source GitLab instance that matches a confirmed email in the destination GitLab instance.
@ -490,7 +492,7 @@ for your version of GitLab to check which items can be imported to the destinati
Group items that are exported include:
- Milestones
- Labels
- Group Labels (_without_ associated label priorities)
- Boards and Board Lists
- Badges
- Subgroups (including all the aforementioned data)

View File

@ -6,7 +6,7 @@ module API
include ::API::Helpers::Authentication
ML_MODEL_PACKAGES_REQUIREMENTS = {
package_name: API::NO_SLASH_URL_PART_REGEX,
model_name: API::NO_SLASH_URL_PART_REGEX,
file_name: API::NO_SLASH_URL_PART_REGEX
}.freeze
@ -47,15 +47,15 @@ module API
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/packages/ml_models' do
params do
requires :package_name, type: String, desc: 'Package name', regexp: Gitlab::Regex.ml_model_name_regex,
requires :model_name, type: String, desc: 'Model name', regexp: Gitlab::Regex.ml_model_name_regex,
file_path: true
requires :package_version, type: String, desc: 'Package version',
requires :model_version, type: String, desc: 'Model version',
regexp: Gitlab::Regex.ml_model_version_regex
requires :file_name, type: String, desc: 'Package file name',
regexp: Gitlab::Regex.ml_model_file_name_regex, file_path: true
optional :status, type: String, values: ALLOWED_STATUSES, desc: 'Package status'
end
namespace ':package_name/*package_version/:file_name', requirements: ML_MODEL_PACKAGES_REQUIREMENTS do
namespace ':model_name/*model_version/:file_name', requirements: ML_MODEL_PACKAGES_REQUIREMENTS do
desc 'Workhorse authorize model package file' do
detail 'Introduced in GitLab 16.1'
success code: 200
@ -91,7 +91,12 @@ module API
bad_request!('File is too large') if max_file_size_exceeded?
create_package_file_params = declared(params).merge(build: current_authenticated_job)
create_package_file_params = declared(params).merge(
build: current_authenticated_job,
package_name: params[:model_name],
package_version: params[:model_version]
)
package_file = ::Packages::MlModel::CreatePackageFileService
.new(project, current_user, create_package_file_params)
.execute
@ -119,7 +124,7 @@ module API
authorize_read_package!(project)
package = ::Packages::MlModel::PackageFinder.new(project)
.execute!(params[:package_name], params[:package_version])
.execute!(params[:model_name], params[:model_version])
package_file = ::Packages::PackageFileFinder.new(package, params[:file_name]).execute!
present_package_file!(package_file)

View File

@ -6,6 +6,7 @@ module Gitlab
:name,
:description,
:gitlab_schemas,
:lock_gitlab_schemas,
:klass,
:fallback_database,
:db_dir,
@ -20,6 +21,7 @@ module Gitlab
self.name = name.to_sym
self.gitlab_schemas = gitlab_schemas.map(&:to_sym)
self.klass = klass.constantize
self.lock_gitlab_schemas = (lock_gitlab_schemas || []).map(&:to_sym)
self.fallback_database = fallback_database&.to_sym
self.db_dir = Rails.root.join(db_dir || 'db')
end

View File

@ -23,6 +23,21 @@ module Gitlab
tables.map { |table| table_schema!(table) }.to_set
end
# Mainly used for test tables
# It maps table names prefixes to gitlab_schemas.
# The order of keys matter. Prefixes that contain other prefixes should come first.
IMPLICIT_GITLAB_SCHEMAS = {
'_test_gitlab_main_clusterwide_' => :gitlab_main_clusterwide,
'_test_gitlab_main_cell_' => :gitlab_main_cell,
'_test_gitlab_main_' => :gitlab_main,
'_test_gitlab_ci_' => :gitlab_ci,
'_test_gitlab_embedding_' => :gitlab_embedding,
'_test_gitlab_geo_' => :gitlab_geo,
'_test_gitlab_pm_' => :gitlab_pm,
'_test_' => :gitlab_shared,
'pg_' => :gitlab_internal
}.freeze
# rubocop:disable Metrics/CyclomaticComplexity
def self.table_schema(name)
schema_name, table_name = name.split('.', 2) # Strip schema name like: `public.`
@ -54,19 +69,11 @@ module Gitlab
# All tables from `information_schema.` are marked as `internal`
return :gitlab_internal if schema_name == 'information_schema'
return :gitlab_main if table_name.start_with?('_test_gitlab_main_')
IMPLICIT_GITLAB_SCHEMAS.each do |prefix, gitlab_schema|
return gitlab_schema if table_name.start_with?(prefix)
end
return :gitlab_ci if table_name.start_with?('_test_gitlab_ci_')
return :gitlab_embedding if table_name.start_with?('_test_gitlab_embedding_')
return :gitlab_geo if table_name.start_with?('_test_gitlab_geo_')
# All tables that start with `_test_` without a following schema are shared and ignored
return :gitlab_shared if table_name.start_with?('_test_')
# All `pg_` tables are marked as `internal`
return :gitlab_internal if table_name.start_with?('pg_')
nil
end
# rubocop:enable Metrics/CyclomaticComplexity

View File

@ -11,7 +11,9 @@ module Gitlab
end
def exec_migration(connection, direction)
return super if %w[main ci].exclude?(Gitlab::Database.db_config_name(connection))
db_config_name = Gitlab::Database.db_config_name(connection)
db_info = Gitlab::Database.all_database_connections.fetch(db_config_name)
return super if db_info.lock_gitlab_schemas.empty?
return super if automatic_lock_on_writes_disabled?
# This compares the tables only on the `public` schema. Partitions are not affected
@ -20,7 +22,7 @@ module Gitlab
new_tables = connection.tables - tables
new_tables.each do |table_name|
lock_writes_on_table(connection, table_name) if should_lock_writes_on_table?(table_name)
lock_writes_on_table(connection, table_name) if should_lock_writes_on_table?(db_info, table_name)
end
end
@ -39,16 +41,17 @@ module Gitlab
end
end
def should_lock_writes_on_table?(table_name)
# currently gitlab_schema represents only present existing tables, this is workaround for deleted tables
# that should be skipped as they will be removed in a future migration.
def should_lock_writes_on_table?(db_info, table_name)
# We skip locking writes on tables that are scheduled for deletion in a future migration
return false if Gitlab::Database::GitlabSchema.deleted_tables_to_schema[table_name]
table_schema = Gitlab::Database::GitlabSchema.table_schema!(table_name.to_s)
return false unless %i[gitlab_main gitlab_ci].include?(table_schema)
Gitlab::Database.gitlab_schemas_for_connection(connection).exclude?(table_schema)
# This takes into consideration which database mode is used.
# In single-db and single-db-ci-connection the main database includes gitlab_ci tables,
# so we don't lock them there.
Gitlab::Database.gitlab_schemas_for_connection(connection).exclude?(table_schema) &&
db_info.lock_gitlab_schemas.include?(table_schema)
end
# with_retries creates new a transaction. So we set it to false if the connection is

View File

@ -7,12 +7,10 @@ tree:
group:
- :milestones
- :badges
- labels:
- :priorities
- :labels
- boards:
- lists:
- label:
- :priorities
- :label
- :board
- members:
- :user
@ -126,8 +124,7 @@ ee:
- boards:
- :board_assignee
- :milestone
- labels:
- :priorities
- :labels
- lists:
- milestone:
- events:

View File

@ -6,7 +6,6 @@ module Gitlab
class RelationFactory < Base::RelationFactory
OVERRIDES = {
labels: :group_labels,
priorities: :label_priorities,
label: :group_label,
parent: :epic,
iterations_cadences: 'Iterations::Cadence'

View File

@ -27,6 +27,15 @@ module Gitlab
"#{Gitlab::Observability.observability_url}/v1/auth/start"
end
def tracing_url(project)
"#{Gitlab::Observability.observability_url}/query/#{project.group.id}/#{project.id}/v1/traces"
end
def provisioning_url(_project)
# TODO Change to correct endpoint when API is ready
Gitlab::Observability.observability_url.to_s
end
# Returns true if the GitLab Observability UI (GOUI) feature flag is enabled
#
# @deprecated

View File

@ -881,6 +881,9 @@ msgstr ""
msgid "%{linkStart} Learn more%{linkEnd}."
msgstr ""
msgid "%{linkStart}%{linkEnd} review summary"
msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more"
msgstr ""
@ -11353,6 +11356,9 @@ msgstr ""
msgid "Comment templates can be used when creating comments inside issues, merge requests, and epics."
msgstr ""
msgid "Comment type"
msgstr ""
msgid "Comment/Reply (quoting selected text)"
msgstr ""

View File

@ -148,7 +148,7 @@ module QA
def start_discussion(text)
fill_element :comment_field, text
within_element(:comment_button) { click_button(class: 'dropdown-toggle-split') }
within_element(:comment_button) { click_button(class: 'gl-new-dropdown-toggle') }
click_element :discussion_menu_item
click_element :comment_button

View File

@ -46,8 +46,8 @@ RSpec.describe 'Merge request > User posts notes', :js, feature_category: :code_
it 'has enable submit button, preview button and saves content to local storage' do
page.within('.js-main-target-form') do
page.within('[data-testid="comment-button"]') do
expect(page).to have_css('.split-content-button')
expect(page).not_to have_css('.split-content-button[disabled]')
expect(page).to have_css('.gl-button')
expect(page).not_to have_css('.disabled')
end
expect(page).to have_css('.js-md-preview-button', visible: true)
end

View File

@ -1,4 +1,4 @@
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { GlButton, GlCollapsibleListbox, GlListboxItem } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import CommentTypeDropdown from '~/notes/components/comment_type_dropdown.vue';
@ -8,9 +8,9 @@ import { COMMENT_FORM } from '~/notes/i18n';
describe('CommentTypeDropdown component', () => {
let wrapper;
const findCommentGlDropdown = () => wrapper.findComponent(GlDropdown);
const findCommentDropdownOption = () => wrapper.findAllComponents(GlDropdownItem).at(0);
const findDiscussionDropdownOption = () => wrapper.findAllComponents(GlDropdownItem).at(1);
const findCommentButton = () => wrapper.findComponent(GlButton);
const findCommentListboxOption = () => wrapper.findAllComponents(GlListboxItem).at(0);
const findDiscussionListboxOption = () => wrapper.findAllComponents(GlListboxItem).at(1);
const mountComponent = ({ props = {} } = {}) => {
wrapper = extendedWrapper(
@ -20,6 +20,10 @@ describe('CommentTypeDropdown component', () => {
noteType: constants.COMMENT,
...props,
},
stubs: {
GlCollapsibleListbox,
GlListboxItem,
},
}),
);
};
@ -33,15 +37,15 @@ describe('CommentTypeDropdown component', () => {
({ isInternalNote, buttonText }) => {
mountComponent({ props: { noteType: constants.COMMENT, isInternalNote } });
expect(findCommentGlDropdown().props()).toMatchObject({ text: buttonText });
expect(findCommentButton().text()).toBe(buttonText);
},
);
it('Should set correct dropdown item checked when comment is selected', () => {
mountComponent({ props: { noteType: constants.COMMENT } });
expect(findCommentDropdownOption().props()).toMatchObject({ isChecked: true });
expect(findDiscussionDropdownOption().props()).toMatchObject({ isChecked: false });
expect(findCommentListboxOption().props('isSelected')).toBe(true);
expect(findDiscussionListboxOption().props('isSelected')).toBe(false);
});
it.each`
@ -53,32 +57,22 @@ describe('CommentTypeDropdown component', () => {
({ isInternalNote, buttonText }) => {
mountComponent({ props: { noteType: constants.DISCUSSION, isInternalNote } });
expect(findCommentGlDropdown().props()).toMatchObject({ text: buttonText });
expect(findCommentButton().text()).toBe(buttonText);
},
);
it('Should set correct dropdown item option checked when discussion is selected', () => {
mountComponent({ props: { noteType: constants.DISCUSSION } });
expect(findCommentDropdownOption().props()).toMatchObject({ isChecked: false });
expect(findDiscussionDropdownOption().props()).toMatchObject({ isChecked: true });
expect(findCommentListboxOption().props('isSelected')).toBe(false);
expect(findDiscussionListboxOption().props('isSelected')).toBe(true);
});
it('Should emit `change` event when clicking on an alternate dropdown option', () => {
mountComponent({ props: { noteType: constants.DISCUSSION } });
const event = {
type: 'click',
stopPropagation: jest.fn(),
preventDefault: jest.fn(),
};
findCommentDropdownOption().vm.$emit('click', event);
findDiscussionDropdownOption().vm.$emit('click', event);
// ensure the native events don't trigger anything
expect(event.stopPropagation).toHaveBeenCalledTimes(2);
expect(event.preventDefault).toHaveBeenCalledTimes(2);
findCommentListboxOption().trigger('click');
findDiscussionListboxOption().trigger('click');
expect(wrapper.emitted('change')[0]).toEqual([constants.COMMENT]);
expect(wrapper.emitted('change').length).toEqual(1);
@ -87,7 +81,7 @@ describe('CommentTypeDropdown component', () => {
it('Should emit `click` event when clicking on the action button', () => {
mountComponent({ props: { noteType: constants.DISCUSSION } });
findCommentGlDropdown().vm.$emit('click');
findCommentButton().vm.$emit('click');
expect(wrapper.emitted('click').length > 0).toBe(true);
});

View File

@ -0,0 +1,134 @@
import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { stubComponent } from 'helpers/stub_component';
import ObservabilityContainer from '~/observability/components/observability_container.vue';
import ObservabilitySkeleton from '~/observability/components/skeleton/index.vue';
import { buildClient } from '~/observability/client';
jest.mock('~/observability/client');
describe('ObservabilityContainer', () => {
let wrapper;
const mockSkeletonOnContentLoaded = jest.fn();
const mockSkeletonOnError = jest.fn();
const OAUTH_URL = 'https://example.com/oauth';
const TRACING_URL = 'https://example.com/tracing';
const PROVISIONING_URL = 'https://example.com/provisioning';
beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation();
buildClient.mockReturnValue({});
wrapper = shallowMountExtended(ObservabilityContainer, {
propsData: {
oauthUrl: OAUTH_URL,
tracingUrl: TRACING_URL,
provisioningUrl: PROVISIONING_URL,
},
stubs: {
ObservabilitySkeleton: stubComponent(ObservabilitySkeleton, {
methods: { onContentLoaded: mockSkeletonOnContentLoaded, onError: mockSkeletonOnError },
}),
},
slots: {
default: {
render(h) {
h(`<div>mockedComponent</div>`);
},
name: 'MockComponent',
props: {
observabilityClient: {
type: Object,
required: true,
},
},
},
},
});
});
const dispatchMessageEvent = (status, origin) =>
window.dispatchEvent(
new MessageEvent('message', {
data: {
type: 'AUTH_COMPLETION',
status,
},
origin: origin ?? new URL(OAUTH_URL).origin,
}),
);
const findIframe = () => wrapper.findByTestId('observability-oauth-iframe');
const findSlotComponent = () => wrapper.findComponent({ name: 'MockComponent' });
it('should render the oauth iframe', () => {
const iframe = findIframe();
expect(iframe.exists()).toBe(true);
expect(iframe.attributes('hidden')).toBe('hidden');
expect(iframe.attributes('src')).toBe(OAUTH_URL);
expect(iframe.attributes('sandbox')).toBe('allow-same-origin allow-forms allow-scripts');
});
it('should render the ObservabilitySkeleton', () => {
const skeleton = wrapper.findComponent(ObservabilitySkeleton);
expect(skeleton.exists()).toBe(true);
});
it('should not render the default slot', () => {
expect(findSlotComponent().exists()).toBe(false);
});
it('renders the slot content and removes the iframe on oauth success message', async () => {
dispatchMessageEvent('success');
await nextTick();
expect(mockSkeletonOnContentLoaded).toHaveBeenCalledTimes(1);
const slotComponent = findSlotComponent();
expect(slotComponent.exists()).toBe(true);
expect(buildClient).toHaveBeenCalledWith({
provisioningUrl: PROVISIONING_URL,
tracingUrl: TRACING_URL,
});
expect(findIframe().exists()).toBe(false);
});
it('does not render the slot content and removes the iframe on oauth error message', async () => {
dispatchMessageEvent('error');
await nextTick();
expect(mockSkeletonOnError).toHaveBeenCalledTimes(1);
expect(findSlotComponent().exists()).toBe(false);
expect(findIframe().exists()).toBe(false);
expect(buildClient).not.toHaveBeenCalled();
});
it('handles oauth message only once', () => {
dispatchMessageEvent('success');
dispatchMessageEvent('success');
expect(mockSkeletonOnContentLoaded).toHaveBeenCalledTimes(1);
});
it('only handles messages from the oauth url', () => {
dispatchMessageEvent('success', 'www.fake-url.com');
expect(mockSkeletonOnContentLoaded).toHaveBeenCalledTimes(0);
expect(findSlotComponent().exists()).toBe(false);
expect(findIframe().exists()).toBe(true);
});
it('does not handle messages if the component has been destroyed', () => {
wrapper.destroy();
dispatchMessageEvent('success');
expect(mockSkeletonOnContentLoaded).toHaveBeenCalledTimes(0);
});
});

View File

@ -19,7 +19,7 @@ describe('Skeleton component', () => {
const SKELETON_VARIANTS = Object.values(SKELETON_VARIANTS_BY_ROUTE);
const findContentWrapper = () => wrapper.findByTestId('observability-wrapper');
const findContentWrapper = () => wrapper.findByTestId('content-wrapper');
const findExploreSkeleton = () => wrapper.findComponent(ExploreSkeleton);
@ -42,8 +42,8 @@ describe('Skeleton component', () => {
mountComponent({ variant: 'explore' });
});
describe('loading timers', () => {
it('show Skeleton if content is not loaded within CONTENT_WAIT_MS', async () => {
describe('showing content', () => {
it('shows the skeleton if content is not loaded within CONTENT_WAIT_MS', async () => {
expect(findExploreSkeleton().exists()).toBe(false);
expect(findContentWrapper().isVisible()).toBe(false);
@ -55,7 +55,7 @@ describe('Skeleton component', () => {
expect(findContentWrapper().isVisible()).toBe(false);
});
it('does not show the skeleton if content has loaded within CONTENT_WAIT_MS', async () => {
it('does not show the skeleton if content loads within CONTENT_WAIT_MS', async () => {
expect(findExploreSkeleton().exists()).toBe(false);
expect(findContentWrapper().isVisible()).toBe(false);
@ -73,9 +73,25 @@ describe('Skeleton component', () => {
expect(findContentWrapper().isVisible()).toBe(true);
expect(findExploreSkeleton().exists()).toBe(false);
});
it('hides the skeleton after content loads', async () => {
jest.advanceTimersByTime(DEFAULT_TIMERS.CONTENT_WAIT_MS);
await nextTick();
expect(findExploreSkeleton().exists()).toBe(true);
expect(findContentWrapper().isVisible()).toBe(false);
wrapper.vm.onContentLoaded();
await nextTick();
expect(findContentWrapper().isVisible()).toBe(true);
expect(findExploreSkeleton().exists()).toBe(false);
});
});
describe('error timeout', () => {
describe('error handling', () => {
it('shows the error dialog if content has not loaded within TIMEOUT_MS', async () => {
expect(findAlert().exists()).toBe(false);
jest.advanceTimersByTime(DEFAULT_TIMERS.TIMEOUT_MS);
@ -86,6 +102,17 @@ describe('Skeleton component', () => {
expect(findContentWrapper().isVisible()).toBe(false);
});
it('shows the error dialog if content fails to load', async () => {
expect(findAlert().exists()).toBe(false);
wrapper.vm.onError();
await nextTick();
expect(findAlert().exists()).toBe(true);
expect(findContentWrapper().isVisible()).toBe(false);
});
it('does not show the error dialog if content has loaded within TIMEOUT_MS', async () => {
wrapper.vm.onContentLoaded();
jest.advanceTimersByTime(DEFAULT_TIMERS.TIMEOUT_MS);

View File

@ -0,0 +1,37 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ListIndex from '~/tracing/list_index.vue';
import TracingList from '~/tracing/components/tracing_list.vue';
import ObservabilityContainer from '~/observability/components/observability_container.vue';
describe('ListIndex', () => {
const props = {
oauthUrl: 'https://example.com/oauth',
tracingUrl: 'https://example.com/tracing',
provisioningUrl: 'https://example.com/provisioning',
};
let wrapper;
const mountComponent = () => {
wrapper = shallowMountExtended(ListIndex, {
propsData: props,
});
};
it('renders ObservabilityContainer component', () => {
mountComponent();
const observabilityContainer = wrapper.findComponent(ObservabilityContainer);
expect(observabilityContainer.exists()).toBe(true);
expect(observabilityContainer.props('oauthUrl')).toBe(props.oauthUrl);
expect(observabilityContainer.props('tracingUrl')).toBe(props.tracingUrl);
expect(observabilityContainer.props('provisioningUrl')).toBe(props.provisioningUrl);
});
it('renders TracingList component inside ObservabilityContainer', () => {
mountComponent();
const observabilityContainer = wrapper.findComponent(ObservabilityContainer);
expect(observabilityContainer.findComponent(TracingList).exists()).toBe(true);
});
});

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'spec_helper'
require 'json'
RSpec.describe Projects::ObservabilityHelper, type: :helper, feature_category: :tracing do
describe '#observability_tracing_view_model' do
let_it_be(:group) { build_stubbed(:group) }
let_it_be(:project) { build_stubbed(:project, group: group) }
it 'generates the correct JSON' do
expected_json = {
tracingUrl: Gitlab::Observability.tracing_url(project),
provisioningUrl: Gitlab::Observability.provisioning_url(project),
oauthUrl: Gitlab::Observability.oauth_url
}.to_json
expect(helper.observability_tracing_view_model(project)).to eq(expected_json)
end
end
end

View File

@ -29,6 +29,9 @@ RSpec.describe Gitlab::Database::GitlabSchema, feature_category: :database do
'audit_events_part_5fc467ac26' | :gitlab_main
'_test_gitlab_main_table' | :gitlab_main
'_test_gitlab_ci_table' | :gitlab_ci
'_test_gitlab_main_clusterwide_table' | :gitlab_main_clusterwide
'_test_gitlab_main_cell_table' | :gitlab_main_cell
'_test_gitlab_pm_table' | :gitlab_pm
'_test_my_table' | :gitlab_shared
'pg_attribute' | :gitlab_internal
end

View File

@ -9,7 +9,10 @@ RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
let(:schema_class) { Class.new(Gitlab::Database::Migration[2.1]) }
let(:skip_automatic_lock_on_writes) { false }
let(:gitlab_main_table_name) { :_test_gitlab_main_table }
let(:gitlab_main_clusterwide_table_name) { :_test_gitlab_main_clusterwide_table }
let(:gitlab_main_cell_table_name) { :_test_gitlab_main_cell_table }
let(:gitlab_ci_table_name) { :_test_gitlab_ci_table }
let(:gitlab_pm_table_name) { :_test_gitlab_pm_table }
let(:gitlab_geo_table_name) { :_test_gitlab_geo_table }
let(:gitlab_shared_table_name) { :_test_table }
@ -24,8 +27,11 @@ RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
# Drop the created test tables, because we use non-transactional tests
after do
drop_table_if_exists(gitlab_main_table_name)
drop_table_if_exists(gitlab_main_clusterwide_table_name)
drop_table_if_exists(gitlab_main_cell_table_name)
drop_table_if_exists(gitlab_ci_table_name)
drop_table_if_exists(gitlab_geo_table_name)
drop_table_if_exists(gitlab_pm_table_name)
drop_table_if_exists(gitlab_shared_table_name)
drop_table_if_exists(renamed_gitlab_main_table_name)
drop_table_if_exists(renamed_gitlab_ci_table_name)
@ -82,8 +88,12 @@ RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
context 'when single database' do
let(:config_model) { Gitlab::Database.database_base_models[:main] }
let(:create_gitlab_main_table_migration_class) { create_table_migration(gitlab_main_table_name) }
let(:create_gitlab_main_cell_table_migration_class) { create_table_migration(gitlab_main_cell_table_name) }
let(:create_gitlab_ci_table_migration_class) { create_table_migration(gitlab_ci_table_name) }
let(:create_gitlab_shared_table_migration_class) { create_table_migration(gitlab_shared_table_name) }
let(:create_gitlab_main_clusterwide_table_migration_class) do
create_table_migration(gitlab_main_clusterwide_table_name)
end
before do
skip_if_database_exists(:ci)
@ -93,13 +103,19 @@ RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
expect(Gitlab::Database::LockWritesManager).not_to receive(:new)
create_gitlab_main_table_migration_class.migrate(:up)
create_gitlab_main_cell_table_migration_class.migrate(:up)
create_gitlab_main_clusterwide_table_migration_class.migrate(:up)
create_gitlab_ci_table_migration_class.migrate(:up)
create_gitlab_shared_table_migration_class.migrate(:up)
expect do
create_gitlab_main_table_migration_class.connection.execute("DELETE FROM #{gitlab_main_table_name}")
create_gitlab_main_cell_table_migration_class.connection.execute("DELETE FROM #{gitlab_main_cell_table_name}")
create_gitlab_ci_table_migration_class.connection.execute("DELETE FROM #{gitlab_ci_table_name}")
create_gitlab_shared_table_migration_class.connection.execute("DELETE FROM #{gitlab_shared_table_name}")
create_gitlab_main_clusterwide_table_migration_class.connection.execute(
"DELETE FROM #{gitlab_main_clusterwide_table_name}"
)
end.not_to raise_error
end
end
@ -163,6 +179,27 @@ RSpec.describe Gitlab::Database::MigrationHelpers::AutomaticLockWritesOnTables,
end
end
context 'for creating a gitlab_main_clusterwide table' do
let(:table_name) { gitlab_main_clusterwide_table_name }
it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci]
end
context 'for creating a gitlab_main_cell table' do
let(:table_name) { gitlab_main_cell_table_name }
it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci]
end
context 'for creating a gitlab_pm table' do
let(:table_name) { gitlab_pm_table_name }
it_behaves_like 'does not lock writes on table', Gitlab::Database.database_base_models[:main]
it_behaves_like 'locks writes on table', Gitlab::Database.database_base_models[:ci]
end
context 'for creating a gitlab_ci table' do
let(:table_name) { gitlab_ci_table_name }

View File

@ -71,20 +71,22 @@ RSpec.describe Gitlab::ImportExport::Group::RelationTreeRestorer, feature_catego
before do
allow(shared.logger).to receive(:info).and_call_original
allow(relation_reader).to receive(:consume_relation).and_call_original
allow(relation_reader)
.to receive(:consume_relation)
.with(importable_name, 'labels')
.and_return([[label, 0]])
end
context 'when relation object is new' do
before do
allow(relation_reader)
.to receive(:consume_relation)
.with(importable_name, 'boards')
.and_return([[board, 0]])
end
context 'when relation object has invalid subrelations' do
let(:label) do
let(:board) do
{
'title' => 'test',
'priorities' => [LabelPriority.new, LabelPriority.new],
'type' => 'GroupLabel'
'name' => 'test',
'lists' => [List.new, List.new],
'group_id' => importable.id
}
end
@ -94,26 +96,33 @@ RSpec.describe Gitlab::ImportExport::Group::RelationTreeRestorer, feature_catego
.with(
message: '[Project/Group Import] Invalid subrelation',
group_id: importable.id,
relation_key: 'labels',
error_messages: "Project can't be blank, Priority can't be blank, and Priority is not a number"
relation_key: 'boards',
error_messages: "Label can't be blank, Position can't be blank, and Position is not a number"
)
subject
label = importable.labels.first
board = importable.boards.last
failure = importable.import_failures.first
expect(importable.import_failures.count).to eq(2)
expect(label.title).to eq('test')
expect(board.name).to eq('test')
expect(failure.exception_class).to eq('ActiveRecord::RecordInvalid')
expect(failure.source).to eq('RelationTreeRestorer#save_relation_object')
expect(failure.exception_message)
.to eq("Project can't be blank, Priority can't be blank, and Priority is not a number")
.to eq("Label can't be blank, Position can't be blank, and Position is not a number")
end
end
end
context 'when relation object is persisted' do
before do
allow(relation_reader)
.to receive(:consume_relation)
.with(importable_name, 'labels')
.and_return([[label, 0]])
end
context 'when relation object is invalid' do
let(:label) { create(:group_label, group: group, title: 'test') }

View File

@ -3,6 +3,9 @@
require 'spec_helper'
RSpec.describe Gitlab::Observability, feature_category: :error_tracking do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
describe '.observability_url' do
let(:gitlab_url) { 'https://example.com' }
@ -37,6 +40,18 @@ RSpec.describe Gitlab::Observability, feature_category: :error_tracking do
it { is_expected.to eq("#{described_class.observability_url}/v1/auth/start") }
end
describe '.tracing_url' do
subject { described_class.tracing_url(project) }
it { is_expected.to eq("#{described_class.observability_url}/query/#{group.id}/#{project.id}/v1/traces") }
end
describe '.provisioning_url' do
subject { described_class.provisioning_url(project) }
it { is_expected.to eq(described_class.observability_url.to_s) }
end
describe '.build_full_url' do
let_it_be(:group) { build_stubbed(:group, id: 123) }
let(:observability_url) { described_class.observability_url }

View File

@ -40,7 +40,12 @@ RSpec.describe BulkImports::FileTransfer::GroupConfig, feature_category: :import
describe '#top_relation_tree' do
it 'returns relation tree of a top level relation' do
expect(subject.top_relation_tree('labels')).to eq('priorities' => {})
expect(subject.top_relation_tree('boards')).to include(
'lists' => a_hash_including({
'board' => anything,
'label' => anything
})
)
end
end

View File

@ -1510,52 +1510,22 @@ RSpec.describe Issue, feature_category: :team_planning do
end
describe '#publicly_visible?' do
context 'using a public project' do
let(:project) { create(:project, :public) }
let(:project) { build(:project, project_visiblity) }
let(:issue) { build(:issue, confidential: confidential, project: project) }
it 'returns true for a regular issue' do
issue = build(:issue, project: project)
subject { issue.send(:publicly_visible?) }
expect(issue).to be_truthy
end
it 'returns false for a confidential issue' do
issue = build(:issue, :confidential, project: project)
expect(issue).not_to be_falsy
end
where(:project_visiblity, :confidential, :expected_value) do
:public | false | true
:public | true | false
:internal | false | false
:internal | true | false
:private | false | false
:private | true | false
end
context 'using an internal project' do
let(:project) { create(:project, :internal) }
it 'returns false for a regular issue' do
issue = build(:issue, project: project)
expect(issue).not_to be_falsy
end
it 'returns false for a confidential issue' do
issue = build(:issue, :confidential, project: project)
expect(issue).not_to be_falsy
end
end
context 'using a private project' do
let(:project) { create(:project, :private) }
it 'returns false for a regular issue' do
issue = build(:issue, project: project)
expect(issue).not_to be_falsy
end
it 'returns false for a confidential issue' do
issue = build(:issue, :confidential, project: project)
expect(issue).not_to be_falsy
end
with_them do
it { is_expected.to eq(expected_value) }
end
end

View File

@ -124,18 +124,18 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
project.send("add_#{user_role}", user) if member && user_role != :anonymous
end
describe 'PUT /api/v4/projects/:id/packages/ml_models/:package_name/:package_version/:file_name/authorize' do
describe 'PUT /api/v4/projects/:id/packages/ml_models/:model_name/:model_version/:file_name/authorize' do
include_context 'ml model authorize permissions table'
let(:token) { tokens[:personal_access_token] }
let(:user_headers) { { 'HTTP_AUTHORIZATION' => token } }
let(:headers) { user_headers.merge(workhorse_headers) }
let(:request) { authorize_upload_file(headers) }
let(:package_name) { 'my_package' }
let(:model_name) { 'my_package' }
let(:file_name) { 'myfile.tar.gz' }
subject(:api_response) do
url = "/projects/#{project.id}/packages/ml_models/#{package_name}/0.0.1/#{file_name}/authorize"
url = "/projects/#{project.id}/packages/ml_models/#{model_name}/0.0.1/#{file_name}/authorize"
put api(url), headers: headers
@ -162,7 +162,7 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
end
describe 'application security' do
where(:package_name, :file_name) do
where(:model_name, :file_name) do
'my-package/../' | 'myfile.tar.gz'
'my-package%2f%2e%2e%2f' | 'myfile.tar.gz'
'my_package' | '../.ssh%2fauthorized_keys'
@ -177,7 +177,7 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
end
end
describe 'PUT /api/v4/projects/:id/packages/ml_models/:package_name/:package_version/:file_name' do
describe 'PUT /api/v4/projects/:id/packages/ml_models/:model_name/:model_version/:file_name' do
include_context 'ml model authorize permissions table'
let_it_be(:file_name) { 'model.md5' }
@ -188,10 +188,10 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
let(:params) { { file: temp_file(file_name) } }
let(:file_key) { :file }
let(:send_rewritten_field) { true }
let(:package_name) { 'my_package' }
let(:model_name) { 'my_package' }
subject(:api_response) do
url = "/projects/#{project.id}/packages/ml_models/#{package_name}/0.0.1/#{file_name}"
url = "/projects/#{project.id}/packages/ml_models/#{model_name}/0.0.1/#{file_name}"
workhorse_finalize(
api(url),
@ -236,14 +236,14 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
end
end
describe 'GET /api/v4/projects/:project_id/packages/ml_models/:package_name/:package_version/:file_name' do
describe 'GET /api/v4/projects/:project_id/packages/ml_models/:model_name/:model_version/:file_name' do
include_context 'ml model authorize permissions table'
let_it_be(:package) { create(:ml_model_package, project: project, name: 'model', version: '0.0.1') }
let_it_be(:package_file) { create(:package_file, :generic, package: package, file_name: 'model.md5') }
let(:package_name) { package.name }
let(:package_version) { package.version }
let(:model_name) { package.name }
let(:model_version) { package.version }
let(:file_name) { package_file.file_name }
let(:token) { tokens[:personal_access_token] }
@ -251,7 +251,7 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do
let(:headers) { user_headers.merge(workhorse_headers) }
subject(:api_response) do
url = "/projects/#{project.id}/packages/ml_models/#{package_name}/#{package_version}/#{file_name}"
url = "/projects/#{project.id}/packages/ml_models/#{model_name}/#{model_version}/#{file_name}"
get api(url), headers: headers

View File

@ -121,7 +121,7 @@ RSpec.describe API::ProtectedBranches, feature_category: :source_code_management
get api(route, user)
expect(json_response['push_access_levels']).to include(
a_hash_including('access_level_description' => 'Deploy key', 'deploy_key_id' => deploy_key.id)
a_hash_including('access_level_description' => deploy_key.title, 'deploy_key_id' => deploy_key.id)
)
end
end

View File

@ -95,7 +95,7 @@ RSpec.describe API::ProtectedTags, feature_category: :source_code_management do
get api(route, user)
expect(json_response['create_access_levels']).to include(
a_hash_including('access_level_description' => 'Deploy key', 'deploy_key_id' => deploy_key.id)
a_hash_including('access_level_description' => deploy_key.title, 'deploy_key_id' => deploy_key.id)
)
end
end

View File

@ -3,7 +3,8 @@
require 'spec_helper'
RSpec.describe Projects::TracingController, feature_category: :tracing do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:user) { create(:user) }
let(:path) { nil }
let(:observability_tracing_ff) { true }
@ -44,6 +45,17 @@ RSpec.describe Projects::TracingController, feature_category: :tracing do
expect(subject).to have_gitlab_http_status(:ok)
end
it 'renders the js-tracing element correctly' do
element = Nokogiri::HTML.parse(subject.body).at_css('#js-tracing')
expected_view_model = {
tracingUrl: Gitlab::Observability.tracing_url(project),
provisioningUrl: Gitlab::Observability.provisioning_url(project),
oauthUrl: Gitlab::Observability.oauth_url
}.to_json
expect(element.attributes['data-view-model'].value).to eq(expected_view_model)
end
context 'when feature is disabled' do
let(:observability_tracing_ff) { false }

View File

@ -3,8 +3,8 @@
RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name|
let(:form_selector) { '.js-main-target-form' }
let(:dropdown_selector) { "#{form_selector} .comment-type-dropdown" }
let(:toggle_selector) { "#{dropdown_selector} .gl-dropdown-toggle" }
let(:menu_selector) { "#{dropdown_selector} .dropdown-menu" }
let(:toggle_selector) { "#{dropdown_selector} .gl-new-dropdown-toggle" }
let(:menu_selector) { "#{dropdown_selector} .gl-new-dropdown-contents" }
let(:submit_selector) { "#{form_selector} .js-comment-submit-button > button:first-child" }
let(:close_selector) { "#{form_selector} .btn-comment-and-close" }
let(:comments_selector) { '.timeline > .note.timeline-entry:not(.being-posted)' }
@ -63,33 +63,6 @@ RSpec.shared_examples 'thread comments for commit and snippet' do |resource_name
expect(page).not_to have_selector menu_selector
end
it 'clicking the ul padding or divider should not change the text' do
execute_script("document.querySelector('#{menu_selector}').click()")
# on issues page, the menu closes when clicking anywhere, on other pages it will
# remain open if clicking divider or menu padding, but should not change button action
#
# if dropdown menu is not toggled (and also not present),
# it's "issue-type" dropdown
if first(menu_selector, minimum: 0).nil?
expect(find(dropdown_selector)).to have_content 'Comment'
find(toggle_selector).click
execute_script("document.querySelector('#{menu_selector} .dropdown-divider').click()")
else
execute_script("document.querySelector('#{menu_selector}').click()")
expect(page).to have_selector menu_selector
expect(find(dropdown_selector)).to have_content 'Comment'
execute_script("document.querySelector('#{menu_selector} .dropdown-divider').click()")
expect(page).to have_selector menu_selector
end
expect(find(dropdown_selector)).to have_content 'Comment'
end
describe 'when selecting "Start thread"' do
before do
find("#{menu_selector} li", match: :first)
@ -178,10 +151,10 @@ end
RSpec.shared_examples 'thread comments for issue, epic and merge request' do |resource_name|
let(:form_selector) { '.js-main-target-form' }
let(:dropdown_selector) { "#{form_selector} [data-testid='comment-button']" }
let(:submit_button_selector) { "#{dropdown_selector} .split-content-button" }
let(:toggle_selector) { "#{dropdown_selector} .dropdown-toggle-split" }
let(:menu_selector) { "#{dropdown_selector} .dropdown-menu" }
let(:dropdown_selector) { "#{form_selector} .comment-type-dropdown" }
let(:toggle_selector) { "#{dropdown_selector} .gl-new-dropdown-toggle" }
let(:menu_selector) { "#{dropdown_selector} .gl-new-dropdown-contents" }
let(:submit_selector) { "#{form_selector} .js-comment-submit-button > button:first-child" }
let(:close_selector) { "#{form_selector} .btn-comment-and-close" }
let(:comments_selector) { '.timeline > .note.timeline-entry:not(.being-posted)' }
let(:comment) { 'My comment' }
@ -191,7 +164,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
find("#{form_selector} .note-textarea").send_keys(comment)
find(submit_button_selector).click
find(submit_selector).click
wait_for_all_requests
@ -260,7 +233,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
describe 'creating a thread' do
before do
find(submit_button_selector).click
find(submit_selector).click
wait_for_requests
find(comments_selector, match: :first)
@ -366,7 +339,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
end
it 'updates the submit button text and closes the dropdown' do
button = find(submit_button_selector)
button = find(submit_selector)
expect(button).to have_content 'Comment'

View File

@ -40,7 +40,8 @@ RSpec.describe 'gitlab:feature_categories:index', :silence_stdout, feature_categ
)
),
'database_tables' => a_hash_including(
'container_scanning' => a_collection_including('vulnerability_advisories')
'continuous_integration' => a_collection_including('ci_pipelines'),
'user_profile' => a_collection_including('users')
)
}