Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-12-02 03:13:01 +00:00
parent ef615776bf
commit edfc0f680f
16 changed files with 173 additions and 41 deletions

View File

@ -1,12 +1,22 @@
<script> <script>
import { GlTabs, GlTab } from '@gitlab/ui'; import { GlTabs, GlTab } from '@gitlab/ui';
import { CLUSTERS_TABS, MAX_CLUSTERS_LIST, MAX_LIST_COUNT, AGENT } from '../constants'; import Tracking from '~/tracking';
import {
CLUSTERS_TABS,
MAX_CLUSTERS_LIST,
MAX_LIST_COUNT,
AGENT,
EVENT_LABEL_TABS,
EVENT_ACTIONS_CHANGE,
} from '../constants';
import Agents from './agents.vue'; import Agents from './agents.vue';
import InstallAgentModal from './install_agent_modal.vue'; import InstallAgentModal from './install_agent_modal.vue';
import ClustersActions from './clusters_actions.vue'; import ClustersActions from './clusters_actions.vue';
import Clusters from './clusters.vue'; import Clusters from './clusters.vue';
import ClustersViewAll from './clusters_view_all.vue'; import ClustersViewAll from './clusters_view_all.vue';
const trackingMixin = Tracking.mixin({ label: EVENT_LABEL_TABS });
export default { export default {
components: { components: {
GlTabs, GlTabs,
@ -18,6 +28,7 @@ export default {
InstallAgentModal, InstallAgentModal,
}, },
CLUSTERS_TABS, CLUSTERS_TABS,
mixins: [trackingMixin],
props: { props: {
defaultBranchName: { defaultBranchName: {
default: '.noBranch', default: '.noBranch',
@ -34,9 +45,12 @@ export default {
methods: { methods: {
onTabChange(tabName) { onTabChange(tabName) {
this.selectedTabIndex = CLUSTERS_TABS.findIndex((tab) => tab.queryParamValue === tabName); this.selectedTabIndex = CLUSTERS_TABS.findIndex((tab) => tab.queryParamValue === tabName);
this.maxAgents = tabName === AGENT ? MAX_LIST_COUNT : MAX_CLUSTERS_LIST; this.maxAgents = tabName === AGENT ? MAX_LIST_COUNT : MAX_CLUSTERS_LIST;
}, },
trackTabChange(tab) {
const tabName = CLUSTERS_TABS[tab].queryParamValue;
this.track(EVENT_ACTIONS_CHANGE, { property: tabName });
},
}, },
}; };
</script> </script>
@ -47,6 +61,7 @@ export default {
sync-active-tab-with-query-params sync-active-tab-with-query-params
nav-class="gl-flex-grow-1 gl-align-items-center" nav-class="gl-flex-grow-1 gl-align-items-center"
lazy lazy
@input="trackTabChange"
> >
<gl-tab <gl-tab
v-for="(tab, idx) in $options.CLUSTERS_TABS" v-for="(tab, idx) in $options.CLUSTERS_TABS"

View File

@ -11,8 +11,19 @@ import {
import { helpPagePath } from '~/helpers/help_page_helper'; import { helpPagePath } from '~/helpers/help_page_helper';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import CodeBlock from '~/vue_shared/components/code_block.vue'; import CodeBlock from '~/vue_shared/components/code_block.vue';
import Tracking from '~/tracking';
import { generateAgentRegistrationCommand } from '../clusters_util'; import { generateAgentRegistrationCommand } from '../clusters_util';
import { INSTALL_AGENT_MODAL_ID, I18N_AGENT_MODAL, KAS_DISABLED_ERROR } from '../constants'; import {
INSTALL_AGENT_MODAL_ID,
I18N_AGENT_MODAL,
KAS_DISABLED_ERROR,
EVENT_LABEL_MODAL,
EVENT_ACTIONS_OPEN,
EVENT_ACTIONS_SELECT,
EVENT_ACTIONS_CLICK,
MODAL_TYPE_EMPTY,
MODAL_TYPE_REGISTER,
} from '../constants';
import { addAgentToStore, addAgentConfigToStore } from '../graphql/cache_update'; import { addAgentToStore, addAgentConfigToStore } from '../graphql/cache_update';
import createAgent from '../graphql/mutations/create_agent.mutation.graphql'; import createAgent from '../graphql/mutations/create_agent.mutation.graphql';
import createAgentToken from '../graphql/mutations/create_agent_token.mutation.graphql'; import createAgentToken from '../graphql/mutations/create_agent_token.mutation.graphql';
@ -20,8 +31,13 @@ import getAgentsQuery from '../graphql/queries/get_agents.query.graphql';
import agentConfigurations from '../graphql/queries/agent_configurations.query.graphql'; import agentConfigurations from '../graphql/queries/agent_configurations.query.graphql';
import AvailableAgentsDropdown from './available_agents_dropdown.vue'; import AvailableAgentsDropdown from './available_agents_dropdown.vue';
const trackingMixin = Tracking.mixin({ label: EVENT_LABEL_MODAL });
export default { export default {
modalId: INSTALL_AGENT_MODAL_ID, modalId: INSTALL_AGENT_MODAL_ID,
EVENT_ACTIONS_OPEN,
EVENT_ACTIONS_CLICK,
EVENT_LABEL_MODAL,
components: { components: {
AvailableAgentsDropdown, AvailableAgentsDropdown,
ClipboardButton, ClipboardButton,
@ -34,6 +50,7 @@ export default {
GlModal, GlModal,
GlSprintf, GlSprintf,
}, },
mixins: [trackingMixin],
inject: ['projectPath', 'kasAddress', 'emptyStateImage'], inject: ['projectPath', 'kasAddress', 'emptyStateImage'],
props: { props: {
defaultBranchName: { defaultBranchName: {
@ -81,7 +98,7 @@ export default {
return !this.registering && this.agentName !== null; return !this.registering && this.agentName !== null;
}, },
canCancel() { canCancel() {
return !this.registered && !this.registering && this.isRegisterModal; return !this.registered && !this.registering && this.isAgentRegistrationModal;
}, },
agentRegistrationCommand() { agentRegistrationCommand() {
return generateAgentRegistrationCommand(this.agentToken, this.kasAddress); return generateAgentRegistrationCommand(this.agentToken, this.kasAddress);
@ -117,21 +134,24 @@ export default {
return `/${this.projectPath}`; return `/${this.projectPath}`;
}, },
modalType() { modalType() {
return !this.availableAgents?.length && !this.registered ? 'install' : 'register'; return !this.availableAgents?.length && !this.registered
? MODAL_TYPE_EMPTY
: MODAL_TYPE_REGISTER;
}, },
modalSize() { modalSize() {
return this.isInstallModal ? 'sm' : 'md'; return this.isEmptyStateModal ? 'sm' : 'md';
}, },
isInstallModal() { isEmptyStateModal() {
return this.modalType === 'install'; return this.modalType === MODAL_TYPE_EMPTY;
}, },
isRegisterModal() { isAgentRegistrationModal() {
return this.modalType === 'register'; return this.modalType === MODAL_TYPE_REGISTER;
}, },
}, },
methods: { methods: {
setAgentName(name) { setAgentName(name) {
this.agentName = name; this.agentName = name;
this.track(EVENT_ACTIONS_SELECT);
}, },
closeModal() { closeModal() {
this.$refs.modal.hide(); this.$refs.modal.hide();
@ -242,8 +262,9 @@ export default {
static static
lazy lazy
@hidden="resetModal" @hidden="resetModal"
@show="track($options.EVENT_ACTIONS_OPEN, { property: modalType })"
> >
<template v-if="isRegisterModal"> <template v-if="isAgentRegistrationModal">
<template v-if="!registered"> <template v-if="!registered">
<p> <p>
<strong>{{ i18n.selectAgentTitle }}</strong> <strong>{{ i18n.selectAgentTitle }}</strong>
@ -347,23 +368,40 @@ export default {
</template> </template>
<template #modal-footer> <template #modal-footer>
<gl-button v-if="canCancel" @click="closeModal">{{ i18n.cancel }} </gl-button> <gl-button
v-if="canCancel"
:data-track-action="$options.EVENT_ACTIONS_CLICK"
:data-track-label="$options.EVENT_LABEL_MODAL"
data-track-property="cancel"
@click="closeModal"
>{{ i18n.cancel }}
</gl-button>
<gl-button v-if="registered" variant="confirm" category="primary" @click="closeModal" <gl-button
v-if="registered"
variant="confirm"
category="primary"
:data-track-action="$options.EVENT_ACTIONS_CLICK"
:data-track-label="$options.EVENT_LABEL_MODAL"
data-track-property="close"
@click="closeModal"
>{{ i18n.close }} >{{ i18n.close }}
</gl-button> </gl-button>
<gl-button <gl-button
v-else-if="isRegisterModal" v-else-if="isAgentRegistrationModal"
:disabled="!nextButtonDisabled" :disabled="!nextButtonDisabled"
variant="confirm" variant="confirm"
category="primary" category="primary"
:data-track-action="$options.EVENT_ACTIONS_CLICK"
:data-track-label="$options.EVENT_LABEL_MODAL"
data-track-property="register"
@click="registerAgent" @click="registerAgent"
>{{ i18n.registerAgentButton }} >{{ i18n.registerAgentButton }}
</gl-button> </gl-button>
<gl-button <gl-button
v-if="isInstallModal" v-if="isEmptyStateModal"
:href="repositoryPath" :href="repositoryPath"
variant="confirm" variant="confirm"
category="secondary" category="secondary"
@ -371,7 +409,14 @@ export default {
>{{ i18n.secondaryButton }} >{{ i18n.secondaryButton }}
</gl-button> </gl-button>
<gl-button v-if="isInstallModal" variant="confirm" category="primary" @click="closeModal" <gl-button
v-if="isEmptyStateModal"
variant="confirm"
category="primary"
:data-track-action="$options.EVENT_ACTIONS_CLICK"
:data-track-label="$options.EVENT_LABEL_MODAL"
data-track-property="done"
@click="closeModal"
>{{ i18n.done }} >{{ i18n.done }}
</gl-button> </gl-button>
</template> </template>

View File

@ -65,7 +65,7 @@ export const STATUSES = {
}; };
export const I18N_AGENT_MODAL = { export const I18N_AGENT_MODAL = {
register: { agent_registration: {
registerAgentButton: s__('ClusterAgents|Register Agent'), registerAgentButton: s__('ClusterAgents|Register Agent'),
close: __('Close'), close: __('Close'),
cancel: __('Cancel'), cancel: __('Cancel'),
@ -104,7 +104,7 @@ export const I18N_AGENT_MODAL = {
registrationErrorTitle: __('Failed to register Agent'), registrationErrorTitle: __('Failed to register Agent'),
unknownError: s__('ClusterAgents|An unknown error occurred. Please try again.'), unknownError: s__('ClusterAgents|An unknown error occurred. Please try again.'),
}, },
install: { empty_state: {
modalTitle: s__('ClusterAgents|Install new Agent'), modalTitle: s__('ClusterAgents|Install new Agent'),
modalBody: s__( modalBody: s__(
'ClusterAgents|To install an Agent you should create an agent directory in the Repository first. We recommend that you add the Agent configuration to the directory before you start the installation process.', 'ClusterAgents|To install an Agent you should create an agent directory in the Repository first. We recommend that you add the Agent configuration to the directory before you start the installation process.',
@ -236,3 +236,13 @@ export const CLUSTERS_ACTIONS = {
export const AGENT = 'agent'; export const AGENT = 'agent';
export const CERTIFICATE_BASED = 'certificate_based'; export const CERTIFICATE_BASED = 'certificate_based';
export const EVENT_LABEL_MODAL = 'agent_registration_modal';
export const EVENT_LABEL_TABS = 'kubernetes_section_tabs';
export const EVENT_ACTIONS_OPEN = 'open_modal';
export const EVENT_ACTIONS_SELECT = 'select_agent';
export const EVENT_ACTIONS_CLICK = 'click_button';
export const EVENT_ACTIONS_CHANGE = 'change_tab';
export const MODAL_TYPE_EMPTY = 'empty_state';
export const MODAL_TYPE_REGISTER = 'agent_registration';

View File

@ -67,7 +67,7 @@ export default {
data-qa-selector="dropdown_button" data-qa-selector="dropdown_button"
@click.stop="openDropdown()" @click.stop="openDropdown()"
> >
<gl-icon name="ellipsis_v" /> <gl-icon name="chevron-down" /> <gl-icon name="ellipsis_v" />
</button> </button>
<ul ref="dropdownMenu" class="dropdown-menu dropdown-menu-right"> <ul ref="dropdownMenu" class="dropdown-menu dropdown-menu-right">
<template v-if="type === 'tree'"> <template v-if="type === 'tree'">

View File

@ -2099,6 +2099,10 @@ This endpoint:
- Deletes a project including all associated resources (including issues and - Deletes a project including all associated resources (including issues and
merge requests). merge requests).
- In [GitLab 12.6](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) and later, on
[Premium or higher](https://about.gitlab.com/pricing/) tiers,
[delayed project deletion](../user/project/settings/index.md#delayed-project-deletion)
is applied if enabled.
- From [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) on - From [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) on
[Premium or higher](https://about.gitlab.com/pricing/) tiers, group [Premium or higher](https://about.gitlab.com/pricing/) tiers, group
administrators can [configure](../user/group/index.md#enable-delayed-project-deletion) administrators can [configure](../user/group/index.md#enable-delayed-project-deletion)

View File

@ -22,8 +22,7 @@ This tutorial assumes you are familiar with GitLab CI/CD and Vault.
To follow along, you must have: To follow along, you must have:
- An account on GitLab. - An account on GitLab.
- A running Vault server and access to it is required to configure authentication and create roles - Access to a running Vault server (at least v1.2.0) to configure authentication and to create roles and policies. For HashiCorp Vaults, this can be the Open Source or Enterprise version.
and policies. For HashiCorp Vaults, this can be the Open Source or Enterprise version.
NOTE: NOTE:
You must replace the `vault.example.com` URL below with the URL of your Vault server, and `gitlab.example.com` with the URL of your GitLab instance. You must replace the `vault.example.com` URL below with the URL of your Vault server, and `gitlab.example.com` with the URL of your GitLab instance.

View File

@ -53,6 +53,7 @@ and supports multiple secrets engines.
To configure your Vault server: To configure your Vault server:
1. Ensure your Vault server is running on version 1.2.0 or higher.
1. Enable the authentication method by running these commands. They provide your Vault 1. Enable the authentication method by running these commands. They provide your Vault
server the [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) (JWKS) endpoint for your GitLab instance, so Vault server the [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) (JWKS) endpoint for your GitLab instance, so Vault
can fetch the public signing key and verify the JSON Web Token (JWT) when authenticating: can fetch the public signing key and verify the JSON Web Token (JWT) when authenticating:

View File

@ -97,8 +97,8 @@ delete a project. To allow only users with the Administrator role to delete proj
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/255449) in GitLab 14.2 for groups created after August 12, 2021. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/255449) in GitLab 14.2 for groups created after August 12, 2021.
Projects in a group (but not a personal namespace) can be deleted after a delayed period. [Delayed project deletion](../../project/settings/index.md#delayed-project-deletion) allows projects in a group (not a personal namespace)
You can [configure it in group settings](../../group/index.md#enable-delayed-project-deletion). to be deleted after a period of delay.
To enable delayed project deletion by default in new groups: To enable delayed project deletion by default in new groups:
@ -110,14 +110,13 @@ To enable delayed project deletion by default in new groups:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6.
By default, a project marked for deletion is permanently removed with immediate effect. By default, a project marked for deletion is permanently removed with immediate effect.
See [delayed project deletion](../../project/settings/index.md#delayed-project-deletion) to learn more.
By default, a group marked for deletion is permanently removed after seven days. By default, a group marked for deletion is permanently removed after seven days.
WARNING: WARNING:
The default behavior of [Delayed Project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6 was changed to The default behavior of [Delayed Project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6 was changed to
[Immediate deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) in GitLab 13.2. [Immediate deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) in GitLab 13.2.
Projects in a group (but not a personal namespace) can be deleted after a delayed period, by
[configuring in Group Settings](../../group/index.md#enable-delayed-project-deletion).
The default period is seven days, and can be changed. Setting this period to `0` enables immediate removal The default period is seven days, and can be changed. Setting this period to `0` enables immediate removal
of projects or groups. of projects or groups.

View File

@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
INFO: INFO:
Want to try out container scanning? Want to try out container scanning?
[Get a free 30-day trial GitLab Ultimate](https://about.gitlab.com/free-trial?glm_source=docs.gitlab.com&glm_content=u-container-scanning-docs). [Get a free 30-day trial of GitLab Ultimate](https://about.gitlab.com/free-trial?glm_source=docs.gitlab.com&glm_content=u-container-scanning-docs).
Your application's Docker image may itself be based on Docker images that contain known Your application's Docker image may itself be based on Docker images that contain known
vulnerabilities. By including an extra job in your pipeline that scans for those vulnerabilities and vulnerabilities. By including an extra job in your pipeline that scans for those vulnerabilities and
@ -135,6 +135,7 @@ You can [configure](#customizing-the-container-scanning-settings) analyzers by u
| `CI_APPLICATION_TAG` | `$CI_COMMIT_SHA` | Docker repository tag for the image to be scanned. | All | | `CI_APPLICATION_TAG` | `$CI_COMMIT_SHA` | Docker repository tag for the image to be scanned. | All |
| `CS_ANALYZER_IMAGE` | `registry.gitlab.com/security-products/container-scanning:4` | Docker image of the analyzer. | All | | `CS_ANALYZER_IMAGE` | `registry.gitlab.com/security-products/container-scanning:4` | Docker image of the analyzer. | All |
| `CS_DEFAULT_BRANCH_IMAGE` | `""` | The name of the `DOCKER_IMAGE` on the default branch. See [Setting the default branch image](#setting-the-default-branch-image) for more details. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338877) in GitLab 14.5. | All | | `CS_DEFAULT_BRANCH_IMAGE` | `""` | The name of the `DOCKER_IMAGE` on the default branch. See [Setting the default branch image](#setting-the-default-branch-image) for more details. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338877) in GitLab 14.5. | All |
| `CS_DISABLE_DEPENDENCY_SCAN` | `"true"` | Disable Dependency Scanning for packages installed in the scanned image. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345434) in GitLab 14.6. | All |
| `CS_DOCKER_INSECURE` | `"false"` | Allow access to secure Docker registries using HTTPS without validating the certificates. | All | | `CS_DOCKER_INSECURE` | `"false"` | Allow access to secure Docker registries using HTTPS without validating the certificates. | All |
| `CS_REGISTRY_INSECURE` | `"false"` | Allow access to insecure registries (HTTP only). Should only be set to `true` when testing the image locally. Works with all scanners, but the registry must listen on port `80/tcp` for Trivy to work. | All | | `CS_REGISTRY_INSECURE` | `"false"` | Allow access to insecure registries (HTTP only). Should only be set to `true` when testing the image locally. Works with all scanners, but the registry must listen on port `80/tcp` for Trivy to work. | All |
| `CS_SEVERITY_THRESHOLD` | `UNKNOWN` | Severity level threshold. The scanner outputs vulnerabilities with severity level higher than or equal to this threshold. Supported levels are Unknown, Low, Medium, High, and Critical. | Trivy | | `CS_SEVERITY_THRESHOLD` | `UNKNOWN` | Severity level threshold. The scanner outputs vulnerabilities with severity level higher than or equal to this threshold. Supported levels are Unknown, Low, Medium, High, and Critical. | Trivy |

View File

@ -672,12 +672,9 @@ To disable group mentions:
> - [Inheritance and enforcement added](https://gitlab.com/gitlab-org/gitlab/-/issues/321724) in GitLab 13.11. > - [Inheritance and enforcement added](https://gitlab.com/gitlab-org/gitlab/-/issues/321724) in GitLab 13.11.
> - [Instance setting to enable by default added](https://gitlab.com/gitlab-org/gitlab/-/issues/255449) in GitLab 14.2. > - [Instance setting to enable by default added](https://gitlab.com/gitlab-org/gitlab/-/issues/255449) in GitLab 14.2.
Projects can be configured to be deleted either: [Delayed project deletion](../project/settings/index.md#delayed-project-deletion) can be enabled for groups. When enabled, projects in
the group are deleted after a period of delay. During this period, projects are in a read-only state and can be restored. The default
- Immediately. period is seven days but [is configurable at the instance level](../admin_area/settings/visibility_and_access_controls.md#default-deletion-delay).
- After a delayed interval. During this interval period, the projects are in a read-only state
and can be restored. The default interval period is seven days but
[is configurable](../admin_area/settings/visibility_and_access_controls.md#default-deletion-delay).
On self-managed GitLab, projects are deleted immediately by default. On self-managed GitLab, projects are deleted immediately by default.
In GitLab 14.2 and later, an administrator can In GitLab 14.2 and later, an administrator can

View File

@ -248,7 +248,7 @@ To delete a project, first navigate to the home page for that project.
1. Click **Delete project** 1. Click **Delete project**
1. Confirm this action by typing in the expected text. 1. Confirm this action by typing in the expected text.
Projects in personal namespaces are deleted immediately on request. For information on delayed deletion of projects in a group, please see [Enable delayed project deletion](../group/index.md#enable-delayed-project-deletion). Projects in personal namespaces are deleted immediately on request. For information on delayed deletion of projects in a group, please see [delayed project deletion](settings/index.md#delayed-project-deletion).
## Project settings ## Project settings

View File

@ -38,7 +38,8 @@ container_scanning:
artifacts: artifacts:
reports: reports:
container_scanning: gl-container-scanning-report.json container_scanning: gl-container-scanning-report.json
paths: [gl-container-scanning-report.json] dependency_scanning: gl-dependency-scanning-report.json
paths: [gl-container-scanning-report.json, gl-dependency-scanning-report.json]
dependencies: [] dependencies: []
script: script:
- gtcs scan - gtcs scan

View File

@ -1,5 +1,6 @@
import { GlTabs, GlTab } from '@gitlab/ui'; import { GlTabs, GlTab } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { mockTracking } from 'helpers/tracking_helper';
import ClustersMainView from '~/clusters_list/components/clusters_main_view.vue'; import ClustersMainView from '~/clusters_list/components/clusters_main_view.vue';
import InstallAgentModal from '~/clusters_list/components/install_agent_modal.vue'; import InstallAgentModal from '~/clusters_list/components/install_agent_modal.vue';
import { import {
@ -8,12 +9,15 @@ import {
CLUSTERS_TABS, CLUSTERS_TABS,
MAX_CLUSTERS_LIST, MAX_CLUSTERS_LIST,
MAX_LIST_COUNT, MAX_LIST_COUNT,
EVENT_LABEL_TABS,
EVENT_ACTIONS_CHANGE,
} from '~/clusters_list/constants'; } from '~/clusters_list/constants';
const defaultBranchName = 'default-branch'; const defaultBranchName = 'default-branch';
describe('ClustersMainViewComponent', () => { describe('ClustersMainViewComponent', () => {
let wrapper; let wrapper;
let trackingSpy;
const propsData = { const propsData = {
defaultBranchName, defaultBranchName,
@ -23,6 +27,7 @@ describe('ClustersMainViewComponent', () => {
wrapper = shallowMountExtended(ClustersMainView, { wrapper = shallowMountExtended(ClustersMainView, {
propsData, propsData,
}); });
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
}); });
afterEach(() => { afterEach(() => {
@ -71,6 +76,7 @@ describe('ClustersMainViewComponent', () => {
beforeEach(() => { beforeEach(() => {
findComponent().vm.$emit('changeTab', AGENT); findComponent().vm.$emit('changeTab', AGENT);
}); });
it('changes the tab', () => { it('changes the tab', () => {
expect(findTabs().attributes('value')).toBe('1'); expect(findTabs().attributes('value')).toBe('1');
}); });
@ -78,5 +84,13 @@ describe('ClustersMainViewComponent', () => {
it('passes correct max-agents param to the modal', () => { it('passes correct max-agents param to the modal', () => {
expect(findModal().props('maxAgents')).toBe(MAX_LIST_COUNT); expect(findModal().props('maxAgents')).toBe(MAX_LIST_COUNT);
}); });
it('sends the correct tracking event', () => {
findTabs().vm.$emit('input', 1);
expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS_CHANGE, {
label: EVENT_LABEL_TABS,
property: AGENT,
});
});
}); });
}); });

View File

@ -2,9 +2,18 @@ import { GlAlert, GlButton, GlFormInputGroup } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils'; import { createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { mockTracking } from 'helpers/tracking_helper';
import AvailableAgentsDropdown from '~/clusters_list/components/available_agents_dropdown.vue'; import AvailableAgentsDropdown from '~/clusters_list/components/available_agents_dropdown.vue';
import InstallAgentModal from '~/clusters_list/components/install_agent_modal.vue'; import InstallAgentModal from '~/clusters_list/components/install_agent_modal.vue';
import { I18N_AGENT_MODAL, MAX_LIST_COUNT } from '~/clusters_list/constants'; import {
I18N_AGENT_MODAL,
MAX_LIST_COUNT,
EVENT_LABEL_MODAL,
EVENT_ACTIONS_OPEN,
EVENT_ACTIONS_SELECT,
MODAL_TYPE_EMPTY,
MODAL_TYPE_REGISTER,
} from '~/clusters_list/constants';
import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql'; import getAgentsQuery from '~/clusters_list/graphql/queries/get_agents.query.graphql';
import getAgentConfigurations from '~/clusters_list/graphql/queries/agent_configurations.query.graphql'; import getAgentConfigurations from '~/clusters_list/graphql/queries/agent_configurations.query.graphql';
import createAgentMutation from '~/clusters_list/graphql/mutations/create_agent.mutation.graphql'; import createAgentMutation from '~/clusters_list/graphql/mutations/create_agent.mutation.graphql';
@ -34,6 +43,7 @@ const maxAgents = MAX_LIST_COUNT;
describe('InstallAgentModal', () => { describe('InstallAgentModal', () => {
let wrapper; let wrapper;
let apolloProvider; let apolloProvider;
let trackingSpy;
const configurations = [{ agentName: 'agent-name' }]; const configurations = [{ agentName: 'agent-name' }];
const apolloQueryResponse = { const apolloQueryResponse = {
@ -56,7 +66,7 @@ describe('InstallAgentModal', () => {
const findActionButton = () => findButtonByVariant('confirm'); const findActionButton = () => findButtonByVariant('confirm');
const findCancelButton = () => findButtonByVariant('default'); const findCancelButton = () => findButtonByVariant('default');
const findSecondaryButton = () => wrapper.findByTestId('agent-secondary-button'); const findSecondaryButton = () => wrapper.findByTestId('agent-secondary-button');
const findImage = () => wrapper.findByRole('img', { alt: I18N_AGENT_MODAL.install.altText }); const findImage = () => wrapper.findByRole('img', { alt: I18N_AGENT_MODAL.empty_state.altText });
const expectDisabledAttribute = (element, disabled) => { const expectDisabledAttribute = (element, disabled) => {
if (disabled) { if (disabled) {
@ -121,6 +131,7 @@ describe('InstallAgentModal', () => {
[getAgentConfigurations, jest.fn().mockResolvedValue(apolloQueryResponse)], [getAgentConfigurations, jest.fn().mockResolvedValue(apolloQueryResponse)],
]); ]);
createWrapper(); createWrapper();
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
}); });
afterEach(() => { afterEach(() => {
@ -129,7 +140,7 @@ describe('InstallAgentModal', () => {
}); });
describe('when agent configurations are present', () => { describe('when agent configurations are present', () => {
const i18n = I18N_AGENT_MODAL.register; const i18n = I18N_AGENT_MODAL.agent_registration;
describe('initial state', () => { describe('initial state', () => {
it('renders the dropdown for available agents', () => { it('renders the dropdown for available agents', () => {
@ -150,6 +161,14 @@ describe('InstallAgentModal', () => {
expect(findActionButton().text()).toBe(i18n.registerAgentButton); expect(findActionButton().text()).toBe(i18n.registerAgentButton);
expectDisabledAttribute(findActionButton(), true); expectDisabledAttribute(findActionButton(), true);
}); });
it('sends the event with the modalType', () => {
findModal().vm.$emit('show');
expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS_OPEN, {
label: EVENT_LABEL_MODAL,
property: MODAL_TYPE_REGISTER,
});
});
}); });
describe('an agent is selected', () => { describe('an agent is selected', () => {
@ -161,6 +180,12 @@ describe('InstallAgentModal', () => {
expect(findActionButton().isVisible()).toBe(true); expect(findActionButton().isVisible()).toBe(true);
expectDisabledAttribute(findActionButton(), false); expectDisabledAttribute(findActionButton(), false);
}); });
it('sends the correct tracking event', () => {
expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS_SELECT, {
label: EVENT_LABEL_MODAL,
});
});
}); });
describe('registering an agent', () => { describe('registering an agent', () => {
@ -247,7 +272,7 @@ describe('InstallAgentModal', () => {
}); });
describe('when there are no agent configurations present', () => { describe('when there are no agent configurations present', () => {
const i18n = I18N_AGENT_MODAL.install; const i18n = I18N_AGENT_MODAL.empty_state;
const apolloQueryEmptyResponse = { const apolloQueryEmptyResponse = {
data: { data: {
project: { project: {
@ -272,5 +297,13 @@ describe('InstallAgentModal', () => {
expect(findSecondaryButton().isVisible()).toBe(true); expect(findSecondaryButton().isVisible()).toBe(true);
expect(findSecondaryButton().text()).toBe(i18n.secondaryButton); expect(findSecondaryButton().text()).toBe(i18n.secondaryButton);
}); });
it('sends the event with the modalType', () => {
findModal().vm.$emit('show');
expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTIONS_OPEN, {
label: EVENT_LABEL_MODAL,
property: MODAL_TYPE_EMPTY,
});
});
}); });
}); });

View File

@ -31,9 +31,13 @@ module Database
# See https://gitlab.com/gitlab-org/gitlab/-/issues/339396 # See https://gitlab.com/gitlab-org/gitlab/-/issues/339396
return if sql.include?("DISABLE TRIGGER") || sql.include?("ENABLE TRIGGER") return if sql.include?("DISABLE TRIGGER") || sql.include?("ENABLE TRIGGER")
# PgQuery might fail in some cases due to limited nesting: tables = begin
# https://github.com/pganalyze/pg_query/issues/209 PgQuery.parse(sql).tables
tables = PgQuery.parse(sql).tables rescue PgQuery::ParseError
# PgQuery might fail in some cases due to limited nesting:
# https://github.com/pganalyze/pg_query/issues/209
return
end
schemas = ::Gitlab::Database::GitlabSchema.table_schemas(tables) schemas = ::Gitlab::Database::GitlabSchema.table_schemas(tables)

View File

@ -39,6 +39,15 @@ RSpec.describe Database::PreventCrossJoins do
expect { main_and_ci_query_allowlist_nested }.not_to raise_error expect { main_and_ci_query_allowlist_nested }.not_to raise_error
end end
end end
context 'when there is a parser error' do
it 'does not raise parse PGQuery::ParseError' do
# Since this is in an invalid query it still raises from ActiveRecord
# but this tests that we rescue the PGQuery::ParseError which would
# have otherwise raised first
expect { ApplicationRecord.connection.execute('SELECT SELECT FROM SELECT') }.to raise_error(ActiveRecord::StatementInvalid)
end
end
end end
end end