Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-09-15 21:11:12 +00:00
parent 27d1ed4ddf
commit a7aff3e0e4
38 changed files with 368 additions and 81 deletions

View File

@ -1,5 +1,5 @@
<script>
import { GlButton, GlFormGroup, GlFormCheckbox } from '@gitlab/ui';
import { GlButton, GlFormGroup, GlFormCheckbox, GlFormRadioGroup, GlFormRadio } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
import ErrorTrackingForm from './error_tracking_form.vue';
import ProjectDropdown from './project_dropdown.vue';
@ -10,6 +10,8 @@ export default {
GlButton,
GlFormCheckbox,
GlFormGroup,
GlFormRadioGroup,
GlFormRadio,
ProjectDropdown,
},
props: {
@ -22,6 +24,10 @@ export default {
type: String,
required: true,
},
initialIntegrated: {
type: String,
required: true,
},
initialProject: {
type: String,
required: false,
@ -49,12 +55,20 @@ export default {
'isProjectInvalid',
'projectSelectionLabel',
]),
...mapState(['enabled', 'projects', 'selectedProject', 'settingsLoading', 'token']),
...mapState([
'enabled',
'integrated',
'projects',
'selectedProject',
'settingsLoading',
'token',
]),
},
created() {
this.setInitialState({
apiHost: this.initialApiHost,
enabled: this.initialEnabled,
integrated: this.initialIntegrated,
project: this.initialProject,
token: this.initialToken,
listProjectsEndpoint: this.listProjectsEndpoint,
@ -62,7 +76,13 @@ export default {
});
},
methods: {
...mapActions(['setInitialState', 'updateEnabled', 'updateSelectedProject', 'updateSettings']),
...mapActions([
'setInitialState',
'updateEnabled',
'updateIntegrated',
'updateSelectedProject',
'updateSettings',
]),
handleSubmit() {
this.updateSettings();
},
@ -76,27 +96,44 @@ export default {
:label="s__('ErrorTracking|Enable error tracking')"
label-for="error-tracking-enabled"
>
<gl-form-checkbox
id="error-tracking-enabled"
:checked="enabled"
@change="updateEnabled($event)"
>
<gl-form-checkbox id="error-tracking-enabled" :checked="enabled" @change="updateEnabled">
{{ s__('ErrorTracking|Active') }}
</gl-form-checkbox>
</gl-form-group>
<error-tracking-form />
<div class="form-group">
<project-dropdown
:has-projects="hasProjects"
:invalid-project-label="invalidProjectLabel"
:is-project-invalid="isProjectInvalid"
:dropdown-label="dropdownLabel"
:project-selection-label="projectSelectionLabel"
:projects="projects"
:selected-project="selectedProject"
:token="token"
@select-project="updateSelectedProject"
/>
<gl-form-group
:label="s__('ErrorTracking|Error tracking backend')"
data-testid="tracking-backend-settings"
>
<gl-form-radio-group name="explicit" :checked="integrated" @change="updateIntegrated">
<gl-form-radio name="error-tracking-integrated" :value="false">
{{ __('Sentry') }}
<template #help>
{{ __('Requires you to deploy or set up cloud-hosted Sentry.') }}
</template>
</gl-form-radio>
<gl-form-radio name="error-tracking-integrated" :value="true">
{{ __('GitLab') }}
<template #help>
{{ __('Uses GitLab as a lightweight alternative to Sentry.') }}
</template>
</gl-form-radio>
</gl-form-radio-group>
</gl-form-group>
<div v-if="!integrated" class="js-sentry-setting-form" data-testid="sentry-setting-form">
<error-tracking-form />
<div class="form-group">
<project-dropdown
:has-projects="hasProjects"
:invalid-project-label="invalidProjectLabel"
:is-project-invalid="isProjectInvalid"
:dropdown-label="dropdownLabel"
:project-selection-label="projectSelectionLabel"
:projects="projects"
:selected-project="selectedProject"
:token="token"
@select-project="updateSelectedProject"
/>
</div>
</div>
<gl-button
:disabled="settingsLoading"

View File

@ -5,7 +5,15 @@ import createStore from './store';
export default () => {
const formContainerEl = document.querySelector('.js-error-tracking-form');
const {
dataset: { apiHost, enabled, project, token, listProjectsEndpoint, operationsSettingsEndpoint },
dataset: {
apiHost,
enabled,
integrated,
project,
token,
listProjectsEndpoint,
operationsSettingsEndpoint,
},
} = formContainerEl;
return new Vue({
@ -16,6 +24,7 @@ export default () => {
props: {
initialApiHost: apiHost,
initialEnabled: enabled,
initialIntegrated: integrated,
initialProject: project,
initialToken: token,
listProjectsEndpoint,

View File

@ -79,6 +79,10 @@ export const updateEnabled = ({ commit }, enabled) => {
commit(types.UPDATE_ENABLED, enabled);
};
export const updateIntegrated = ({ commit }, integrated) => {
commit(types.UPDATE_INTEGRATED, integrated);
};
export const updateToken = ({ commit }, token) => {
commit(types.UPDATE_TOKEN, token);
commit(types.RESET_CONNECT);

View File

@ -6,6 +6,7 @@ export const UPDATE_API_HOST = 'UPDATE_API_HOST';
export const UPDATE_CONNECT_ERROR = 'UPDATE_CONNECT_ERROR';
export const UPDATE_CONNECT_SUCCESS = 'UPDATE_CONNECT_SUCCESS';
export const UPDATE_ENABLED = 'UPDATE_ENABLED';
export const UPDATE_INTEGRATED = 'UPDATE_INTEGRATED';
export const UPDATE_SELECTED_PROJECT = 'UPDATE_SELECTED_PROJECT';
export const UPDATE_SETTINGS_LOADING = 'UPDATE_SETTINGS_LOADING';
export const UPDATE_TOKEN = 'UPDATE_TOKEN';

View File

@ -20,9 +20,18 @@ export default {
},
[types.SET_INITIAL_STATE](
state,
{ apiHost, enabled, project, token, listProjectsEndpoint, operationsSettingsEndpoint },
{
apiHost,
enabled,
integrated,
project,
token,
listProjectsEndpoint,
operationsSettingsEndpoint,
},
) {
state.enabled = parseBoolean(enabled);
state.integrated = parseBoolean(integrated);
state.apiHost = apiHost;
state.token = token;
state.listProjectsEndpoint = listProjectsEndpoint;
@ -38,6 +47,9 @@ export default {
[types.UPDATE_ENABLED](state, enabled) {
state.enabled = enabled;
},
[types.UPDATE_INTEGRATED](state, integrated) {
state.integrated = integrated;
},
[types.UPDATE_TOKEN](state, token) {
state.token = token;
},

View File

@ -1,6 +1,7 @@
export default () => ({
apiHost: '',
enabled: false,
integrated: false,
token: '',
projects: [],
isLoadingProjects: false,

View File

@ -1,6 +1,12 @@
export const projectKeys = ['name', 'organizationName', 'organizationSlug', 'slug'];
export const transformFrontendSettings = ({ apiHost, enabled, token, selectedProject }) => {
export const transformFrontendSettings = ({
apiHost,
enabled,
integrated,
token,
selectedProject,
}) => {
const project = selectedProject
? {
slug: selectedProject.slug,
@ -10,7 +16,7 @@ export const transformFrontendSettings = ({ apiHost, enabled, token, selectedPro
}
: null;
return { api_host: apiHost || null, enabled, token: token || null, project };
return { api_host: apiHost || null, enabled, integrated, token: token || null, project };
};
export const getDisplayName = (project) => `${project.organizationName} | ${project.slug}`;

View File

@ -136,6 +136,7 @@ module Projects
error_tracking_setting_attributes: [
:enabled,
:integrated,
:api_host,
:token,
project: [:slug, :name, :organization_slug, :organization_name]

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
class SecurityReportsMrWidgetPromptExperiment < ApplicationExperiment # rubocop:disable Gitlab/NamespacedClass
def publish(_result = nil)
super
publish_to_database
end
# This is a purely client side experiment, and since we don't have a nicer
# way to define variants yet, we define them here.
def candidate_behavior
end
end

View File

@ -94,6 +94,7 @@ module Projects
}
}
params[:error_tracking_setting_attributes][:token] = settings[:token] unless /\A\*+\z/.match?(settings[:token]) # Don't update token if we receive masked value
params[:error_tracking_setting_attributes][:integrated] = settings[:integrated] unless settings[:integrated].nil?
params
end

View File

@ -17,4 +17,5 @@
project: error_tracking_setting_project_json,
api_host: setting.api_host,
enabled: setting.enabled.to_json,
integrated: setting.integrated.to_json,
token: setting.token.present? ? '*' * 12 : nil } }

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337628
milestone: '14.2'
type: development
group: group::pipeline authoring
default_enabled: false
default_enabled: true

View File

@ -0,0 +1,8 @@
---
name: security_reports_mr_widget_prompt
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70086
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340436
milestone: '14.3'
type: experiment
group: group::adoption
default_enabled: false

View File

@ -7,7 +7,8 @@ product_stage: configure
product_group: group::configure
product_category: infrastructure_as_code
value_type: number
status: broken
status: removed
milestone_removed: '14.3'
repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332466
time_frame: 28d
data_source: redis_hll

View File

@ -7,7 +7,8 @@ product_stage: configure
product_group: group::configure
product_category: infrastructure_as_code
value_type: number
status: broken
status: removed
milestone_removed: '14.3'
repair_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332466
time_frame: 7d
data_source: redis_hll

View File

@ -385,7 +385,6 @@ tables:
- public_builds
- last_repository_check_failed
- last_repository_check_at
- container_registry_enabled
- only_allow_merge_if_pipeline_succeeds
- has_external_issue_tracker
- repository_storage

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
class DropTemporaryColumnsAndTriggersForCiBuildNeeds < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
TABLE = 'ci_build_needs'
TEMPORARY_COLUMN = 'build_id_convert_to_bigint'
MAIN_COLUMN = 'build_id'
# rubocop:disable Migration/WithLockRetriesDisallowedMethod
def up
with_lock_retries do
cleanup_conversion_of_integer_to_bigint(TABLE, MAIN_COLUMN)
end
end
def down
check_trigger_permissions!(TABLE)
with_lock_retries do
add_column(TABLE, TEMPORARY_COLUMN, :int, default: 0, null: false)
install_rename_triggers(TABLE, MAIN_COLUMN, TEMPORARY_COLUMN)
end
end
# rubocop:enable Migration/WithLockRetriesDisallowedMethod
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class RemoveContainerRegistryEnabled < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
with_lock_retries do
remove_column :projects, :container_registry_enabled
end
end
def down
with_lock_retries do
add_column :projects, :container_registry_enabled, :boolean # rubocop:disable Migration/AddColumnsToWideTables
end
end
end

View File

@ -0,0 +1 @@
c99df310082dd6f5faff3cfd90dfca88af26d840889910ebac0e73ba483a09b2

View File

@ -0,0 +1 @@
abed3f9a6c188890d3dcd21f73a09347f8ccec0f6cc448220fadba5cbda17281

View File

@ -64,15 +64,6 @@ RETURN NULL;
END
$$;
CREATE FUNCTION trigger_21e7a2602957() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW."build_id_convert_to_bigint" := NEW."build_id";
RETURN NEW;
END;
$$;
CREATE FUNCTION trigger_3f6129be01d2() RETURNS trigger
LANGUAGE plpgsql
AS $$
@ -11227,7 +11218,6 @@ ALTER SEQUENCE chat_teams_id_seq OWNED BY chat_teams.id;
CREATE TABLE ci_build_needs (
id integer NOT NULL,
build_id_convert_to_bigint integer DEFAULT 0 NOT NULL,
name text NOT NULL,
artifacts boolean DEFAULT true NOT NULL,
optional boolean DEFAULT false NOT NULL,
@ -18184,7 +18174,6 @@ CREATE TABLE projects (
public_builds boolean DEFAULT true NOT NULL,
last_repository_check_failed boolean,
last_repository_check_at timestamp without time zone,
container_registry_enabled boolean,
only_allow_merge_if_pipeline_succeeds boolean DEFAULT false NOT NULL,
has_external_issue_tracker boolean,
repository_storage character varying DEFAULT 'default'::character varying NOT NULL,
@ -27322,8 +27311,6 @@ ALTER INDEX product_analytics_events_experimental_pkey ATTACH PARTITION gitlab_p
ALTER INDEX product_analytics_events_experimental_pkey ATTACH PARTITION gitlab_partitions_static.product_analytics_events_experimental_63_pkey;
CREATE TRIGGER trigger_21e7a2602957 BEFORE INSERT OR UPDATE ON ci_build_needs FOR EACH ROW EXECUTE FUNCTION trigger_21e7a2602957();
CREATE TRIGGER trigger_3f6129be01d2 BEFORE INSERT OR UPDATE ON ci_builds FOR EACH ROW EXECUTE FUNCTION trigger_3f6129be01d2();
CREATE TRIGGER trigger_542d6c2ad72e BEFORE INSERT OR UPDATE ON ci_builds_metadata FOR EACH ROW EXECUTE FUNCTION trigger_542d6c2ad72e();

View File

@ -265,3 +265,9 @@ by assigning different processes to different parts of the table. The `BATCH`
and `UPDATE_DELAY` parameters allow the speed of the migration to be traded off
against concurrent access to the table. The `ANSI` parameter should be set to
false if your terminal does not support ANSI escape codes.
By default, `sudo` does not preserve existing environment variables. You should append them, rather than prefix them.
```shell
sudo gitlab-rake gitlab:external_diffs:force_object_storage START_ID=59946109 END_ID=59946109 UPDATE_DELAY=5
```

View File

@ -78,7 +78,7 @@ packages on the group level, create a distribution with the same `codename`.
To create a project-level distribution:
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<project_id>/debian_distributions?codename=unstable
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<project_id>/debian_distributions?codename=unstable"
```
Example response:

View File

@ -18,6 +18,24 @@ module Gitlab
KNOWN_EVENTS_PATH = File.expand_path('known_events/*.yml', __dir__)
ALLOWED_AGGREGATIONS = %i(daily weekly).freeze
CATEGORIES_FOR_TOTALS = %w[
analytics
code_review
compliance
deploy_token_packages
ecosystem
epic_boards_usage
epics_usage
ide_edit
incident_management
issues_edit
pipeline_authoring
quickactions
search
testing
user_packages
].freeze
# Track event on entity_id
# Increment a Redis HLL counter for unique event_name and entity_id
#
@ -90,7 +108,7 @@ module Gitlab
hash["#{event}_monthly"] = unique_events(**monthly_time_range.merge(event_names: [event]))
end
if eligible_for_totals?(events_names)
if eligible_for_totals?(events_names) && CATEGORIES_FOR_TOTALS.include?(category)
event_results["#{category}_total_unique_counts_weekly"] = unique_events(**weekly_time_range.merge(event_names: events_names))
event_results["#{category}_total_unique_counts_monthly"] = unique_events(**monthly_time_range.merge(event_names: events_names))
end

View File

@ -13366,6 +13366,9 @@ msgstr ""
msgid "ErrorTracking|Enable error tracking"
msgstr ""
msgid "ErrorTracking|Error tracking backend"
msgstr ""
msgid "ErrorTracking|If you self-host Sentry, enter your Sentry instance's full URL. If you use Sentry's hosted solution, enter https://sentry.io"
msgstr ""
@ -28637,6 +28640,9 @@ msgstr[1] ""
msgid "Requires values to meet regular expression requirements."
msgstr ""
msgid "Requires you to deploy or set up cloud-hosted Sentry."
msgstr ""
msgid "Requires your primary GitLab email address."
msgstr ""
@ -30517,6 +30523,9 @@ msgstr ""
msgid "Send service data"
msgstr ""
msgid "Sentry"
msgstr ""
msgid "Sentry API URL"
msgstr ""
@ -37013,6 +37022,9 @@ msgstr ""
msgid "UsersSelect|Unassigned"
msgstr ""
msgid "Uses GitLab as a lightweight alternative to Sentry."
msgstr ""
msgid "Using %{code_start}::%{code_end} denotes a %{link_start}scoped label set%{link_end}"
msgstr ""

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe SecurityReportsMrWidgetPromptExperiment do
it "defines a control and candidate" do
expect(subject.behaviors.keys).to match_array(%w[control candidate])
end
it "publishes to the database" do
expect(subject).to receive(:publish_to_database)
subject.publish
end
end

View File

@ -89,7 +89,7 @@ RSpec.describe 'Search bar', :js do
expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: original_size)
end
it 'resets the dropdown filters', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/9985' do
it 'resets the dropdown filters' do
filtered_search.click
hint_offset = get_left_style(find('#js-dropdown-hint')['style'])
@ -103,7 +103,7 @@ RSpec.describe 'Search bar', :js do
find('.filtered-search-box .clear-search').click
filtered_search.click
expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 6)
expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', minimum: 6)
expect(get_left_style(find('#js-dropdown-hint')['style'])).to eq(hint_offset)
end
end

View File

@ -150,6 +150,33 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do
assert_text('Connection failed. Check Auth Token and try again.')
end
end
context 'integrated error tracking backend' do
it 'successfully fills and submits the form' do
visit project_settings_operations_path(project)
wait_for_requests
within '.js-error-tracking-settings' do
click_button('Expand')
end
expect(page).to have_content('Error tracking backend')
within '.js-error-tracking-settings' do
check('Active')
choose('GitLab')
end
expect(page).not_to have_content('Sentry API URL')
click_button('Save changes')
wait_for_requests
assert_text('Your changes have been saved')
end
end
end
context 'grafana integration settings form' do

View File

@ -1,6 +1,9 @@
import { GlFormRadioGroup, GlFormRadio } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import Vuex from 'vuex';
import { TEST_HOST } from 'helpers/test_constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ErrorTrackingSettings from '~/error_tracking_settings/components/app.vue';
import ErrorTrackingForm from '~/error_tracking_settings/components/error_tracking_form.vue';
import ProjectDropdown from '~/error_tracking_settings/components/project_dropdown.vue';
@ -14,20 +17,31 @@ describe('error tracking settings app', () => {
let wrapper;
function mountComponent() {
wrapper = shallowMount(ErrorTrackingSettings, {
localVue,
store, // Override the imported store
propsData: {
initialEnabled: 'true',
initialApiHost: TEST_HOST,
initialToken: 'someToken',
initialProject: null,
listProjectsEndpoint: TEST_HOST,
operationsSettingsEndpoint: TEST_HOST,
},
});
wrapper = extendedWrapper(
shallowMount(ErrorTrackingSettings, {
localVue,
store, // Override the imported store
propsData: {
initialEnabled: 'true',
initialIntegrated: 'false',
initialApiHost: TEST_HOST,
initialToken: 'someToken',
initialProject: null,
listProjectsEndpoint: TEST_HOST,
operationsSettingsEndpoint: TEST_HOST,
},
}),
);
}
const findBackendSettingsSection = () => wrapper.findByTestId('tracking-backend-settings');
const findBackendSettingsRadioGroup = () =>
findBackendSettingsSection().findComponent(GlFormRadioGroup);
const findBackendSettingsRadioButtons = () =>
findBackendSettingsRadioGroup().findAllComponents(GlFormRadio);
const findElementWithText = (wrappers, text) => wrappers.filter((item) => item.text() === text);
const findSentrySettings = () => wrapper.findByTestId('sentry-setting-form');
beforeEach(() => {
store = createStore();
@ -62,4 +76,46 @@ describe('error tracking settings app', () => {
});
});
});
describe('tracking-backend settings', () => {
it('contains a form-group with the correct label', () => {
expect(findBackendSettingsSection().attributes('label')).toBe('Error tracking backend');
});
it('contains a radio group', () => {
expect(findBackendSettingsRadioGroup().exists()).toBe(true);
});
it('contains the correct radio buttons', () => {
expect(findBackendSettingsRadioButtons()).toHaveLength(2);
expect(findElementWithText(findBackendSettingsRadioButtons(), 'Sentry')).toHaveLength(1);
expect(findElementWithText(findBackendSettingsRadioButtons(), 'GitLab')).toHaveLength(1);
});
it('toggles the sentry-settings section when sentry is selected as a tracking-backend', async () => {
expect(findSentrySettings().exists()).toBe(true);
// set the "integrated" setting to "true"
findBackendSettingsRadioGroup().vm.$emit('change', true);
await nextTick();
expect(findSentrySettings().exists()).toBe(false);
});
it.each([true, false])(
'calls the `updateIntegrated` action when the setting changes to `%s`',
(integrated) => {
jest.spyOn(store, 'dispatch').mockImplementation();
expect(store.dispatch).toHaveBeenCalledTimes(0);
findBackendSettingsRadioGroup().vm.$emit('change', integrated);
expect(store.dispatch).toHaveBeenCalledTimes(1);
expect(store.dispatch).toHaveBeenCalledWith('updateIntegrated', integrated);
},
);
});
});

View File

@ -42,6 +42,7 @@ export const sampleBackendProject = {
export const sampleFrontendSettings = {
apiHost: 'apiHost',
enabled: false,
integrated: false,
token: 'token',
selectedProject: {
slug: normalizedProject.slug,
@ -54,6 +55,7 @@ export const sampleFrontendSettings = {
export const transformedSettings = {
api_host: 'apiHost',
enabled: false,
integrated: false,
token: 'token',
project: {
slug: normalizedProject.slug,
@ -71,6 +73,7 @@ export const defaultProps = {
export const initialEmptyState = {
apiHost: '',
enabled: false,
integrated: false,
project: null,
token: '',
listProjectsEndpoint: TEST_HOST,
@ -80,6 +83,7 @@ export const initialEmptyState = {
export const initialPopulatedState = {
apiHost: 'apiHost',
enabled: true,
integrated: true,
project: JSON.stringify(projectList[0]),
token: 'token',
listProjectsEndpoint: TEST_HOST,

View File

@ -202,5 +202,11 @@ describe('error tracking settings actions', () => {
done,
);
});
it.each([true, false])('should set the `integrated` flag to `%s`', async (payload) => {
await testAction(actions.updateIntegrated, payload, state, [
{ type: types.UPDATE_INTEGRATED, payload },
]);
});
});
});

View File

@ -25,6 +25,7 @@ describe('error tracking settings mutations', () => {
expect(state.apiHost).toEqual('');
expect(state.enabled).toEqual(false);
expect(state.integrated).toEqual(false);
expect(state.selectedProject).toEqual(null);
expect(state.token).toEqual('');
expect(state.listProjectsEndpoint).toEqual(TEST_HOST);
@ -38,6 +39,7 @@ describe('error tracking settings mutations', () => {
expect(state.apiHost).toEqual('apiHost');
expect(state.enabled).toEqual(true);
expect(state.integrated).toEqual(true);
expect(state.selectedProject).toEqual(projectList[0]);
expect(state.token).toEqual('token');
expect(state.listProjectsEndpoint).toEqual(TEST_HOST);
@ -78,5 +80,11 @@ describe('error tracking settings mutations', () => {
expect(state.connectSuccessful).toBe(false);
expect(state.connectError).toBe(false);
});
it.each([true, false])('should update `integrated` to `%s`', (integrated) => {
mutations[types.UPDATE_INTEGRATED](state, integrated);
expect(state.integrated).toBe(integrated);
});
});
});

View File

@ -11,12 +11,14 @@ describe('error tracking settings utils', () => {
const emptyFrontendSettingsObject = {
apiHost: '',
enabled: false,
integrated: false,
token: '',
selectedProject: null,
};
const transformedEmptySettingsObject = {
api_host: null,
enabled: false,
integrated: false,
token: null,
project: null,
};

View File

@ -462,6 +462,8 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
allow(described_class).to receive(:known_events).and_return(known_events)
allow(described_class).to receive(:categories).and_return(%w(category1 category2))
stub_const('Gitlab::UsageDataCounters::HLLRedisCounter::CATEGORIES_FOR_TOTALS', %w(category1 category2))
described_class.track_event('event1_slot', values: entity1, time: 2.days.ago)
described_class.track_event('event2_slot', values: entity2, time: 2.days.ago)
described_class.track_event('event2_slot', values: entity3, time: 2.weeks.ago)

View File

@ -1279,9 +1279,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
subject { described_class.redis_hll_counters }
let(:categories) { ::Gitlab::UsageDataCounters::HLLRedisCounter.categories }
let(:ineligible_total_categories) do
%w[source_code ci_secrets_management incident_management_alerts snippets terraform incident_management_oncall secure network_policies]
end
context 'with redis_hll_tracking feature enabled' do
it 'has all known_events' do
@ -1296,7 +1293,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
metrics = keys.map { |key| "#{key}_weekly" } + keys.map { |key| "#{key}_monthly" }
if ineligible_total_categories.exclude?(category)
if ::Gitlab::UsageDataCounters::HLLRedisCounter::CATEGORIES_FOR_TOTALS.include?(category)
metrics.append("#{category}_total_unique_counts_weekly", "#{category}_total_unique_counts_monthly")
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!('drop_temporary_columns_and_triggers_for_ci_build_needs')
RSpec.describe DropTemporaryColumnsAndTriggersForCiBuildNeeds do
let(:ci_build_needs_table) { table(:ci_build_needs) }
it 'correctly migrates up and down' do
reversible_migration do |migration|
migration.before -> {
expect(ci_build_needs_table.column_names).to include('build_id_convert_to_bigint')
}
migration.after -> {
ci_build_needs_table.reset_column_information
expect(ci_build_needs_table.column_names).not_to include('build_id_convert_to_bigint')
}
end
end
end

View File

@ -153,6 +153,7 @@ RSpec.describe Projects::Operations::UpdateService do
{
error_tracking_setting_attributes: {
enabled: false,
integrated: true,
api_host: 'http://gitlab.com/',
token: 'token',
project: {
@ -174,6 +175,7 @@ RSpec.describe Projects::Operations::UpdateService do
project.reload
expect(project.error_tracking_setting).not_to be_enabled
expect(project.error_tracking_setting.integrated).to be_truthy
expect(project.error_tracking_setting.api_url).to eq(
'http://gitlab.com/api/0/projects/org/project/'
)
@ -206,6 +208,7 @@ RSpec.describe Projects::Operations::UpdateService do
{
error_tracking_setting_attributes: {
enabled: true,
integrated: true,
api_host: 'http://gitlab.com/',
token: 'token',
project: {
@ -222,6 +225,7 @@ RSpec.describe Projects::Operations::UpdateService do
expect(result[:status]).to eq(:success)
expect(project.error_tracking_setting).to be_enabled
expect(project.error_tracking_setting.integrated).to be_truthy
expect(project.error_tracking_setting.api_url).to eq(
'http://gitlab.com/api/0/projects/org/project/'
)

View File

@ -17,25 +17,6 @@ class BareRepoOperations
commit_id[0]
end
def commit_file(file, dst_path, branch = 'master')
head_id = execute(['show', '--format=format:%H', '--no-patch', branch], allow_failure: true)[0] || Gitlab::Git::EMPTY_TREE_ID
execute(['read-tree', '--empty'])
execute(['read-tree', head_id])
blob_id = execute(['hash-object', '--stdin', '-w']) do |stdin|
stdin.write(file.read)
end
execute(['update-index', '--add', '--cacheinfo', '100644', blob_id[0], dst_path])
tree_id = execute(['write-tree'])
commit_id = commit_tree(tree_id[0], "Add #{dst_path}", parent: head_id)
execute(['update-ref', "refs/heads/#{branch}", commit_id])
end
private
def execute(args, allow_failure: false)