Commit 445ff2d0 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 8ce1730f 7a57eef4
import Vue from 'vue';
import { GlToast } from '@gitlab/ui';
import Translate from '~/vue_shared/translate';
import { parseBoolean } from '~/lib/utils/common_utils';
import SettingsApp from './components/group_settings_app.vue';
import { apolloProvider } from './graphql';
Vue.use(Translate);
Vue.use(GlToast);
export default () => {
const el = document.getElementById('js-packages-and-registries-settings');
......
<script>
import { GlSprintf, GlLink } from '@gitlab/ui';
import createFlash from '~/flash';
import { GlSprintf, GlLink, GlAlert } from '@gitlab/ui';
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
......@@ -26,6 +25,7 @@ export default {
PACKAGES_DOCS_PATH,
},
components: {
GlAlert,
GlSprintf,
GlLink,
SettingsBlock,
......@@ -49,6 +49,7 @@ export default {
return {
packageSettings: {},
errors: {},
alertMessage: null,
};
},
computed: {
......@@ -57,6 +58,9 @@ export default {
},
},
methods: {
dismissAlert() {
this.alertMessage = null;
},
updateSettings(payload) {
this.errors = {};
return this.$apollo
......@@ -76,9 +80,10 @@ export default {
})
.then(({ data }) => {
if (data.updateNamespacePackageSettings?.errors?.length > 0) {
createFlash({ message: ERROR_UPDATING_SETTINGS, type: 'warning' });
this.alertMessage = ERROR_UPDATING_SETTINGS;
} else {
createFlash({ message: SUCCESS_UPDATING_SETTINGS, type: 'success' });
this.dismissAlert();
this.$toast.show(SUCCESS_UPDATING_SETTINGS, { type: 'success' });
}
})
.catch((e) => {
......@@ -93,7 +98,7 @@ export default {
this.errors = { ...this.errors, [key]: message };
});
}
createFlash({ message: ERROR_UPDATING_SETTINGS, type: 'warning' });
this.alertMessage = ERROR_UPDATING_SETTINGS;
});
},
},
......@@ -102,6 +107,10 @@ export default {
<template>
<div>
<gl-alert v-if="alertMessage" variant="warning" class="gl-mt-4" @dismiss="dismissAlert">
{{ alertMessage }}
</gl-alert>
<settings-block :default-expanded="defaultExpanded">
<template #title> {{ $options.i18n.PACKAGE_SETTINGS_HEADER }}</template>
<template #description>
......
---
title: Track 5 mins production app template usage
merge_request: 53618
author:
type: other
......@@ -72,7 +72,7 @@ If you have project maintainer access you have the option to set up Service Desk
WARNING:
This email address can be used by anyone to create an issue on this project, regardless
of their access level to your GitLab instance. We recommend **putting this behind an alias** so it can be
of their access level to your GitLab instance. We recommend **putting this behind an alias on your email system** so it can be
changed if needed. We also recommend **[enabling Akismet](../../integration/akismet.md)** on your GitLab
instance to add spam checking to this service. Unblocked email spam would result in many spam
issues being created.
......
......@@ -5,6 +5,8 @@ require 'spec_helper'
require_migration!
RSpec.describe RemoveDuplicatedCsFindingsWithoutVulnerabilityId, :migration do
include MigrationHelpers::VulnerabilitiesFindingsHelper
let(:migration) { 'RemoveDuplicatedCsFindingsWithoutVulnerabilityId'}
let(:namespaces) { table(:namespaces) }
let(:notes) { table(:notes) }
......@@ -99,21 +101,16 @@ RSpec.describe RemoveDuplicatedCsFindingsWithoutVulnerabilityId, :migration do
end
def finding_params(primary_identifier_id, project_id)
attrs = attributes_for(:vulnerabilities_finding) # rubocop: disable RSpec/FactoriesInMigrationSpecs
{
custom_attributes = {
severity: 0,
confidence: 5,
report_type: 2,
project_id: project_id,
scanner_id: 6,
primary_identifier_id: primary_identifier_id,
project_fingerprint: attrs[:project_fingerprint],
location_fingerprint: Digest::SHA1.hexdigest(SecureRandom.hex(10)),
uuid: SecureRandom.uuid,
name: attrs[:name],
metadata_version: '1.3',
raw_metadata: attrs[:raw_metadata]
primary_identifier_id: primary_identifier_id
}
attributes_for_vulnerabilities_finding.merge(custom_attributes)
end
def create_identifier(number_of)
......
......@@ -5,6 +5,7 @@ module Gitlab::UsageDataCounters
REDIS_SLOT = 'ci_templates'.freeze
TEMPLATE_TO_EVENT = {
'5-Minute-Production-App.gitlab-ci.yml' => '5_min_production_app',
'Auto-DevOps.gitlab-ci.yml' => 'auto_devops',
'AWS/CF-Provision-and-Deploy-EC2.gitlab-ci.yml' => 'aws_cf_deploy_ec2',
'AWS/Deploy-ECS.gitlab-ci.yml' => 'aws_deploy_ecs',
......
......@@ -588,6 +588,11 @@
aggregation: weekly
feature_flag: usage_data_p_terraform_state_api_unique_users
# CI templates
- name: p_ci_templates_5_min_production_app
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
feature_flag: usage_data_track_ci_templates_unique_projects
- name: p_ci_templates_auto_devops
category: ci_templates
redis_slot: ci_templates
......
......@@ -92,12 +92,12 @@ then
echo "Merge request pipeline (detached) detected. Testing all files."
else
MERGE_BASE=$(git merge-base ${CI_MERGE_REQUEST_TARGET_BRANCH_SHA} ${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA})
if git diff --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" | grep -E "\.vale|\.markdownlint|lint-doc\.sh"
if git diff --diff-filter=d --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" | grep -E "\.vale|\.markdownlint|lint-doc\.sh"
then
MD_DOC_PATH=${MD_DOC_PATH:-doc}
echo "Vale, Markdownlint, or lint-doc.sh configuration changed. Testing all files."
else
MD_DOC_PATH=$(git diff --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" 'doc/*.md')
MD_DOC_PATH=$(git diff --diff-filter=d --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" -- 'doc/*.md')
if [ -n "${MD_DOC_PATH}" ]
then
echo -e "Merged results pipeline detected. Testing only the following files:\n${MD_DOC_PATH}"
......
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlSprintf, GlLink } from '@gitlab/ui';
import { GlSprintf, GlLink, GlAlert } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
import component from '~/packages_and_registries/settings/group/components/group_settings_app.vue';
import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
......@@ -30,6 +29,7 @@ const localVue = createLocalVue();
describe('Group Settings App', () => {
let wrapper;
let apolloProvider;
let show;
const defaultProvide = {
defaultExpanded: false,
......@@ -40,6 +40,7 @@ describe('Group Settings App', () => {
provide = defaultProvide,
resolver = jest.fn().mockResolvedValue(groupPackageSettingsMock),
mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock()),
data = {},
} = {}) => {
localVue.use(VueApollo);
......@@ -54,13 +55,27 @@ describe('Group Settings App', () => {
localVue,
apolloProvider,
provide,
data() {
return {
...data,
};
},
stubs: {
GlSprintf,
SettingsBlock,
},
mocks: {
$toast: {
show,
},
},
});
};
beforeEach(() => {
show = jest.fn();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
......@@ -70,6 +85,7 @@ describe('Group Settings App', () => {
const findDescription = () => wrapper.find('[data-testid="description"');
const findLink = () => wrapper.find(GlLink);
const findMavenSettings = () => wrapper.find(MavenSettings);
const findAlert = () => wrapper.find(GlAlert);
const waitForApolloQueryAndRender = async () => {
await waitForPromises();
......@@ -178,8 +194,7 @@ describe('Group Settings App', () => {
await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({
message: SUCCESS_UPDATING_SETTINGS,
expect(show).toHaveBeenCalledWith(SUCCESS_UPDATING_SETTINGS, {
type: 'success',
});
});
......@@ -211,6 +226,12 @@ describe('Group Settings App', () => {
});
describe('errors', () => {
const verifyAlert = () => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toBe(ERROR_UPDATING_SETTINGS);
expect(findAlert().props('variant')).toBe('warning');
};
it('mutation payload with root level errors', async () => {
// note this is a complex test that covers all the path around errors that are shown in the form
// it's one single it case, due to the expensive preparation and execution
......@@ -229,10 +250,8 @@ describe('Group Settings App', () => {
);
// general error message is shown
expect(createFlash).toHaveBeenCalledWith({
message: ERROR_UPDATING_SETTINGS,
type: 'warning',
});
verifyAlert();
emitSettingsUpdate();
......@@ -242,11 +261,11 @@ describe('Group Settings App', () => {
expect(findMavenSettings().props('mavenDuplicateExceptionRegexError')).toBe('');
});
it('mutation payload with local errors', async () => {
const mutationResolver = jest
.fn()
.mockResolvedValue(groupPackageSettingsMutationMock({ errors: ['foo'] }));
it.each`
type | mutationResolver
${'local'} | ${jest.fn().mockResolvedValue(groupPackageSettingsMutationMock({ errors: ['foo'] }))}
${'network'} | ${jest.fn().mockRejectedValue()}
`('mutation payload with $type error', async ({ mutationResolver }) => {
mountComponent({ mutationResolver });
await waitForApolloQueryAndRender();
......@@ -255,26 +274,35 @@ describe('Group Settings App', () => {
await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({
message: ERROR_UPDATING_SETTINGS,
type: 'warning',
});
verifyAlert();
});
it('shows an error in case of network error', async () => {
const mutationResolver = jest.fn().mockRejectedValue();
mountComponent({ mutationResolver });
it('a successful request dismisses the alert', async () => {
mountComponent({ data: { alertMessage: 'foo' } });
await waitForApolloQueryAndRender();
expect(findAlert().exists()).toBe(true);
emitSettingsUpdate();
await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({
message: ERROR_UPDATING_SETTINGS,
type: 'warning',
});
expect(findAlert().exists()).toBe(false);
});
it('dismiss event from alert dismiss it from the page', async () => {
mountComponent({ data: { alertMessage: 'foo' } });
await waitForApolloQueryAndRender();
expect(findAlert().exists()).toBe(true);
findAlert().vm.$emit('dismiss');
await wrapper.vm.$nextTick();
expect(findAlert().exists()).toBe(false);
});
});
});
......
# frozen_string_literal: true
module MigrationHelpers
module VulnerabilitiesFindingsHelper
def attributes_for_vulnerabilities_finding
uuid = SecureRandom.uuid
{
project_fingerprint: SecureRandom.hex(20),
location_fingerprint: Digest::SHA1.hexdigest(SecureRandom.hex(10)),
uuid: uuid,
name: "Vulnerability Finding #{uuid}",
metadata_version: '1.3',
raw_metadata: raw_metadata
}
end
def raw_metadata
{
"description" => "The cipher does not provide data integrity update 1",
"message" => "The cipher does not provide data integrity",
"cve" => "818bf5dacb291e15d9e6dc3c5ac32178:CIPHER",
"solution" => "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
"location" => {
"file" => "maven/src/main/java/com/gitlab/security_products/tests/App.java",
"start_line" => 29,
"end_line" => 29,
"class" => "com.gitlab.security_products.tests.App",
"method" => "insecureCypher"
},
"links" => [
{
"name" => "Cipher does not check for integrity first?",
"url" => "https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first"
}
],
"assets" => [
{
"type" => "postman",
"name" => "Test Postman Collection",
"url" => "http://localhost/test.collection"
}
],
"evidence" => {
"summary" => "Credit card detected",
"request" => {
"method" => "GET",
"url" => "http://goat:8080/WebGoat/logout",
"body" => nil,
"headers" => [
{
"name" => "Accept",
"value" => "*/*"
}
]
},
"response" => {
"reason_phrase" => "OK",
"status_code" => 200,
"body" => nil,
"headers" => [
{
"name" => "Content-Length",
"value" => "0"
}
]
},
"source" => {
"id" => "assert:Response Body Analysis",
"name" => "Response Body Analysis",
"url" => "htpp://hostname/documentation"
},
"supporting_messages" => [
{
"name" => "Origional",
"request" => {
"method" => "GET",
"url" => "http://goat:8080/WebGoat/logout",
"body" => "",
"headers" => [
{
"name" => "Accept",
"value" => "*/*"
}
]
}
},
{
"name" => "Recorded",
"request" => {
"method" => "GET",
"url" => "http://goat:8080/WebGoat/logout",
"body" => "",
"headers" => [
{
"name" => "Accept",
"value" => "*/*"
}
]
},
"response" => {
"reason_phrase" => "OK",
"status_code" => 200,
"body" => "",
"headers" => [
{
"name" => "Content-Length",
"value" => "0"
}
]
}
}
]
}
}
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment