Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-08-01 09:09:08 +00:00
parent 2bfa43cf3a
commit 46f35a6167
19 changed files with 390 additions and 286 deletions

View File

@ -18,9 +18,6 @@ Layout/HashAlignment:
- 'app/graphql/mutations/design_management/move.rb'
- 'app/graphql/mutations/issues/set_severity.rb'
- 'app/graphql/mutations/security/ci_configuration/base_security_analyzer.rb'
- 'app/graphql/resolvers/ci/template_resolver.rb'
- 'app/graphql/resolvers/projects_resolver.rb'
- 'app/graphql/resolvers/users_resolver.rb'
- 'app/graphql/types/access_level_type.rb'
- 'app/graphql/types/admin/analytics/usage_trends/measurement_type.rb'
- 'app/graphql/types/board_list_type.rb'

View File

@ -1,8 +1,10 @@
<script>
import $ from 'jquery';
import { GlDropdown, GlButton, GlIcon, GlForm, GlFormGroup } from '@gitlab/ui';
import { mapGetters, mapActions } from 'vuex';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { scrollToElement } from '~/lib/utils/common_utils';
import Autosave from '~/autosave';
export default {
components: {
@ -23,6 +25,11 @@ export default {
...mapGetters(['getNotesData', 'getNoteableData', 'noteableType', 'getCurrentUserLastNote']),
},
mounted() {
this.autosave = new Autosave(
$(this.$refs.textarea),
`submit_review_dropdown/${this.getNoteableData.id}`,
);
// We override the Bootstrap Vue click outside behaviour
// to allow for clicking in the autocomplete dropdowns
// without this override the submit dropdown will close
@ -47,6 +54,8 @@ export default {
await this.publishReview(noteData);
this.autosave.reset();
if (window.mrTabs && this.note) {
window.location.hash = `note_${this.getCurrentUserLastNote.id}`;
window.mrTabs.tabShown('show');

View File

@ -131,7 +131,9 @@ const lazyLaunchPopover = debounce((mountPopover, event) => {
let hasAddedLazyPopovers = false;
export default function addPopovers(mountPopover = (instance) => instance.$mount()) {
if (!hasAddedLazyPopovers) {
// The web request fails for anonymous users so we don't want to show the popover when the user is not signed in.
// https://gitlab.com/gitlab-org/gitlab/-/issues/351395#note_1039341458
if (window.gon?.current_user_id && !hasAddedLazyPopovers) {
document.addEventListener('mouseover', (event) => lazyLaunchPopover(mountPopover, event));
hasAddedLazyPopovers = true;
}

View File

@ -5,8 +5,11 @@ module Resolvers
class TemplateResolver < BaseResolver
type Types::Ci::TemplateType, null: true
argument :name, GraphQL::Types::String, required: true,
description: 'Name of the CI/CD template to search for. Template must be formatted as `Name.gitlab-ci.yml`.'
argument :name,
GraphQL::Types::String,
required: true,
description: 'Name of the CI/CD template to search for. ' \
'Template must be formatted as `Name.gitlab-ci.yml`.'
alias_method :project, :object

View File

@ -25,8 +25,8 @@ module Resolvers
description: 'Sort order of results.'
argument :topics, type: [GraphQL::Types::String],
required: false,
description: 'Filters projects by topics.'
required: false,
description: 'Filters projects by topics.'
def resolve(**args)
ProjectsFinder

View File

@ -12,7 +12,7 @@ module Resolvers
description: 'List of user Global IDs.'
argument :usernames, [GraphQL::Types::String], required: false,
description: 'List of usernames.'
description: 'List of usernames.'
argument :sort, Types::SortEnum,
description: 'Sort users by this criteria.',

View File

@ -1322,6 +1322,8 @@ module Ci
raise ArgumentError, 'pipeline not fully loaded'
end
return 0 unless created_at
(Time.current - created_at).ceil / 60
end

View File

@ -46,8 +46,8 @@ module DatabaseEventTracking
end
def filtered_record_attributes
attributes.select do |key, _value|
self.class::SNOWPLOW_ATTRIBUTES.include?(key)
end
attributes
.with_indifferent_access
.slice(*self.class::SNOWPLOW_ATTRIBUTES)
end
end

View File

@ -2,22 +2,18 @@
- page_title _('Monitor Settings')
- breadcrumb_title _('Monitor Settings')
.gl-alert.gl-alert-danger.gl-mb-5
- removal_epic_link_url = 'https://gitlab.com/groups/gitlab-org/-/epics/7188'
- removal_epic_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" class="gl-link">'.html_safe % { url: removal_epic_link_url }
- opstrace_link_url = 'https://gitlab.com/groups/gitlab-org/-/epics/6976'
- opstrace_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" class="gl-link">'.html_safe % { url: opstrace_link_url }
- link_end = '</a>'.html_safe
.gl-alert-container
= sprite_icon('error', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
.gl-alert-content
.gl-alert-title
= s_('Deprecations|Feature deprecation and removal')
.gl-alert-body
%p
= html_escape(s_('Deprecations|The metrics feature was deprecated in GitLab 14.7.'))
= html_escape(s_('Deprecations|The logs and tracing features were also deprecated in GitLab 14.7, and are %{removal_link_start} scheduled for removal %{link_end} in GitLab 15.0.')) % {removal_link_start: removal_epic_link_start, link_end: link_end }
= html_escape(s_('Deprecations|For information on a possible replacement, %{opstrace_link_start} learn more about Opstrace %{link_end}.')) % {opstrace_link_start: opstrace_link_start, link_end: link_end }
= render Pajamas::AlertComponent.new(variant: :danger,
dismissible: false,
title: s_('Deprecations|Feature deprecation and removal')) do |c|
= c.body do
- removal_epic_link_url = 'https://gitlab.com/groups/gitlab-org/-/epics/7188'
- removal_epic_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" class="gl-link">'.html_safe % { url: removal_epic_link_url }
- opstrace_link_url = 'https://gitlab.com/groups/gitlab-org/-/epics/6976'
- opstrace_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" class="gl-link">'.html_safe % { url: opstrace_link_url }
- link_end = '</a>'.html_safe
= html_escape(s_('Deprecations|The metrics feature was deprecated in GitLab 14.7.'))
= html_escape(s_('Deprecations|The logs and tracing features were also deprecated in GitLab 14.7, and are %{removal_link_start} scheduled for removal %{link_end} in GitLab 15.0.')) % {removal_link_start: removal_epic_link_start, link_end: link_end }
= html_escape(s_('Deprecations|For information on a possible replacement, %{opstrace_link_start} learn more about Opstrace %{link_end}.')) % {opstrace_link_start: opstrace_link_start, link_end: link_end }
= render 'projects/settings/operations/metrics_dashboard'
= render 'projects/settings/operations/error_tracking'

View File

@ -1,14 +0,0 @@
module.exports = {
source: {
include: ['app/assets/javascripts/'],
},
opts: {
template: 'node_modules/docdash',
destination: 'jsdoc/',
recurse: true,
},
docdash: {
search: true,
static: true,
},
};

View File

@ -75,6 +75,7 @@ at the group level.
> - Support for specifying multiple email domains [added](https://gitlab.com/gitlab-org/gitlab/-/issues/33143) in GitLab 13.1.
> - Support for restricting access to projects in the group [added](https://gitlab.com/gitlab-org/gitlab/-/issues/14004) in GitLab 14.1.2.
> - Support for restricting group memberships to groups with a subset of the allowed email domains [added](https://gitlab.com/gitlab-org/gitlab/-/issues/354791) in GitLab 15.1.1
You can prevent users with email addresses in specific domains from being added to a group and its projects.
@ -95,6 +96,13 @@ The most popular public email domains cannot be restricted, such as:
- `hotmail.com`, `hotmail.co.uk`, `hotmail.fr`
- `msn.com`, `live.com`, `outlook.com`
When you share a group, both the source and target namespaces must allow the domains of the members' email addresses.
NOTE:
Removing a domain from the **Restrict membership by email** list does not remove the users with this email domain from the groups and projects under this group.
Also, if you share a group or project with another group, the target group can add more email domains to its list that are not in the list of the source group.
Hence, this feature does not ensure that the current members always conform to the **Restrict membership by email** list.
## Prevent group sharing outside the group hierarchy
You can configure a top-level group so its subgroups and projects

View File

@ -16,7 +16,6 @@
"jest:ci": "jest --config jest.config.js --ci --coverage --testSequencer ./scripts/frontend/parallel_ci_sequencer.js",
"jest:ci:minimal": "jest --config jest.config.js --ci --coverage --findRelatedTests $(cat tmp/changed_files.txt) --passWithNoTests --testSequencer ./scripts/frontend/parallel_ci_sequencer.js",
"jest:integration": "jest --config jest.config.integration.js",
"jsdoc": "jsdoc -c config/jsdocs.config.js",
"lint:eslint": "node scripts/frontend/eslint.js",
"lint:eslint:fix": "node scripts/frontend/eslint.js --fix",
"lint:eslint:all": "node scripts/frontend/eslint.js .",
@ -212,7 +211,6 @@
"cheerio": "^1.0.0-rc.9",
"commander": "^2.20.3",
"custom-jquery-matchers": "^2.1.0",
"docdash": "^1.0.2",
"eslint": "7.32.0",
"eslint-import-resolver-jest": "3.0.2",
"eslint-import-resolver-webpack": "0.13.2",
@ -231,10 +229,7 @@
"jest-raw-loader": "^1.0.1",
"jest-transform-graphql": "^2.1.0",
"jest-util": "^26.5.2",
"jsdoc": "^3.5.5",
"jsdoc-vue": "^1.0.0",
"markdownlint-cli": "0.31.0",
"md5": "^2.2.1",
"miragejs": "^0.1.40",
"mock-apollo-client": "1.2.0",
"nodemon": "^2.0.19",

View File

@ -40,8 +40,6 @@ module QA
find_element(:status_badge).text
end
private
def completed?(timeout: 60)
wait_until(reload: false, sleep_interval: 3.0, max_duration: timeout) do
COMPLETED_STATUSES.include?(status_badge)

View File

@ -0,0 +1,175 @@
# frozen_string_literal: true
module QA
# TODO: remove feature flag upon rollout completion
# FF rollout issue: https://gitlab.com/gitlab-org/gitlab/-/issues/363186
RSpec.describe 'Verify', :runner, feature_flag: {
name: 'ci_docker_image_pull_policy',
scope: :global
} do
describe 'Pipeline with image:pull_policy' do
let(:runner_name) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
let(:job_name) { "test-job-#{pull_policies.join('-')}" }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'pipeline-with-image-pull-policy'
end
end
let!(:runner) do
Resource::Runner.fabricate! do |runner|
runner.project = project
runner.name = runner_name
runner.tags = [runner_name]
runner.executor = :docker
end
end
before do
Runtime::Feature.enable(:ci_docker_image_pull_policy)
# Give the feature some time to switch
sleep(30)
update_runner_policy(allowed_policies)
add_ci_file
Flow::Login.sign_in
project.visit!
Flow::Pipeline.visit_latest_pipeline
end
after do
Runtime::Feature.disable(:ci_docker_image_pull_policy)
runner.remove_via_api!
end
context 'when policy is allowed' do
let(:allowed_policies) { %w[if-not-present always never] }
where do
{
'with [always] policy' => {
pull_policies: %w[always],
pull_image: true,
message: 'Pulling docker image ruby:2.6',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/367154'
},
'with [always if-not-present] policies' => {
pull_policies: %w[always if-not-present],
pull_image: true,
message: 'Pulling docker image ruby:2.6',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368857'
},
'with [if-not-present] policy' => {
pull_policies: %w[if-not-present],
pull_image: true,
message: 'Using locally found image version due to "if-not-present" pull policy',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368858'
},
'with [never] policy' => {
pull_policies: %w[never],
pull_image: false,
message: 'Pulling docker image',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368859'
}
}
end
with_them do
it 'applies pull policy in job correctly', testcase: params[:testcase] do
visit_job
if pull_image
expect(job_log).to have_content(message),
"Expected to find #{message} in #{job_log}, but didn't."
else
expect(job_log).not_to have_content(message),
"Found #{message} in #{job_log}, but didn't expect to."
end
end
end
end
context 'when policy is not allowed' do
let(:allowed_policies) { %w[never] }
let(:pull_policies) { %w[always] }
let(:message) do
'ERROR: Preparation failed: the configured PullPolicies ([always])'\
' are not allowed by AllowedPullPolicies ([never])'
end
it(
'fails job with policy not allowed message',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368853'
) do
visit_job
expect(job_log).to have_content(message),
"Expected to find #{message} in #{job_log}, but didn't."
end
end
private
def update_runner_policy(allowed_policies)
Runtime::Logger.info('Updating runner config to allow pull policies...')
# Copy config.toml file from docker to local
# Update local file with allowed_pull_policies config
# Copy file with new content back to docker
tempdir = Tempfile.new('config.toml')
QA::Service::Shellout.shell("docker cp #{runner_name}:/etc/gitlab-runner/config.toml #{tempdir.path}")
File.open(tempdir.path, 'a') do |f|
f << %Q[ allowed_pull_policies = #{allowed_policies}\n]
end
QA::Service::Shellout.shell("docker cp #{tempdir.path} #{runner_name}:/etc/gitlab-runner/config.toml")
tempdir.close!
# Give runner some time to pick up new configuration
sleep(30)
end
def add_ci_file
Resource::Repository::Commit.fabricate_via_api! do |commit|
commit.project = project
commit.commit_message = 'Add .gitlab-ci.yml'
commit.add_files(
[
{
file_path: '.gitlab-ci.yml',
content: <<~YAML
default:
image: ruby:2.6
tags: [#{runner_name}]
#{job_name}:
script: echo "Using pull policies #{pull_policies}"
image:
name: ruby:2.6
pull_policy: #{pull_policies}
YAML
}
]
)
end
end
def visit_job
Page::Project::Pipeline::Show.perform do |show|
Support::Waiter.wait_until { show.completed? }
show.click_job(job_name)
end
end
def job_log
Page::Project::Job::Show.perform(&:output)
end
end
end
end

View File

@ -10,6 +10,8 @@ jest.mock('~/api/user_api', () => ({
}));
describe('User Popovers', () => {
let origGon;
const fixtureTemplate = 'merge_requests/merge_request_with_mentions.html';
const selector = '.js-user-link[data-user], .js-user-link[data-user-id]';
@ -39,7 +41,7 @@ describe('User Popovers', () => {
el.dispatchEvent(event);
};
beforeEach(() => {
const setupTestSubject = () => {
loadHTMLFixture(fixtureTemplate);
const usersCacheSpy = () => Promise.resolve(dummyUser);
@ -56,147 +58,179 @@ describe('User Popovers', () => {
document.body.appendChild(mountingRoot);
popoverInstance.$mount(mountingRoot);
});
};
beforeEach(() => {
origGon = window.gon;
window.gon = {};
});
afterEach(() => {
resetHTMLFixture();
window.gon = origGon;
});
describe('shows a placeholder popover on hover', () => {
let linksWithUsers;
describe('when signed out', () => {
beforeEach(() => {
linksWithUsers = findFixtureLinks();
setupTestSubject();
});
it('does not show a placeholder popover on hover', () => {
const linksWithUsers = findFixtureLinks();
linksWithUsers.forEach((el) => {
triggerEvent('mouseover', el);
});
expect(findPopovers().length).toBe(0);
});
});
describe('when signed in', () => {
beforeEach(() => {
window.gon.current_user_id = 7;
setupTestSubject();
});
it('for initial links', () => {
expect(findPopovers().length).toBe(linksWithUsers.length);
afterEach(() => {
resetHTMLFixture();
});
it('for elements added after initial load', async () => {
const addedLinks = [createUserLink(), createUserLink()];
addedLinks.forEach((link) => {
document.body.appendChild(link);
describe('shows a placeholder popover on hover', () => {
let linksWithUsers;
beforeEach(() => {
linksWithUsers = findFixtureLinks();
linksWithUsers.forEach((el) => {
triggerEvent('mouseover', el);
});
});
jest.runOnlyPendingTimers();
addedLinks.forEach((link) => {
triggerEvent('mouseover', link);
it('for initial links', () => {
expect(findPopovers().length).toBe(linksWithUsers.length);
});
expect(findPopovers().length).toBe(linksWithUsers.length + addedLinks.length);
});
});
it('for elements added after initial load', async () => {
const addedLinks = [createUserLink(), createUserLink()];
addedLinks.forEach((link) => {
document.body.appendChild(link);
});
it('does not initialize the popovers for group references', async () => {
const [groupLink] = Array.from(document.querySelectorAll('.js-user-link[data-group]'));
jest.runOnlyPendingTimers();
triggerEvent('mouseover', groupLink);
jest.runOnlyPendingTimers();
addedLinks.forEach((link) => {
triggerEvent('mouseover', link);
});
expect(findPopovers().length).toBe(0);
});
it('does not initialize the popovers for @all references', async () => {
const [projectLink] = Array.from(document.querySelectorAll('.js-user-link[data-project]'));
triggerEvent('mouseover', projectLink);
jest.runOnlyPendingTimers();
expect(findPopovers().length).toBe(0);
});
it('does not initialize the user popovers twice for the same element', async () => {
const [firstUserLink] = findFixtureLinks();
triggerEvent('mouseover', firstUserLink);
jest.runOnlyPendingTimers();
triggerEvent('mouseleave', firstUserLink);
jest.runOnlyPendingTimers();
triggerEvent('mouseover', firstUserLink);
jest.runOnlyPendingTimers();
expect(findPopovers().length).toBe(1);
});
describe('when user link emits mouseenter event with empty user cache', () => {
let userLink;
beforeEach(() => {
UsersCache.retrieveById.mockReset();
[userLink] = findFixtureLinks();
triggerEvent('mouseover', userLink);
expect(findPopovers().length).toBe(linksWithUsers.length + addedLinks.length);
});
});
it('populates popover with preloaded user data', () => {
const { name, userId, username } = userLink.dataset;
expect(userLink.user).toEqual(
expect.objectContaining({
name,
userId,
username,
}),
);
});
});
describe('when user link emits mouseenter event', () => {
let userLink;
beforeEach(() => {
[userLink] = findFixtureLinks();
triggerEvent('mouseover', userLink);
});
it('removes title attribute from user links', () => {
expect(userLink.getAttribute('title')).toBeFalsy();
expect(userLink.dataset.originalTitle).toBeFalsy();
});
it('fetches user info and status from the user cache', () => {
const { userId } = userLink.dataset;
expect(UsersCache.retrieveById).toHaveBeenCalledWith(userId);
expect(UsersCache.retrieveStatusById).toHaveBeenCalledWith(userId);
});
it('removes aria-describedby attribute from the user link on mouseleave', () => {
userLink.setAttribute('aria-describedby', 'popover');
triggerEvent('mouseleave', userLink);
expect(userLink.getAttribute('aria-describedby')).toBe(null);
});
it('updates toggle follow button and `UsersCache` when toggle follow button is clicked', async () => {
const [firstPopover] = findPopovers();
const withinFirstPopover = within(firstPopover);
const findFollowButton = () => withinFirstPopover.queryByRole('button', { name: 'Follow' });
const findUnfollowButton = () =>
withinFirstPopover.queryByRole('button', { name: 'Unfollow' });
it('does not initialize the popovers for group references', async () => {
const [groupLink] = Array.from(document.querySelectorAll('.js-user-link[data-group]'));
triggerEvent('mouseover', groupLink);
jest.runOnlyPendingTimers();
const { userId } = document.querySelector(selector).dataset;
expect(findPopovers().length).toBe(0);
});
triggerEvent('click', findFollowButton());
it('does not initialize the popovers for @all references', async () => {
const [projectLink] = Array.from(document.querySelectorAll('.js-user-link[data-project]'));
await waitForPromises();
triggerEvent('mouseover', projectLink);
jest.runOnlyPendingTimers();
expect(findUnfollowButton()).not.toBe(null);
expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: true });
expect(findPopovers().length).toBe(0);
});
triggerEvent('click', findUnfollowButton());
it('does not initialize the user popovers twice for the same element', async () => {
const [firstUserLink] = findFixtureLinks();
triggerEvent('mouseover', firstUserLink);
jest.runOnlyPendingTimers();
triggerEvent('mouseleave', firstUserLink);
jest.runOnlyPendingTimers();
triggerEvent('mouseover', firstUserLink);
jest.runOnlyPendingTimers();
await waitForPromises();
expect(findPopovers().length).toBe(1);
});
expect(findFollowButton()).not.toBe(null);
expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: false });
describe('when user link emits mouseenter event with empty user cache', () => {
let userLink;
beforeEach(() => {
UsersCache.retrieveById.mockReset();
[userLink] = findFixtureLinks();
triggerEvent('mouseover', userLink);
});
it('populates popover with preloaded user data', () => {
const { name, userId, username } = userLink.dataset;
expect(userLink.user).toEqual(
expect.objectContaining({
name,
userId,
username,
}),
);
});
});
describe('when user link emits mouseenter event', () => {
let userLink;
beforeEach(() => {
[userLink] = findFixtureLinks();
triggerEvent('mouseover', userLink);
});
it('removes title attribute from user links', () => {
expect(userLink.getAttribute('title')).toBeFalsy();
expect(userLink.dataset.originalTitle).toBeFalsy();
});
it('fetches user info and status from the user cache', () => {
const { userId } = userLink.dataset;
expect(UsersCache.retrieveById).toHaveBeenCalledWith(userId);
expect(UsersCache.retrieveStatusById).toHaveBeenCalledWith(userId);
});
it('removes aria-describedby attribute from the user link on mouseleave', () => {
userLink.setAttribute('aria-describedby', 'popover');
triggerEvent('mouseleave', userLink);
expect(userLink.getAttribute('aria-describedby')).toBe(null);
});
it('updates toggle follow button and `UsersCache` when toggle follow button is clicked', async () => {
const [firstPopover] = findPopovers();
const withinFirstPopover = within(firstPopover);
const findFollowButton = () => withinFirstPopover.queryByRole('button', { name: 'Follow' });
const findUnfollowButton = () =>
withinFirstPopover.queryByRole('button', { name: 'Unfollow' });
jest.runOnlyPendingTimers();
const { userId } = document.querySelector(selector).dataset;
triggerEvent('click', findFollowButton());
await waitForPromises();
expect(findUnfollowButton()).not.toBe(null);
expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: true });
triggerEvent('click', findUnfollowButton());
await waitForPromises();
expect(findFollowButton()).not.toBe(null);
expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: false });
});
});
});
});

View File

@ -5339,6 +5339,16 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
expect(pipeline.age_in_minutes).to eq 120
end
end
context 'when pipeline has no created_at' do
before do
pipeline.update!(created_at: nil)
end
it 'returns zero' do
expect(pipeline.age_in_minutes).to eq 0
end
end
end
context 'when pipeline has been loaded without all attributes' do

View File

@ -73,15 +73,17 @@ RSpec.describe Projects::TransferService do
create(:group, :nested).tap { |g| g.add_owner(user) }
end
let(:project) { create(:project, namespace: group) }
it 'publishes a ProjectTransferedEvent' do
expect { execute_transfer }
.to publish_event(Projects::ProjectTransferedEvent)
.with(
project_id: kind_of(Numeric),
project_id: project.id,
old_namespace_id: group.id,
old_root_namespace_id: group.parent_id,
old_root_namespace_id: group.root_ancestor.id,
new_namespace_id: target.id,
new_root_namespace_id: target.parent_id
new_root_namespace_id: target.root_ancestor.id
)
end
end

View File

@ -14,6 +14,8 @@
# - **extra
shared_examples 'Snowplow event tracking' do
let(:extra) { {} }
it 'is not emitted if FF is disabled' do
stub_feature_flags(feature_flag_name => false)
@ -23,8 +25,6 @@ shared_examples 'Snowplow event tracking' do
end
it 'is emitted' do
extra ||= {}
params = {
category: category,
action: action,
@ -32,9 +32,8 @@ shared_examples 'Snowplow event tracking' do
user: try(:user),
project: try(:project),
label: try(:label),
property: try(:property),
**extra
}.compact
property: try(:property)
}.compact.merge(extra)
subject

124
yarn.lock
View File

@ -2870,11 +2870,6 @@ babel-preset-jest@^26.5.0:
babel-plugin-jest-hoist "^26.5.0"
babel-preset-current-node-syntax "^0.1.3"
babylon@7.0.0-beta.19:
version "7.0.0-beta.19"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.19.tgz#e928c7e807e970e0536b078ab3e0c48f9e052503"
integrity sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==
backo2@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
@ -2933,7 +2928,7 @@ binaryextensions@2:
resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.1.tgz#3209a51ca4a4ad541a3b8d3d6a6d5b83a2485935"
integrity sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA==
bluebird@^3.1.1, bluebird@^3.5.5, bluebird@~3.5.0:
bluebird@^3.1.1, bluebird@^3.5.5:
version "3.5.5"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
@ -3279,13 +3274,6 @@ capture-exit@^2.0.0:
dependencies:
rsvp "^4.8.4"
catharsis@~0.8.9:
version "0.8.9"
resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.8.9.tgz#98cc890ca652dd2ef0e70b37925310ff9e90fc8b"
integrity sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=
dependencies:
underscore-contrib "~0.3.0"
ccount@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5"
@ -3318,11 +3306,6 @@ character-entities@^2.0.0:
resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.1.tgz#98724833e1e27990dee0bd0f2b8a859c3476aac7"
integrity sha512-OzmutCf2Kmc+6DrFrrPS8/tDh2+DpnrfzdICHWhcVC9eOd0N1PXmQEE1a8iM4IziIAG+8tmTq3K+oo0ubH6RRQ==
charenc@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
cheerio-select@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.4.0.tgz#3a16f21e37a2ef0f211d6d1aa4eff054bb22cdc9"
@ -3866,11 +3849,6 @@ cross-undici-fetch@^0.1.19:
undici "^5.0.0"
web-streams-polyfill "^3.2.0"
crypt@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
crypto-browserify@^3.11.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
@ -4770,11 +4748,6 @@ dns-packet@^5.2.2:
dependencies:
"@leichtgewicht/ip-codec" "^2.0.1"
docdash@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/docdash/-/docdash-1.0.2.tgz#0449a8f6bb247f563020b78a5485dea95ae2e094"
integrity sha512-IEM57bWPLtVXpUeCKbiGvHsHtW9O9ZiiBPfeQDAZ7JdQiAF3aNWQoJ3e/+uJ63lHO009yn0tbJjtKpXJ2AURCQ==
doctrine@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
@ -5078,7 +5051,7 @@ escape-html@~1.0.3:
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
escape-string-regexp@^1.0.5, escape-string-regexp@~1.0.5:
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
@ -6104,7 +6077,7 @@ good-listener@^1.2.2:
dependencies:
delegate "^3.1.2"
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.4, graceful-fs@^4.2.6:
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4, graceful-fs@^4.2.6:
version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
@ -6756,7 +6729,7 @@ is-boolean-object@^1.1.0:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
is-buffer@^1.1.5, is-buffer@~1.1.1:
is-buffer@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
@ -7549,36 +7522,6 @@ js-yaml@^4.1.0:
dependencies:
argparse "^2.0.1"
js2xmlparser@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-3.0.0.tgz#3fb60eaa089c5440f9319f51760ccd07e2499733"
integrity sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=
dependencies:
xmlcreate "^1.0.1"
jsdoc-vue@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/jsdoc-vue/-/jsdoc-vue-1.0.0.tgz#ff3ac1ba6bc4a74079bb79058a7bf0066e346235"
integrity sha1-/zrBumvEp0B5u3kFinvwBm40YjU=
jsdoc@^3.5.5:
version "3.5.5"
resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.5.5.tgz#484521b126e81904d632ff83ec9aaa096708fa4d"
integrity sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==
dependencies:
babylon "7.0.0-beta.19"
bluebird "~3.5.0"
catharsis "~0.8.9"
escape-string-regexp "~1.0.5"
js2xmlparser "~3.0.0"
klaw "~2.0.0"
marked "~0.3.6"
mkdirp "~0.5.1"
requizzle "~0.2.1"
strip-json-comments "~2.0.1"
taffydb "2.6.2"
underscore "~1.8.3"
jsdom@^16.4.0:
version "16.7.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710"
@ -7730,13 +7673,6 @@ klaw-sync@^6.0.0:
dependencies:
graceful-fs "^4.1.11"
klaw@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/klaw/-/klaw-2.0.0.tgz#59c128e0dc5ce410201151194eeb9cbf858650f6"
integrity sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=
dependencies:
graceful-fs "^4.1.9"
kleur@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
@ -8142,7 +8078,7 @@ markdownlint@~0.25.1:
dependencies:
markdown-it "12.3.2"
marked@^0.3.12, marked@~0.3.6:
marked@^0.3.12:
version "0.3.19"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790"
integrity sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==
@ -8165,15 +8101,6 @@ md5.js@^1.3.4:
hash-base "^3.0.0"
inherits "^2.0.1"
md5@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=
dependencies:
charenc "~0.0.1"
crypt "~0.0.1"
is-buffer "~1.1.1"
mdast-util-definitions@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.0.tgz#b6d10ef00a3c4cf191e8d9a5fa58d7f4a366f817"
@ -8890,7 +8817,7 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
mkdirp@^0.5.1, mkdirp@^0.5.3:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@ -10384,13 +10311,6 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
requizzle@~0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.1.tgz#6943c3530c4d9a7e46f1cddd51c158fc670cdbde"
integrity sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=
dependencies:
underscore "~1.6.0"
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@ -11207,11 +11127,6 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
style-loader@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c"
@ -11384,11 +11299,6 @@ table@^6.0.9, table@^6.8.0:
string-width "^4.2.3"
strip-ansi "^6.0.1"
taffydb@2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268"
integrity sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=
tapable@^0.1.8:
version "0.1.10"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4"
@ -11776,23 +11686,6 @@ undefsafe@^2.0.5:
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
underscore-contrib@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/underscore-contrib/-/underscore-contrib-0.3.0.tgz#665b66c24783f8fa2b18c9f8cbb0e2c7d48c26c7"
integrity sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=
dependencies:
underscore "1.6.0"
underscore@1.6.0, underscore@~1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
integrity sha1-izixDKze9jM3uLJOT/htRa6lKag=
underscore@~1.8.3:
version "1.8.3"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=
undici@^5.0.0:
version "5.8.0"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.8.0.tgz#dec9a8ccd90e5a1d81d43c0eab6503146d649a4f"
@ -12596,11 +12489,6 @@ xmlchars@^2.2.0:
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
xmlcreate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-1.0.2.tgz#fa6bf762a60a413fb3dd8f4b03c5b269238d308f"
integrity sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=
xtend@^4.0.0, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"