Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml3
-rw-r--r--.rubocop_todo/style/float_division.yml8
-rw-r--r--app/assets/javascripts/blob/notebook/notebook_viewer.vue4
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue4
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue4
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue4
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue14
-rw-r--r--app/assets/javascripts/ci_variable_list/constants.js10
-rw-r--r--app/assets/javascripts/crm/constants.js4
-rw-r--r--app/assets/javascripts/crm/organizations/bundle.js18
-rw-r--r--app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql29
-rw-r--r--app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations_count_by_state.query.graphql11
-rw-r--r--app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue2
-rw-r--r--app/assets/javascripts/crm/organizations/components/organizations_root.vue225
-rw-r--r--app/assets/javascripts/deploy_keys/components/key.vue4
-rw-r--r--app/assets/javascripts/deploy_keys/components/keys_panel.vue4
-rw-r--r--app/assets/javascripts/deploy_keys/index.js4
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_discussions.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue4
-rw-r--r--app/assets/javascripts/diffs/index.js4
-rw-r--r--app/assets/javascripts/environments/components/deploy_board.vue4
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js4
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue16
-rw-r--r--app/assets/javascripts/groups/components/item_stats.vue8
-rw-r--r--app/assets/javascripts/groups/index.js12
-rw-r--r--app/assets/javascripts/header_search/constants.js25
-rw-r--r--app/assets/javascripts/header_search/store/actions.js29
-rw-r--r--app/assets/javascripts/header_search/store/getters.js18
-rw-r--r--app/assets/javascripts/header_search/store/mutations.js8
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue4
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue4
-rw-r--r--app/assets/javascripts/issues/show/components/app.vue16
-rw-r--r--app/assets/javascripts/issues/show/components/edited.vue4
-rw-r--r--app/assets/javascripts/issues/show/components/fields/description.vue4
-rw-r--r--app/assets/javascripts/jobs/components/stages_dropdown.vue4
-rw-r--r--app/assets/javascripts/linked_resources/index.js2
-rw-r--r--app/assets/javascripts/mr_notes/index.js4
-rw-r--r--app/assets/javascripts/mr_notes/init_notes.js4
-rw-r--r--app/assets/javascripts/notebook/cells/markdown.vue2
-rw-r--r--app/assets/javascripts/notebook/cells/output/image.vue2
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue12
-rw-r--r--app/assets/javascripts/notes/components/diff_discussion_header.vue8
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue16
-rw-r--r--app/assets/javascripts/notes/components/note_edited_text.vue4
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue4
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue4
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue20
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue8
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue36
-rw-r--r--app/assets/javascripts/notes/index.js4
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue19
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/blob/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js4
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue10
-rw-r--r--app/assets/javascripts/pdf/index.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/header_component.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue4
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_header.js4
-rw-r--r--app/assets/javascripts/profile/account/index.js4
-rw-r--r--app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue4
-rw-r--r--app/assets/javascripts/related_issues/components/related_issuable_input.vue4
-rw-r--r--app/assets/javascripts/related_issues/components/related_issues_root.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/lock/edit_form.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/participants/participants.vue4
-rw-r--r--app/assets/javascripts/sidebar/mount_milestone_sidebar.js6
-rw-r--r--app/assets/javascripts/surveys/merge_request_experience/app.vue15
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue38
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/system_note.vue4
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/index.js2
-rw-r--r--app/finders/merge_requests_finder.rb10
-rw-r--r--app/finders/merge_requests_finder/params.rb6
-rw-r--r--app/models/concerns/merge_request_reviewer_state.rb12
-rw-r--r--app/models/group.rb8
-rw-r--r--app/models/merge_request.rb11
-rw-r--r--app/models/merge_request_assignee.rb4
-rw-r--r--app/models/user.rb4
-rw-r--r--app/models/users/callout.rb3
-rw-r--r--app/serializers/merge_request_user_entity.rb12
-rw-r--r--app/services/merge_requests/base_service.rb43
-rw-r--r--app/services/merge_requests/update_assignees_service.rb2
-rw-r--r--app/views/groups/crm/organizations/index.html.haml2
-rw-r--r--app/workers/all_queues.yml9
-rw-r--r--app/workers/groups/update_two_factor_requirement_for_members_worker.rb22
-rw-r--r--config/feature_flags/development/mr_attention_requests.yml8
-rw-r--r--config/sidekiq_queues.yml2
-rw-r--r--doc/api/graphql/reference/index.md3
-rw-r--r--doc/api/merge_requests.md16
-rw-r--r--doc/development/documentation/styleguide/word_list.md2
-rw-r--r--doc/user/analytics/code_review_analytics.md55
-rw-r--r--doc/user/analytics/img/code_review_analytics_v13_11.pngbin37179 -> 0 bytes
-rw-r--r--doc/user/project/milestones/index.md98
-rw-r--r--lib/api/entities/merge_request_reviewer.rb1
-rw-r--r--lib/gitlab/ci/build/rules/rule/clause/exists.rb2
-rw-r--r--lib/gitlab/gon_helper.rb1
-rw-r--r--locale/gitlab.pot18
-rw-r--r--package.json2
-rw-r--r--qa/qa/support/api.rb20
-rw-r--r--spec/finders/merge_requests_finder/params_spec.rb23
-rw-r--r--spec/finders/merge_requests_finder_spec.rb19
-rw-r--r--spec/fixtures/lib/gitlab/import_export/complex/project.json4
-rw-r--r--spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson2
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js17
-rw-r--r--spec/frontend/ci_variable_list/mocks.js6
-rw-r--r--spec/frontend/crm/mock_data.js22
-rw-r--r--spec/frontend/crm/organizations_root_spec.js92
-rw-r--r--spec/frontend/header_search/mock_data.js44
-rw-r--r--spec/frontend/header_search/store/actions_spec.js66
-rw-r--r--spec/frontend/header_search/store/getters_spec.js24
-rw-r--r--spec/frontend/ide/components/commit_sidebar/radio_group_spec.js8
-rw-r--r--spec/frontend/surveys/merge_request_performance/app_spec.js38
-rw-r--r--spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js2
-rw-r--r--spec/graphql/types/merge_request_review_state_enum_spec.rb4
-rw-r--r--spec/graphql/types/user_merge_request_interaction_type_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb46
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/config/external/processor_spec.rb30
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml2
-rw-r--r--spec/lib/gitlab/utils/delegator_override_spec.rb1
-rw-r--r--spec/lib/gitlab/utils/nokogiri_spec.rb3
-rw-r--r--spec/lib/gitlab/utils/sanitize_node_link_spec.rb4
-rw-r--r--spec/models/group_spec.rb91
-rw-r--r--spec/models/merge_request_assignee_spec.rb22
-rw-r--r--spec/models/merge_request_reviewer_spec.rb20
-rw-r--r--spec/models/merge_request_spec.rb16
-rw-r--r--spec/models/user_spec.rb19
-rw-r--r--spec/models/users/merge_request_interaction_spec.rb3
-rw-r--r--spec/requests/api/graphql/project/merge_request_spec.rb20
-rw-r--r--spec/requests/api/merge_requests_spec.rb1
-rw-r--r--spec/services/merge_requests/update_assignees_service_spec.rb43
-rw-r--r--spec/services/merge_requests/update_service_spec.rb98
-rw-r--r--spec/support/helpers/javascript_form_helper.rb7
-rw-r--r--spec/support/rspec_order_todo.yml22
-rw-r--r--spec/workers/disallow_two_factor_for_group_worker_spec.rb2
-rw-r--r--spec/workers/groups/update_two_factor_requirement_for_members_worker_spec.rb39
-rw-r--r--workhorse/internal/headers/content_headers.go4
-rw-r--r--workhorse/internal/senddata/contentprocessor/contentprocessor_test.go18
-rw-r--r--workhorse/internal/upstream/routes.go3
-rw-r--r--workhorse/testdata/file.docxbin0 -> 6151 bytes
-rw-r--r--workhorse/upload_test.go2
-rw-r--r--yarn.lock8
154 files changed, 1152 insertions, 1009 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index 43b4e46171b..86549bc84d2 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -602,9 +602,6 @@ Style/MultilineWhenThen:
Style/NumericPredicate:
EnforcedStyle: comparison
-Style/FloatDivision:
- Enabled: false
-
Cop/BanCatchThrow:
Enabled: true
diff --git a/.rubocop_todo/style/float_division.yml b/.rubocop_todo/style/float_division.yml
new file mode 100644
index 00000000000..201f89ac35a
--- /dev/null
+++ b/.rubocop_todo/style/float_division.yml
@@ -0,0 +1,8 @@
+---
+Style/FloatDivision:
+ Exclude:
+ - 'ee/app/models/geo_node_status.rb'
+ - 'ee/app/models/namespaces/storage/root_size.rb'
+ - 'lib/learn_gitlab/onboarding.rb'
+ - 'qa/qa/support/formatters/allure_metadata_formatter.rb'
+ - 'qa/qa/tools/reliable_report.rb'
diff --git a/app/assets/javascripts/blob/notebook/notebook_viewer.vue b/app/assets/javascripts/blob/notebook/notebook_viewer.vue
index 79b8d273883..dc1a9cb865a 100644
--- a/app/assets/javascripts/blob/notebook/notebook_viewer.vue
+++ b/app/assets/javascripts/blob/notebook/notebook_viewer.vue
@@ -1,11 +1,11 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
-import notebookLab from '~/notebook/index.vue';
+import NotebookLab from '~/notebook/index.vue';
export default {
components: {
- notebookLab,
+ NotebookLab,
GlLoadingIcon,
},
props: {
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue b/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue
index 83bad9eb518..59ddf4b19d8 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue
@@ -11,11 +11,11 @@ import {
import addAdminVariable from '../graphql/mutations/admin_add_variable.mutation.graphql';
import deleteAdminVariable from '../graphql/mutations/admin_delete_variable.mutation.graphql';
import updateAdminVariable from '../graphql/mutations/admin_update_variable.mutation.graphql';
-import ciVariableSettings from './ci_variable_settings.vue';
+import CiVariableSettings from './ci_variable_settings.vue';
export default {
components: {
- ciVariableSettings,
+ CiVariableSettings,
},
inject: ['endpoint'],
data() {
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue b/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue
index 3af83ffa8ed..3522243e3e7 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue
@@ -14,11 +14,11 @@ import {
import addGroupVariable from '../graphql/mutations/group_add_variable.mutation.graphql';
import deleteGroupVariable from '../graphql/mutations/group_delete_variable.mutation.graphql';
import updateGroupVariable from '../graphql/mutations/group_update_variable.mutation.graphql';
-import ciVariableSettings from './ci_variable_settings.vue';
+import CiVariableSettings from './ci_variable_settings.vue';
export default {
components: {
- ciVariableSettings,
+ CiVariableSettings,
},
mixins: [glFeatureFlagsMixin()],
inject: ['endpoint', 'groupPath', 'groupId'],
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue b/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue
index 2ec7307862f..29db02a3c59 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue
@@ -16,11 +16,11 @@ import {
import addProjectVariable from '../graphql/mutations/project_add_variable.mutation.graphql';
import deleteProjectVariable from '../graphql/mutations/project_delete_variable.mutation.graphql';
import updateProjectVariable from '../graphql/mutations/project_update_variable.mutation.graphql';
-import ciVariableSettings from './ci_variable_settings.vue';
+import CiVariableSettings from './ci_variable_settings.vue';
export default {
components: {
- ciVariableSettings,
+ CiVariableSettings,
},
inject: ['endpoint', 'projectFullPath', 'projectId'],
data() {
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
index 5ba63de8c96..1ae06773ea8 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
@@ -108,7 +108,6 @@ export default {
return {
newEnvironments: [],
isTipDismissed: getCookie(AWS_TIP_DISMISSED_COOKIE_NAME) === 'true',
- typeOptions: variableOptions,
validationErrorEventProperty: '',
variable: { ...defaultVariableState, ...this.selectedVariable },
};
@@ -259,6 +258,7 @@ export default {
},
},
defaultScope: allEnvironments.text,
+ variableOptions,
};
</script>
@@ -298,16 +298,20 @@ export default {
/>
</gl-form-group>
- <div class="d-flex">
- <gl-form-group :label="__('Type')" label-for="ci-variable-type" class="w-50 gl-mr-5">
+ <div class="gl-display-flex">
+ <gl-form-group :label="__('Type')" label-for="ci-variable-type" class="gl-w-half gl-mr-5">
<gl-form-select
id="ci-variable-type"
v-model="variable.variableType"
- :options="typeOptions"
+ :options="$options.variableOptions"
/>
</gl-form-group>
- <gl-form-group label-for="ci-variable-env" class="w-50" data-testid="environment-scope">
+ <gl-form-group
+ label-for="ci-variable-env"
+ class="gl-w-half"
+ data-testid="environment-scope"
+ >
<template #label>
{{ __('Environment scope') }}
<gl-link
diff --git a/app/assets/javascripts/ci_variable_list/constants.js b/app/assets/javascripts/ci_variable_list/constants.js
index 5d22974ffbb..e2dd28cdaa1 100644
--- a/app/assets/javascripts/ci_variable_list/constants.js
+++ b/app/assets/javascripts/ci_variable_list/constants.js
@@ -10,7 +10,7 @@ export const displayText = {
};
export const variableTypes = {
- variableType: 'ENV_VAR',
+ envType: 'ENV_VAR',
fileType: 'FILE',
};
@@ -29,13 +29,13 @@ export const allEnvironments = {
export const variableText = {
[types.variableType]: __('Variable'),
[types.fileType]: __('File'),
- [variableTypes.variableType]: __('Variable'),
+ [variableTypes.envType]: __('Variable'),
[variableTypes.fileType]: __('File'),
};
export const variableOptions = [
- { value: types.variableType, text: variableText[types.variableType] },
- { value: types.fileType, text: variableText[types.fileType] },
+ { value: variableTypes.envType, text: variableText[variableTypes.envType] },
+ { value: variableTypes.fileType, text: variableText[variableTypes.fileType] },
];
export const defaultVariableState = {
@@ -44,7 +44,7 @@ export const defaultVariableState = {
masked: false,
protected: false,
value: '',
- variableType: types.variableType,
+ variableType: variableTypes.envType,
};
// eslint-disable-next-line @gitlab/require-i18n-strings
diff --git a/app/assets/javascripts/crm/constants.js b/app/assets/javascripts/crm/constants.js
index 815289e075e..832efa90956 100644
--- a/app/assets/javascripts/crm/constants.js
+++ b/app/assets/javascripts/crm/constants.js
@@ -5,3 +5,7 @@ export const trackViewsOptions = {
category: 'Customer Relations' /* eslint-disable-line @gitlab/require-i18n-strings */,
action: 'view_contacts_list',
};
+export const organizationTrackViewsOptions = {
+ category: 'Customer Relations' /* eslint-disable-line @gitlab/require-i18n-strings */,
+ action: 'view_organizations_list',
+};
diff --git a/app/assets/javascripts/crm/organizations/bundle.js b/app/assets/javascripts/crm/organizations/bundle.js
index 828d7cd426c..5897810a384 100644
--- a/app/assets/javascripts/crm/organizations/bundle.js
+++ b/app/assets/javascripts/crm/organizations/bundle.js
@@ -3,6 +3,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
import createDefaultClient from '~/lib/graphql';
+import { parseBoolean } from '~/lib/utils/common_utils';
import CrmOrganizationsRoot from './components/organizations_root.vue';
import routes from './routes';
@@ -21,7 +22,14 @@ export default () => {
return false;
}
- const { basePath, canAdminCrmOrganization, groupFullPath, groupId, groupIssuesPath } = el.dataset;
+ const {
+ basePath,
+ canAdminCrmOrganization,
+ groupFullPath,
+ groupId,
+ groupIssuesPath,
+ textQuery,
+ } = el.dataset;
const router = new VueRouter({
base: basePath,
@@ -33,7 +41,13 @@ export default () => {
el,
router,
apolloProvider,
- provide: { canAdminCrmOrganization, groupFullPath, groupId, groupIssuesPath },
+ provide: {
+ canAdminCrmOrganization: parseBoolean(canAdminCrmOrganization),
+ groupFullPath,
+ groupId,
+ groupIssuesPath,
+ textQuery,
+ },
render(createElement) {
return createElement(CrmOrganizationsRoot);
},
diff --git a/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql b/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql
index 97b75091cac..1bdcd9ba352 100644
--- a/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql
+++ b/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql
@@ -1,12 +1,37 @@
#import "./crm_organization_fields.fragment.graphql"
-query organizations($groupFullPath: ID!) {
+query organizations(
+ $groupFullPath: ID!
+ $state: CustomerRelationsOrganizationState
+ $searchTerm: String
+ $sort: OrganizationSort
+ $firstPageSize: Int
+ $lastPageSize: Int
+ $prevPageCursor: String = ""
+ $nextPageCursor: String = ""
+ $ids: [CustomerRelationsOrganizationID!]
+) {
group(fullPath: $groupFullPath) {
id
- organizations {
+ organizations(
+ state: $state
+ search: $searchTerm
+ sort: $sort
+ first: $firstPageSize
+ last: $lastPageSize
+ after: $nextPageCursor
+ before: $prevPageCursor
+ ids: $ids
+ ) {
nodes {
...OrganizationFragment
}
+ pageInfo {
+ hasNextPage
+ endCursor
+ hasPreviousPage
+ startCursor
+ }
}
}
}
diff --git a/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations_count_by_state.query.graphql b/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations_count_by_state.query.graphql
new file mode 100644
index 00000000000..fb6064e171f
--- /dev/null
+++ b/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations_count_by_state.query.graphql
@@ -0,0 +1,11 @@
+query organizationsCountByState($groupFullPath: ID!, $searchTerm: String) {
+ group(fullPath: $groupFullPath) {
+ __typename
+ id
+ organizationStateCounts(search: $searchTerm) {
+ all
+ active
+ inactive
+ }
+ }
+}
diff --git a/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue b/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue
index 5fd0294b0ea..32900d45f22 100644
--- a/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue
+++ b/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue
@@ -36,7 +36,7 @@ export default {
getQuery() {
return {
query: getGroupOrganizationsQuery,
- variables: { groupFullPath: this.groupFullPath },
+ variables: { groupFullPath: this.groupFullPath, ids: [this.organizationGraphQLId] },
};
},
title() {
diff --git a/app/assets/javascripts/crm/organizations/components/organizations_root.vue b/app/assets/javascripts/crm/organizations/components/organizations_root.vue
index a165dd68603..155c8f00537 100644
--- a/app/assets/javascripts/crm/organizations/components/organizations_root.vue
+++ b/app/assets/javascripts/crm/organizations/components/organizations_root.vue
@@ -1,36 +1,54 @@
<script>
-import { GlAlert, GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@gitlab/ui';
-import { parseBoolean } from '~/lib/utils/common_utils';
+import { GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { EDIT_ROUTE_NAME, NEW_ROUTE_NAME } from '../../constants';
+import PaginatedTableWithSearchAndTabs from '~/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue';
+import {
+ bodyTrClass,
+ initialPaginationState,
+} from '~/vue_shared/components/paginated_table_with_search_and_tabs/constants';
+import { convertToSnakeCase } from '~/lib/utils/text_utility';
+import { EDIT_ROUTE_NAME, NEW_ROUTE_NAME, organizationTrackViewsOptions } from '../../constants';
import getGroupOrganizationsQuery from './graphql/get_group_organizations.query.graphql';
+import getGroupOrganizationsCountByStateQuery from './graphql/get_group_organizations_count_by_state.query.graphql';
export default {
components: {
- GlAlert,
GlButton,
GlLoadingIcon,
GlTable,
+ PaginatedTableWithSearchAndTabs,
},
directives: {
GlTooltip: GlTooltipDirective,
},
- inject: ['canAdminCrmOrganization', 'groupFullPath', 'groupIssuesPath'],
+ inject: ['canAdminCrmOrganization', 'groupFullPath', 'groupIssuesPath', 'textQuery'],
data() {
return {
- organizations: [],
+ organizations: { list: [] },
+ organizationsCount: {},
error: false,
+ filteredByStatus: '',
+ pagination: initialPaginationState,
+ statusFilter: 'all',
+ searchTerm: this.textQuery,
+ sort: 'NAME_ASC',
+ sortDesc: false,
};
},
apollo: {
organizations: {
- query() {
- return getGroupOrganizationsQuery;
- },
+ query: getGroupOrganizationsQuery,
variables() {
return {
groupFullPath: this.groupFullPath,
+ searchTerm: this.searchTerm,
+ state: this.statusFilter,
+ sort: this.sort,
+ firstPageSize: this.pagination.firstPageSize,
+ lastPageSize: this.pagination.lastPageSize,
+ prevPageCursor: this.pagination.prevPageCursor,
+ nextPageCursor: this.pagination.nextPageCursor,
};
},
update(data) {
@@ -40,19 +58,52 @@ export default {
this.error = true;
},
},
+ organizationsCount: {
+ query: getGroupOrganizationsCountByStateQuery,
+ variables() {
+ return {
+ groupFullPath: this.groupFullPath,
+ searchTerm: this.searchTerm,
+ };
+ },
+ update(data) {
+ return data?.group?.organizationStateCounts;
+ },
+ error() {
+ this.error = true;
+ },
+ },
},
computed: {
isLoading() {
return this.$apollo.queries.organizations.loading;
},
- canAdmin() {
- return parseBoolean(this.canAdminCrmOrganization);
+ tbodyTrClass() {
+ return {
+ [bodyTrClass]: !this.loading && !this.isEmpty,
+ };
},
},
methods: {
+ errorAlertDismissed() {
+ this.error = false;
+ },
extractOrganizations(data) {
const organizations = data?.group?.organizations?.nodes || [];
- return organizations.slice().sort((a, b) => a.name.localeCompare(b.name));
+ const pageInfo = data?.group?.organizations?.pageInfo || {};
+ return {
+ list: organizations,
+ pageInfo,
+ };
+ },
+ fetchSortedData({ sortBy, sortDesc }) {
+ const sortingColumn = convertToSnakeCase(sortBy).toUpperCase();
+ const sortingDirection = sortDesc ? 'DESC' : 'ASC';
+ this.pagination = initialPaginationState;
+ this.sort = `${sortingColumn}_${sortingDirection}`;
+ },
+ filtersChanged({ searchTerm }) {
+ this.searchTerm = searchTerm;
},
getIssuesPath(path, value) {
return `${path}?crm_organization_id=${value}`;
@@ -60,6 +111,13 @@ export default {
getEditRoute(id) {
return { name: this.$options.EDIT_ROUTE_NAME, params: { id } };
},
+ pageChanged(pagination) {
+ this.pagination = pagination;
+ },
+ statusChanged({ filters, status }) {
+ this.statusFilter = filters;
+ this.filteredByStatus = status;
+ },
},
fields: [
{ key: 'name', sortable: true },
@@ -83,60 +141,113 @@ export default {
},
EDIT_ROUTE_NAME,
NEW_ROUTE_NAME,
+ statusTabs: [
+ {
+ title: __('Active'),
+ status: 'ACTIVE',
+ filters: 'active',
+ },
+ {
+ title: __('Inactive'),
+ status: 'INACTIVE',
+ filters: 'inactive',
+ },
+ {
+ title: __('All'),
+ status: 'ALL',
+ filters: 'all',
+ },
+ ],
+ organizationTrackViewsOptions,
+ emptyArray: [],
};
</script>
<template>
<div>
- <gl-alert v-if="error" variant="danger" class="gl-mt-6" @dismiss="error = false">
- {{ $options.i18n.errorText }}
- </gl-alert>
- <div
- class="gl-display-flex gl-align-items-baseline gl-flex-direction-row gl-justify-content-space-between gl-mt-6"
+ <paginated-table-with-search-and-tabs
+ :show-items="true"
+ :show-error-msg="false"
+ :i18n="$options.i18n"
+ :items="organizations.list"
+ :page-info="organizations.pageInfo"
+ :items-count="organizationsCount"
+ :status-tabs="$options.statusTabs"
+ :track-views-options="$options.organizationTrackViewsOptions"
+ :filter-search-tokens="$options.emptyArray"
+ filter-search-key="organizations"
+ @page-changed="pageChanged"
+ @tabs-changed="statusChanged"
+ @filters-changed="filtersChanged"
+ @error-alert-dismissed="errorAlertDismissed"
>
- <h2 class="gl-font-size-h2 gl-my-0">
- {{ $options.i18n.title }}
- </h2>
- <div
- v-if="canAdmin"
- class="gl-display-none gl-md-display-flex gl-align-items-center gl-justify-content-end"
- >
- <router-link :to="{ name: $options.NEW_ROUTE_NAME }">
- <gl-button variant="confirm" data-testid="new-organization-button">
+ <template #header-actions>
+ <router-link v-if="canAdminCrmOrganization" :to="{ name: $options.NEW_ROUTE_NAME }">
+ <gl-button
+ class="gl-my-3 gl-mr-5"
+ variant="confirm"
+ data-testid="new-organization-button"
+ >
{{ $options.i18n.newOrganization }}
</gl-button>
</router-link>
- </div>
- </div>
- <router-view />
- <gl-loading-icon v-if="isLoading" class="gl-mt-5" size="lg" />
- <gl-table
- v-else
- class="gl-mt-5"
- :items="organizations"
- :fields="$options.fields"
- :empty-text="$options.i18n.emptyText"
- show-empty
- >
- <template #cell(id)="{ value: id }">
- <gl-button
- v-gl-tooltip.hover.bottom="$options.i18n.issuesButtonLabel"
- class="gl-mr-3"
- data-testid="issues-link"
- icon="issues"
- :aria-label="$options.i18n.issuesButtonLabel"
- :href="getIssuesPath(groupIssuesPath, id)"
- />
- <router-link :to="getEditRoute(id)">
- <gl-button
- v-if="canAdmin"
- v-gl-tooltip.hover.bottom="$options.i18n.editButtonLabel"
- data-testid="edit-organization-button"
- icon="pencil"
- :aria-label="$options.i18n.editButtonLabel"
- />
- </router-link>
</template>
- </gl-table>
+
+ <template #title>
+ {{ $options.i18n.title }}
+ </template>
+
+ <template #table>
+ <gl-table
+ :items="organizations.list"
+ :fields="$options.fields"
+ :busy="isLoading"
+ stacked="md"
+ :tbody-tr-class="tbodyTrClass"
+ sort-direction="asc"
+ :sort-desc.sync="sortDesc"
+ sort-by="createdAt"
+ show-empty
+ no-local-sorting
+ sort-icon-left
+ fixed
+ @sort-changed="fetchSortedData"
+ >
+ <template #cell(id)="{ value: id }">
+ <gl-button
+ v-gl-tooltip.hover.bottom="$options.i18n.issuesButtonLabel"
+ class="gl-mr-3"
+ data-testid="issues-link"
+ icon="issues"
+ :aria-label="$options.i18n.issuesButtonLabel"
+ :href="getIssuesPath(groupIssuesPath, id)"
+ />
+ <router-link :to="getEditRoute(id)">
+ <gl-button
+ v-if="canAdminCrmOrganization"
+ v-gl-tooltip.hover.bottom="$options.i18n.editButtonLabel"
+ data-testid="edit-organization-button"
+ icon="pencil"
+ :aria-label="$options.i18n.editButtonLabel"
+ />
+ </router-link>
+ </template>
+
+ <template #table-busy>
+ <gl-loading-icon size="lg" color="dark" class="mt-3" />
+ </template>
+
+ <template #empty>
+ <span v-if="error">
+ {{ $options.i18n.errorText }}
+ </span>
+ <span v-else>
+ {{ $options.i18n.emptyText }}
+ </span>
+ </template>
+ </gl-table>
+ </template>
+ </paginated-table-with-search-and-tabs>
+ <router-view />
</div>
</template>
diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue
index d811bb3b0bf..c9097b9384f 100644
--- a/app/assets/javascripts/deploy_keys/components/key.vue
+++ b/app/assets/javascripts/deploy_keys/components/key.vue
@@ -4,11 +4,11 @@ import { head, tail } from 'lodash';
import { s__, sprintf } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago';
-import actionBtn from './action_btn.vue';
+import ActionBtn from './action_btn.vue';
export default {
components: {
- actionBtn,
+ ActionBtn,
GlButton,
GlIcon,
GlLink,
diff --git a/app/assets/javascripts/deploy_keys/components/keys_panel.vue b/app/assets/javascripts/deploy_keys/components/keys_panel.vue
index d71f4f5507f..77ec1ef590f 100644
--- a/app/assets/javascripts/deploy_keys/components/keys_panel.vue
+++ b/app/assets/javascripts/deploy_keys/components/keys_panel.vue
@@ -1,9 +1,9 @@
<script>
-import deployKey from './key.vue';
+import DeployKey from './key.vue';
export default {
components: {
- deployKey,
+ DeployKey,
},
props: {
keys: {
diff --git a/app/assets/javascripts/deploy_keys/index.js b/app/assets/javascripts/deploy_keys/index.js
index 6e439be42ae..83601d5b2e3 100644
--- a/app/assets/javascripts/deploy_keys/index.js
+++ b/app/assets/javascripts/deploy_keys/index.js
@@ -1,11 +1,11 @@
import Vue from 'vue';
-import deployKeysApp from './components/app.vue';
+import DeployKeysApp from './components/app.vue';
export default () =>
new Vue({
el: document.getElementById('js-deploy-keys'),
components: {
- deployKeysApp,
+ DeployKeysApp,
},
data() {
return {
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 70071a3ff53..d7b63d205dc 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -11,7 +11,7 @@ import NoPreviewViewer from '~/vue_shared/components/diff_viewer/viewers/no_prev
import NotDiffableViewer from '~/vue_shared/components/diff_viewer/viewers/not_diffable.vue';
import NoteForm from '~/notes/components/note_form.vue';
import eventHub from '~/notes/event_hub';
-import userAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import { IMAGE_DIFF_POSITION_TYPE } from '../constants';
import { getDiffMode } from '../store/utils';
import DiffDiscussions from './diff_discussions.vue';
@@ -28,7 +28,7 @@ export default {
ImageDiffOverlay,
NotDiffableViewer,
NoPreviewViewer,
- userAvatarLink,
+ UserAvatarLink,
DiffFileDrafts,
},
mixins: [diffLineNoteFormMixin, draftCommentsMixin],
diff --git a/app/assets/javascripts/diffs/components/diff_discussions.vue b/app/assets/javascripts/diffs/components/diff_discussions.vue
index b39b50c4cdc..25d3bda147b 100644
--- a/app/assets/javascripts/diffs/components/diff_discussions.vue
+++ b/app/assets/javascripts/diffs/components/diff_discussions.vue
@@ -2,11 +2,11 @@
import { GlIcon } from '@gitlab/ui';
import { mapActions } from 'vuex';
import DesignNotePin from '~/vue_shared/components/design_management/design_note_pin.vue';
-import noteableDiscussion from '~/notes/components/noteable_discussion.vue';
+import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
export default {
components: {
- noteableDiscussion,
+ NoteableDiscussion,
GlIcon,
DesignNotePin,
},
diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
index 467a0f8d2db..f63ab1bb067 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -7,7 +7,7 @@ import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import MultilineCommentForm from '~/notes/components/multiline_comment_form.vue';
import { commentLineOptions, formatLineRange } from '~/notes/components/multiline_comment_utils';
-import noteForm from '~/notes/components/note_form.vue';
+import NoteForm from '~/notes/components/note_form.vue';
import autosave from '~/notes/mixins/autosave';
import {
DIFF_NOTE_TYPE,
@@ -18,7 +18,7 @@ import {
export default {
components: {
- noteForm,
+ NoteForm,
MultilineCommentForm,
},
mixins: [autosave, diffLineNoteFormMixin, glFeatureFlagsMixin()],
diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js
index 1691da34c6d..b4ff5e4f250 100644
--- a/app/assets/javascripts/diffs/index.js
+++ b/app/assets/javascripts/diffs/index.js
@@ -3,7 +3,7 @@ import { mapActions, mapState, mapGetters } from 'vuex';
import { getCookie, parseBoolean, removeCookie } from '~/lib/utils/common_utils';
import eventHub from '../notes/event_hub';
-import diffsApp from './components/app.vue';
+import DiffsApp from './components/app.vue';
import { TREE_LIST_STORAGE_KEY, DIFF_WHITESPACE_COOKIE_NAME } from './constants';
import { getReviewsForMergeRequest } from './utils/file_reviews';
@@ -14,7 +14,7 @@ export default function initDiffsApp(store) {
el: '#js-diffs-app',
name: 'MergeRequestDiffs',
components: {
- diffsApp,
+ DiffsApp,
},
store,
data() {
diff --git a/app/assets/javascripts/environments/components/deploy_board.vue b/app/assets/javascripts/environments/components/deploy_board.vue
index 7a2c9a8600e..f22a0705b3d 100644
--- a/app/assets/javascripts/environments/components/deploy_board.vue
+++ b/app/assets/javascripts/environments/components/deploy_board.vue
@@ -20,13 +20,13 @@ import {
} from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { s__, n__ } from '~/locale';
-import instanceComponent from '~/vue_shared/components/deployment_instance.vue';
+import InstanceComponent from '~/vue_shared/components/deployment_instance.vue';
import { STATUS_MAP, CANARY_STATUS } from '../constants';
import CanaryIngress from './canary_ingress.vue';
export default {
components: {
- instanceComponent,
+ InstanceComponent,
CanaryIngress,
GlIcon,
GlLoadingIcon,
diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
index 4e5fe511f8a..1a32de30de0 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js
+++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import Translate from '~/vue_shared/translate';
-import environmentsFolderApp from './environments_folder_view.vue';
+import EnvironmentsFolderApp from './environments_folder_view.vue';
Vue.use(Translate);
Vue.use(VueApollo);
@@ -17,7 +17,7 @@ export default () => {
return new Vue({
el,
components: {
- environmentsFolderApp,
+ EnvironmentsFolderApp,
},
apolloProvider,
provide: {
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 2f182b86d2c..9b45880a42e 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -21,10 +21,10 @@ import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE, ITEM_TYPE } from '../const
import eventHub from '../event_hub';
-import itemActions from './item_actions.vue';
-import itemCaret from './item_caret.vue';
-import itemStats from './item_stats.vue';
-import itemTypeIcon from './item_type_icon.vue';
+import ItemActions from './item_actions.vue';
+import ItemCaret from './item_caret.vue';
+import ItemStats from './item_stats.vue';
+import ItemTypeIcon from './item_type_icon.vue';
export default {
directives: {
@@ -41,10 +41,10 @@ export default {
GlPopover,
GlLink,
UserAccessRoleBadge,
- itemCaret,
- itemTypeIcon,
- itemActions,
- itemStats,
+ ItemCaret,
+ ItemTypeIcon,
+ ItemActions,
+ ItemStats,
},
inject: ['currentGroupVisibility'],
props: {
diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue
index 2aa812250a0..a4c163b0a81 100644
--- a/app/assets/javascripts/groups/components/item_stats.vue
+++ b/app/assets/javascripts/groups/components/item_stats.vue
@@ -1,19 +1,19 @@
<script>
import { GlBadge } from '@gitlab/ui';
import isProjectPendingRemoval from 'ee_else_ce/groups/mixins/is_project_pending_removal';
-import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import {
ITEM_TYPE,
VISIBILITY_TYPE_ICON,
GROUP_VISIBILITY_TYPE,
PROJECT_VISIBILITY_TYPE,
} from '../constants';
-import itemStatsValue from './item_stats_value.vue';
+import ItemStatsValue from './item_stats_value.vue';
export default {
components: {
- timeAgoTooltip,
- itemStatsValue,
+ TimeAgoTooltip,
+ ItemStatsValue,
GlBadge,
},
mixins: [isProjectPendingRemoval],
diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js
index a502fcd31ad..dc2909f2621 100644
--- a/app/assets/javascripts/groups/index.js
+++ b/app/assets/javascripts/groups/index.js
@@ -4,9 +4,9 @@ import { parseBoolean } from '~/lib/utils/common_utils';
import UserCallout from '~/user_callout';
import Translate from '../vue_shared/translate';
-import groupsApp from './components/app.vue';
-import groupFolderComponent from './components/group_folder.vue';
-import groupItemComponent from './components/group_item.vue';
+import GroupsApp from './components/app.vue';
+import GroupFolderComponent from './components/group_folder.vue';
+import GroupItemComponent from './components/group_item.vue';
import { GROUPS_LIST_HOLDER_CLASS, CONTENT_LIST_CLASS } from './constants';
import GroupFilterableList from './groups_filterable_list';
import GroupsService from './service/groups_service';
@@ -33,8 +33,8 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
dataEl = containerEl.querySelector(CONTENT_LIST_CLASS);
}
- Vue.component('GroupFolder', groupFolderComponent);
- Vue.component('GroupItem', groupItemComponent);
+ Vue.component('GroupFolder', GroupFolderComponent);
+ Vue.component('GroupItem', GroupItemComponent);
Vue.use(GlToast);
@@ -42,7 +42,7 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
new Vue({
el,
components: {
- groupsApp,
+ GroupsApp,
},
provide() {
const {
diff --git a/app/assets/javascripts/header_search/constants.js b/app/assets/javascripts/header_search/constants.js
index 3a20fb0216d..332ccee510f 100644
--- a/app/assets/javascripts/header_search/constants.js
+++ b/app/assets/javascripts/header_search/constants.js
@@ -26,11 +26,17 @@ export const GROUPS_CATEGORY = s__('GlobalSearch|Groups');
export const PROJECTS_CATEGORY = s__('GlobalSearch|Projects');
-export const ISSUES_CATEGORY = 'Recent issues';
+export const ISSUES_CATEGORY = s__('GlobalSearch|Recent issues');
-export const MERGE_REQUEST_CATEGORY = 'Recent merge requests';
+export const MERGE_REQUEST_CATEGORY = s__('GlobalSearch|Recent merge requests');
-export const RECENT_EPICS_CATEGORY = 'Recent epics';
+export const RECENT_EPICS_CATEGORY = s__('GlobalSearch|Recent epics');
+
+export const IN_THIS_PROJECT_CATEGORY = s__('GlobalSearch|In this project');
+
+export const SETTINGS_CATEGORY = s__('GlobalSearch|Settings');
+
+export const HELP_CATEGORY = s__('GlobalSearch|Help');
export const LARGE_AVATAR_PX = 32;
@@ -55,3 +61,16 @@ export const HEADER_INIT_EVENTS = ['input', 'focus'];
export const IS_SEARCHING = 'is-searching';
export const IS_FOCUSED = 'is-focused';
export const IS_NOT_FOCUSED = 'is-not-focused';
+
+export const DROPDOWN_ORDER = [
+ MERGE_REQUEST_CATEGORY,
+ ISSUES_CATEGORY,
+ RECENT_EPICS_CATEGORY,
+ GROUPS_CATEGORY,
+ PROJECTS_CATEGORY,
+ IN_THIS_PROJECT_CATEGORY,
+ SETTINGS_CATEGORY,
+ HELP_CATEGORY,
+];
+
+export const FETCH_TYPES = ['generic', 'search'];
diff --git a/app/assets/javascripts/header_search/store/actions.js b/app/assets/javascripts/header_search/store/actions.js
index 3a86dcca409..a0f9e594506 100644
--- a/app/assets/javascripts/header_search/store/actions.js
+++ b/app/assets/javascripts/header_search/store/actions.js
@@ -1,10 +1,26 @@
+import { omitBy, isNil } from 'lodash';
+import { objectToQuery } from '~/lib/utils/url_utility';
import axios from '~/lib/utils/axios_utils';
+import { FETCH_TYPES } from '../constants';
import * as types from './mutation_types';
-export const fetchAutocompleteOptions = ({ commit, getters }) => {
- commit(types.REQUEST_AUTOCOMPLETE);
+export const autocompleteQuery = ({ state, fetchType }) => {
+ const query = omitBy(
+ {
+ term: state.search,
+ project_id: state.searchContext?.project?.id,
+ project_ref: state.searchContext?.ref,
+ filter: fetchType,
+ },
+ isNil,
+ );
+
+ return `${state.autocompletePath}?${objectToQuery(query)}`;
+};
+
+const doFetch = ({ commit, state, fetchType }) => {
return axios
- .get(getters.autocompleteQuery)
+ .get(autocompleteQuery({ state, fetchType }))
.then(({ data }) => {
commit(types.RECEIVE_AUTOCOMPLETE_SUCCESS, data);
})
@@ -13,6 +29,13 @@ export const fetchAutocompleteOptions = ({ commit, getters }) => {
});
};
+export const fetchAutocompleteOptions = ({ commit, state }) => {
+ commit(types.REQUEST_AUTOCOMPLETE);
+ const promises = FETCH_TYPES.map((fetchType) => doFetch({ commit, state, fetchType }));
+
+ return Promise.all(promises);
+};
+
export const clearAutocomplete = ({ commit }) => {
commit(types.CLEAR_AUTOCOMPLETE);
};
diff --git a/app/assets/javascripts/header_search/store/getters.js b/app/assets/javascripts/header_search/store/getters.js
index da7bccd35c0..3da9d2cd961 100644
--- a/app/assets/javascripts/header_search/store/getters.js
+++ b/app/assets/javascripts/header_search/store/getters.js
@@ -14,6 +14,7 @@ import {
PROJECTS_CATEGORY,
GROUPS_CATEGORY,
SEARCH_SHORTCUTS_MIN_CHARACTERS,
+ DROPDOWN_ORDER,
} from '../constants';
export const searchQuery = (state) => {
@@ -34,19 +35,6 @@ export const searchQuery = (state) => {
return `${state.searchPath}?${objectToQuery(query)}`;
};
-export const autocompleteQuery = (state) => {
- const query = omitBy(
- {
- term: state.search,
- project_id: state.searchContext?.project?.id,
- project_ref: state.searchContext?.ref,
- },
- isNil,
- );
-
- return `${state.autocompletePath}?${objectToQuery(query)}`;
-};
-
export const scopedIssuesPath = (state) => {
return (
state.searchContext?.project_metadata?.issues_path ||
@@ -197,7 +185,9 @@ export const autocompleteGroupedSearchOptions = (state) => {
}
});
- return results;
+ return results.sort(
+ (a, b) => DROPDOWN_ORDER.indexOf(a.category) - DROPDOWN_ORDER.indexOf(b.category),
+ );
};
export const searchOptions = (state, getters) => {
diff --git a/app/assets/javascripts/header_search/store/mutations.js b/app/assets/javascripts/header_search/store/mutations.js
index 92948bec515..19b4d4ec330 100644
--- a/app/assets/javascripts/header_search/store/mutations.js
+++ b/app/assets/javascripts/header_search/store/mutations.js
@@ -8,9 +8,11 @@ export default {
},
[types.RECEIVE_AUTOCOMPLETE_SUCCESS](state, data) {
state.loading = false;
- state.autocompleteOptions = data.map((d, i) => {
- return { html_id: `autocomplete-${d.category}-${i}`, ...d };
- });
+ state.autocompleteOptions = [...state.autocompleteOptions].concat(
+ data.map((d, i) => {
+ return { html_id: `autocomplete-${d.category}-${i}`, ...d };
+ }),
+ );
state.autocompleteError = false;
},
[types.RECEIVE_AUTOCOMPLETE_ERROR](state) {
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
index e0b7ac9b1e1..8962bb76926 100644
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_status_bar.vue
@@ -4,7 +4,7 @@ import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex';
import timeAgoMixin from '~/vue_shared/mixins/timeago';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
+import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
import { rightSidebarViews } from '../constants';
import IdeStatusList from './ide_status_list.vue';
import IdeStatusMr from './ide_status_mr.vue';
@@ -12,7 +12,7 @@ import IdeStatusMr from './ide_status_mr.vue';
export default {
components: {
GlIcon,
- userAvatarImage,
+ UserAvatarImage,
CiIcon,
IdeStatusList,
IdeStatusMr,
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index 87b60eca73c..9a529bdcee1 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -4,12 +4,12 @@ import { mapActions } from 'vuex';
import { modalTypes } from '../../constants';
import ItemButton from './button.vue';
import NewModal from './modal.vue';
-import upload from './upload.vue';
+import Upload from './upload.vue';
export default {
components: {
GlIcon,
- upload,
+ Upload,
ItemButton,
NewModal,
},
diff --git a/app/assets/javascripts/issues/show/components/app.vue b/app/assets/javascripts/issues/show/components/app.vue
index c664135f30e..632c2ed371f 100644
--- a/app/assets/javascripts/issues/show/components/app.vue
+++ b/app/assets/javascripts/issues/show/components/app.vue
@@ -17,11 +17,11 @@ import eventHub from '../event_hub';
import getIssueStateQuery from '../queries/get_issue_state.query.graphql';
import Service from '../services/index';
import Store from '../stores';
-import descriptionComponent from './description.vue';
-import editedComponent from './edited.vue';
-import formComponent from './form.vue';
+import DescriptionComponent from './description.vue';
+import EditedComponent from './edited.vue';
+import FormComponent from './form.vue';
import PinnedLinks from './pinned_links.vue';
-import titleComponent from './title.vue';
+import TitleComponent from './title.vue';
export default {
WorkspaceType,
@@ -29,9 +29,9 @@ export default {
GlIcon,
GlBadge,
GlIntersectionObserver,
- titleComponent,
- editedComponent,
- formComponent,
+ TitleComponent,
+ EditedComponent,
+ FormComponent,
PinnedLinks,
ConfidentialityBadge,
},
@@ -181,7 +181,7 @@ export default {
type: Object,
required: false,
default: () => {
- return descriptionComponent;
+ return DescriptionComponent;
},
},
showTitleBorder: {
diff --git a/app/assets/javascripts/issues/show/components/edited.vue b/app/assets/javascripts/issues/show/components/edited.vue
index 41cc3964055..4c5f783cd66 100644
--- a/app/assets/javascripts/issues/show/components/edited.vue
+++ b/app/assets/javascripts/issues/show/components/edited.vue
@@ -1,10 +1,10 @@
<script>
/* eslint-disable @gitlab/vue-require-i18n-strings */
-import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
components: {
- timeAgoTooltip,
+ TimeAgoTooltip,
},
props: {
updatedAt: {
diff --git a/app/assets/javascripts/issues/show/components/fields/description.vue b/app/assets/javascripts/issues/show/components/fields/description.vue
index f45af47374a..c2ab7c4f298 100644
--- a/app/assets/javascripts/issues/show/components/fields/description.vue
+++ b/app/assets/javascripts/issues/show/components/fields/description.vue
@@ -1,11 +1,11 @@
<script>
-import markdownField from '~/vue_shared/components/markdown/field.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { helpPagePath } from '~/helpers/help_page_helper';
import updateMixin from '../../mixins/update';
export default {
components: {
- markdownField,
+ MarkdownField,
},
mixins: [updateMixin],
props: {
diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/stages_dropdown.vue
index 7c4811b2d6f..e3afe9b7c67 100644
--- a/app/assets/javascripts/jobs/components/stages_dropdown.vue
+++ b/app/assets/javascripts/jobs/components/stages_dropdown.vue
@@ -4,14 +4,14 @@ import { isEmpty } from 'lodash';
import Mousetrap from 'mousetrap';
import { s__ } from '~/locale';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { clickCopyToClipboardButton } from '~/behaviors/copy_to_clipboard';
import { keysFor, MR_COPY_SOURCE_BRANCH_NAME } from '~/behaviors/shortcuts/keybindings';
export default {
components: {
CiIcon,
- clipboardButton,
+ ClipboardButton,
GlDropdown,
GlDropdownItem,
GlLink,
diff --git a/app/assets/javascripts/linked_resources/index.js b/app/assets/javascripts/linked_resources/index.js
index 6d799d30b4b..f1d3026c2f1 100644
--- a/app/assets/javascripts/linked_resources/index.js
+++ b/app/assets/javascripts/linked_resources/index.js
@@ -22,7 +22,7 @@ export default function initLinkedResources() {
name: 'LinkedResourcesRoot',
apolloProvider,
components: {
- resourceLinksBlock: ResourceLinksBlock,
+ ResourceLinksBlock,
},
render: (createElement) =>
createElement('resource-links-block', {
diff --git a/app/assets/javascripts/mr_notes/index.js b/app/assets/javascripts/mr_notes/index.js
index 8dba733294e..297420bf94d 100644
--- a/app/assets/javascripts/mr_notes/index.js
+++ b/app/assets/javascripts/mr_notes/index.js
@@ -5,7 +5,7 @@ import initRevertCommitModal from '~/projects/commit/init_revert_commit_modal';
import initDiffsApp from '../diffs';
import { resetServiceWorkersPublicPath } from '../lib/utils/webpack';
import MergeRequest from '../merge_request';
-import discussionCounter from '../notes/components/discussion_counter.vue';
+import DiscussionCounter from '../notes/components/discussion_counter.vue';
import initDiscussionFilters from '../notes/discussion_filters';
import initNotesApp from './init_notes';
@@ -37,7 +37,7 @@ export default function initMrNotes() {
el,
name: 'DiscussionCounter',
components: {
- discussionCounter,
+ DiscussionCounter,
},
store,
render(createElement) {
diff --git a/app/assets/javascripts/mr_notes/init_notes.js b/app/assets/javascripts/mr_notes/init_notes.js
index cf24d18c7b6..e4a7a7bd9fc 100644
--- a/app/assets/javascripts/mr_notes/init_notes.js
+++ b/app/assets/javascripts/mr_notes/init_notes.js
@@ -4,7 +4,7 @@ import { mapActions, mapState, mapGetters } from 'vuex';
import { parseBoolean } from '~/lib/utils/common_utils';
import store from '~/mr_notes/stores';
import discussionNavigator from '../notes/components/discussion_navigator.vue';
-import notesApp from '../notes/components/notes_app.vue';
+import NotesApp from '../notes/components/notes_app.vue';
import initWidget from '../vue_merge_request_widget';
export default () => {
@@ -13,7 +13,7 @@ export default () => {
el: '#js-vue-mr-discussions',
name: 'MergeRequestDiscussions',
components: {
- notesApp,
+ NotesApp,
},
store,
data() {
diff --git a/app/assets/javascripts/notebook/cells/markdown.vue b/app/assets/javascripts/notebook/cells/markdown.vue
index 8351ae7ced6..127e046b5a9 100644
--- a/app/assets/javascripts/notebook/cells/markdown.vue
+++ b/app/assets/javascripts/notebook/cells/markdown.vue
@@ -137,7 +137,7 @@ marked.setOptions({
export default {
components: {
- prompt: Prompt,
+ Prompt,
},
directives: {
SafeHtml,
diff --git a/app/assets/javascripts/notebook/cells/output/image.vue b/app/assets/javascripts/notebook/cells/output/image.vue
index 065f5def83c..da7d83539d3 100644
--- a/app/assets/javascripts/notebook/cells/output/image.vue
+++ b/app/assets/javascripts/notebook/cells/output/image.vue
@@ -3,7 +3,7 @@ import Prompt from '../prompt.vue';
export default {
components: {
- prompt: Prompt,
+ Prompt,
},
props: {
count: {
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index bd5945a951b..61b7f90e87b 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -14,7 +14,7 @@ import {
slugifyWithUnderscore,
} from '~/lib/utils/text_utility';
import { sprintf } from '~/locale';
-import markdownField from '~/vue_shared/components/markdown/field.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -25,8 +25,8 @@ import { COMMENT_FORM } from '../i18n';
import issuableStateMixin from '../mixins/issuable_state';
import CommentFieldLayout from './comment_field_layout.vue';
import CommentTypeDropdown from './comment_type_dropdown.vue';
-import discussionLockedWidget from './discussion_locked_widget.vue';
-import noteSignedOutWidget from './note_signed_out_widget.vue';
+import DiscussionLockedWidget from './discussion_locked_widget.vue';
+import NoteSignedOutWidget from './note_signed_out_widget.vue';
const { UNPROCESSABLE_ENTITY } = httpStatusCodes;
@@ -34,9 +34,9 @@ export default {
name: 'CommentForm',
i18n: COMMENT_FORM,
components: {
- noteSignedOutWidget,
- discussionLockedWidget,
- markdownField,
+ NoteSignedOutWidget,
+ DiscussionLockedWidget,
+ MarkdownField,
GlAlert,
GlButton,
TimelineEntryItem,
diff --git a/app/assets/javascripts/notes/components/diff_discussion_header.vue b/app/assets/javascripts/notes/components/diff_discussion_header.vue
index 3cf47f42e0c..1b1923a90f7 100644
--- a/app/assets/javascripts/notes/components/diff_discussion_header.vue
+++ b/app/assets/javascripts/notes/components/diff_discussion_header.vue
@@ -4,16 +4,16 @@ import { escape } from 'lodash';
import { mapActions } from 'vuex';
import { truncateSha } from '~/lib/utils/text_utility';
import { s__, __, sprintf } from '~/locale';
-import noteEditedText from './note_edited_text.vue';
-import noteHeader from './note_header.vue';
+import NoteEditedText from './note_edited_text.vue';
+import NoteHeader from './note_header.vue';
export default {
name: 'DiffDiscussionHeader',
components: {
GlAvatar,
GlAvatarLink,
- noteEditedText,
- noteHeader,
+ NoteEditedText,
+ NoteHeader,
},
directives: {
SafeHtml,
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index f1c41eea428..57edcadee6f 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -8,17 +8,17 @@ import { __ } from '~/locale';
import '~/behaviors/markdown/render_gfm';
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
import autosave from '../mixins/autosave';
-import noteAttachment from './note_attachment.vue';
-import noteAwardsList from './note_awards_list.vue';
-import noteEditedText from './note_edited_text.vue';
-import noteForm from './note_form.vue';
+import NoteAttachment from './note_attachment.vue';
+import NoteAwardsList from './note_awards_list.vue';
+import NoteEditedText from './note_edited_text.vue';
+import NoteForm from './note_form.vue';
export default {
components: {
- noteEditedText,
- noteAwardsList,
- noteAttachment,
- noteForm,
+ NoteEditedText,
+ NoteAwardsList,
+ NoteAttachment,
+ NoteForm,
Suggestions,
},
directives: {
diff --git a/app/assets/javascripts/notes/components/note_edited_text.vue b/app/assets/javascripts/notes/components/note_edited_text.vue
index 03cbdf45ddd..e0c3ed0c67a 100644
--- a/app/assets/javascripts/notes/components/note_edited_text.vue
+++ b/app/assets/javascripts/notes/components/note_edited_text.vue
@@ -1,11 +1,11 @@
<script>
/* eslint-disable @gitlab/vue-require-i18n-strings */
-import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
name: 'EditedNoteText',
components: {
- timeAgoTooltip,
+ TimeAgoTooltip,
},
props: {
actionText: {
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index 30579a8eb0d..fdb2fbe122a 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -4,7 +4,7 @@ import { mapGetters, mapActions, mapState } from 'vuex';
import { getDraft, updateDraft } from '~/lib/utils/autosave';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
-import markdownField from '~/vue_shared/components/markdown/field.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import eventHub from '../event_hub';
import issuableStateMixin from '../mixins/issuable_state';
import resolvable from '../mixins/resolvable';
@@ -15,7 +15,7 @@ export default {
i18n: COMMENT_FORM,
name: 'NoteForm',
components: {
- markdownField,
+ MarkdownField,
CommentFieldLayout,
GlButton,
GlSprintf,
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index 9917249f0db..c2111d1f79d 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -8,13 +8,13 @@ import {
} from '@gitlab/ui';
import { mapActions } from 'vuex';
import { __, s__ } from '~/locale';
-import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
export default {
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
components: {
- timeAgoTooltip,
+ TimeAgoTooltip,
GitlabTeamMemberBadge: () =>
import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'),
GlIcon,
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index ca880802b5d..208d6eb3c5f 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -10,25 +10,25 @@ import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
import { s__, __, sprintf } from '~/locale';
import diffLineNoteFormMixin from '~/notes/mixins/diff_line_note_form';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
-import userAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../event_hub';
import noteable from '../mixins/noteable';
import resolvable from '../mixins/resolvable';
-import diffDiscussionHeader from './diff_discussion_header.vue';
-import diffWithNote from './diff_with_note.vue';
+import DiffDiscussionHeader from './diff_discussion_header.vue';
+import DiffWithNote from './diff_with_note.vue';
import DiscussionActions from './discussion_actions.vue';
import DiscussionNotes from './discussion_notes.vue';
-import noteForm from './note_form.vue';
-import noteSignedOutWidget from './note_signed_out_widget.vue';
+import NoteForm from './note_form.vue';
+import NoteSignedOutWidget from './note_signed_out_widget.vue';
export default {
name: 'NoteableDiscussion',
components: {
GlIcon,
- userAvatarLink,
- diffDiscussionHeader,
- noteSignedOutWidget,
- noteForm,
+ UserAvatarLink,
+ DiffDiscussionHeader,
+ NoteSignedOutWidget,
+ NoteForm,
DraftNote,
TimelineEntryItem,
DiscussionNotes,
@@ -120,7 +120,7 @@ export default {
return !this.shouldRenderDiffs;
},
wrapperComponent() {
- return this.shouldRenderDiffs ? diffWithNote : 'div';
+ return this.shouldRenderDiffs ? DiffWithNote : 'div';
},
wrapperComponentProps() {
if (this.shouldRenderDiffs) {
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 875cfff74fe..ed7ef72086a 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -22,16 +22,16 @@ import {
commentLineOptions,
formatLineRange,
} from './multiline_comment_utils';
-import noteActions from './note_actions.vue';
+import NoteActions from './note_actions.vue';
import NoteBody from './note_body.vue';
-import noteHeader from './note_header.vue';
+import NoteHeader from './note_header.vue';
export default {
name: 'NoteableNote',
components: {
GlSprintf,
- noteHeader,
- noteActions,
+ NoteHeader,
+ NoteActions,
NoteBody,
TimelineEntryItem,
GlAvatarLink,
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 754c2917182..37bc8bad305 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -6,34 +6,34 @@ import { __ } from '~/locale';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import draftNote from '~/batch_comments/components/draft_note.vue';
+import DraftNote from '~/batch_comments/components/draft_note.vue';
import { getLocationHash, doesHashExistInUrl } from '~/lib/utils/url_utility';
-import placeholderNote from '~/vue_shared/components/notes/placeholder_note.vue';
-import placeholderSystemNote from '~/vue_shared/components/notes/placeholder_system_note.vue';
-import skeletonLoadingContainer from '~/vue_shared/components/notes/skeleton_note.vue';
-import systemNote from '~/vue_shared/components/notes/system_note.vue';
+import PlaceholderNote from '~/vue_shared/components/notes/placeholder_note.vue';
+import PlaceholderSystemNote from '~/vue_shared/components/notes/placeholder_system_note.vue';
+import SkeletonLoadingContainer from '~/vue_shared/components/notes/skeleton_note.vue';
+import SystemNote from '~/vue_shared/components/notes/system_note.vue';
import * as constants from '../constants';
import eventHub from '../event_hub';
-import commentForm from './comment_form.vue';
-import discussionFilterNote from './discussion_filter_note.vue';
-import noteableDiscussion from './noteable_discussion.vue';
-import noteableNote from './noteable_note.vue';
+import CommentForm from './comment_form.vue';
+import DiscussionFilterNote from './discussion_filter_note.vue';
+import NoteableDiscussion from './noteable_discussion.vue';
+import NoteableNote from './noteable_note.vue';
import SidebarSubscription from './sidebar_subscription.vue';
export default {
name: 'NotesApp',
components: {
- noteableNote,
- noteableDiscussion,
- systemNote,
- commentForm,
- placeholderNote,
- placeholderSystemNote,
- skeletonLoadingContainer,
- discussionFilterNote,
+ NoteableNote,
+ NoteableDiscussion,
+ SystemNote,
+ CommentForm,
+ PlaceholderNote,
+ PlaceholderSystemNote,
+ SkeletonLoadingContainer,
+ DiscussionFilterNote,
OrderedLayout,
SidebarSubscription,
- draftNote,
+ DraftNote,
TimelineEntryItem,
},
mixins: [glFeatureFlagsMixin()],
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index bd1bacecddb..27e54a1ea69 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import notesApp from './components/notes_app.vue';
+import NotesApp from './components/notes_app.vue';
import initDiscussionFilters from './discussion_filters';
import { store } from './stores';
import initTimelineToggle from './timeline';
@@ -15,7 +15,7 @@ export default () => {
el,
name: 'NotesRoot',
components: {
- notesApp,
+ NotesApp,
},
store,
data() {
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
index 122d444e859..f581469b12b 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
@@ -14,16 +14,17 @@ import NpmInstallation from './npm_installation.vue';
import NugetInstallation from './nuget_installation.vue';
import PypiInstallation from './pypi_installation.vue';
+const components = {
+ [PACKAGE_TYPE_CONAN]: ConanInstallation,
+ [PACKAGE_TYPE_MAVEN]: MavenInstallation,
+ [PACKAGE_TYPE_NPM]: NpmInstallation,
+ [PACKAGE_TYPE_NUGET]: NugetInstallation,
+ [PACKAGE_TYPE_PYPI]: PypiInstallation,
+ [PACKAGE_TYPE_COMPOSER]: ComposerInstallation,
+};
+
export default {
name: 'InstallationCommands',
- components: {
- [PACKAGE_TYPE_CONAN]: ConanInstallation,
- [PACKAGE_TYPE_MAVEN]: MavenInstallation,
- [PACKAGE_TYPE_NPM]: NpmInstallation,
- [PACKAGE_TYPE_NUGET]: NugetInstallation,
- [PACKAGE_TYPE_PYPI]: PypiInstallation,
- [PACKAGE_TYPE_COMPOSER]: ComposerInstallation,
- },
props: {
packageEntity: {
type: Object,
@@ -32,7 +33,7 @@ export default {
},
computed: {
installationComponent() {
- return this.$options.components[this.packageEntity.packageType];
+ return components[this.packageEntity.packageType];
},
},
};
diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js
index a4d89889d57..4cd312b403c 100644
--- a/app/assets/javascripts/pages/admin/jobs/index/index.js
+++ b/app/assets/javascripts/pages/admin/jobs/index/index.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import Translate from '~/vue_shared/translate';
-import stopJobsModal from './components/stop_jobs_modal.vue';
+import StopJobsModal from './components/stop_jobs_modal.vue';
Vue.use(Translate);
@@ -14,7 +14,7 @@ function initJobs() {
new Vue({
el: `#js-${modalId}`,
components: {
- stopJobsModal,
+ StopJobsModal,
},
mounted() {
stopJobsButton.classList.remove('disabled');
diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js
index 740fdb8a96a..e45f9a10294 100644
--- a/app/assets/javascripts/pages/projects/blob/show/index.js
+++ b/app/assets/javascripts/pages/projects/blob/show/index.js
@@ -9,7 +9,7 @@ import GpgBadges from '~/gpg_badges';
import createDefaultClient from '~/lib/graphql';
import initBlob from '~/pages/projects/init_blob';
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
-import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
+import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import BlobContentViewer from '~/repository/components/blob_content_viewer.vue';
import '~/sourcegraph/load';
import createStore from '~/code_navigation/store';
@@ -64,7 +64,7 @@ if (statusLink) {
new Vue({
el: CommitPipelineStatusEl,
components: {
- commitPipelineStatus,
+ CommitPipelineStatus,
},
render(createElement) {
return createElement('commit-pipeline-status', {
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
index 5dae812bbcb..eae721771de 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
@@ -6,7 +6,7 @@ import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';
import GlFieldErrors from '~/gl_field_errors';
import Translate from '~/vue_shared/translate';
-import intervalPatternInput from './components/interval_pattern_input.vue';
+import IntervalPatternInput from './components/interval_pattern_input.vue';
import TimezoneDropdown from './components/timezone_dropdown';
Vue.use(Translate);
@@ -19,7 +19,7 @@ function initIntervalPatternInput() {
return new Vue({
el: intervalPatternMount,
components: {
- intervalPatternInput,
+ IntervalPatternInput,
},
render(createElement) {
return createElement('interval-pattern-input', {
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index c7c331c7de5..0807756bc89 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -14,8 +14,8 @@ import {
featureAccessLevelDescriptions,
} from '../constants';
import { toggleHiddenClassBySelector } from '../external';
-import projectFeatureSetting from './project_feature_setting.vue';
-import projectSettingRow from './project_setting_row.vue';
+import ProjectFeatureSetting from './project_feature_setting.vue';
+import ProjectSettingRow from './project_setting_row.vue';
const FEATURE_ACCESS_LEVEL_ANONYMOUS = [30, s__('ProjectSettings|Everyone')];
@@ -56,8 +56,8 @@ export default {
},
components: {
- projectFeatureSetting,
- projectSettingRow,
+ ProjectFeatureSetting,
+ ProjectSettingRow,
GlButton,
GlIcon,
GlSprintf,
@@ -65,7 +65,7 @@ export default {
GlFormCheckbox,
GlToggle,
ConfirmDanger,
- otherProjectSettings: () =>
+ OtherProjectSettings: () =>
import(
'jh_component/pages/projects/shared/permissions/components/other_project_settings.vue'
),
diff --git a/app/assets/javascripts/pdf/index.vue b/app/assets/javascripts/pdf/index.vue
index 644eccc0232..ddc880db227 100644
--- a/app/assets/javascripts/pdf/index.vue
+++ b/app/assets/javascripts/pdf/index.vue
@@ -2,10 +2,10 @@
import pdfjsLib from 'pdfjs-dist/build/pdf';
import workerSrc from 'pdfjs-dist/build/pdf.worker.min';
-import page from './page/index.vue';
+import Page from './page/index.vue';
export default {
- components: { page },
+ components: { Page },
props: {
pdf: {
type: [String, Uint8Array],
diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue
index fabae62fc45..a36d5d9b58f 100644
--- a/app/assets/javascripts/pipelines/components/header_component.vue
+++ b/app/assets/javascripts/pipelines/components/header_component.vue
@@ -9,7 +9,7 @@ import {
} from '@gitlab/ui';
import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
-import ciHeader from '~/vue_shared/components/header_ci_component.vue';
+import CiHeader from '~/vue_shared/components/header_ci_component.vue';
import {
LOAD_FAILURE,
POST_FAILURE,
@@ -33,7 +33,7 @@ export default {
pipelineRetry: 'pipelineRetry',
finishedStatuses: ['FAILED', 'SUCCESS', 'CANCELED'],
components: {
- ciHeader,
+ CiHeader,
GlAlert,
GlButton,
GlLoadingIcon,
diff --git a/app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue b/app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue
index 70d1a5c08cc..f4fc6893520 100644
--- a/app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue
+++ b/app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue
@@ -1,5 +1,5 @@
<script>
-import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
/**
* Component that renders both the CI icon status and the job name.
@@ -9,7 +9,7 @@ import ciIcon from '~/vue_shared/components/ci_icon.vue';
*/
export default {
components: {
- ciIcon,
+ CiIcon,
},
props: {
name: {
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
index 793e343a02a..3f1d7255a2b 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
@@ -1,9 +1,9 @@
<script>
-import tooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
export default {
components: {
- tooltipOnTruncate,
+ TooltipOnTruncate,
},
props: {
jobName: {
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue
index e485b38ce11..e693075e19a 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue
@@ -1,10 +1,10 @@
<script>
import { capitalize, escape } from 'lodash';
-import tooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
export default {
components: {
- tooltipOnTruncate,
+ TooltipOnTruncate,
},
props: {
stageName: {
diff --git a/app/assets/javascripts/pipelines/pipeline_details_header.js b/app/assets/javascripts/pipelines/pipeline_details_header.js
index 2fedd7e7a98..c9e60756407 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_header.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_header.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import pipelineHeader from './components/header_component.vue';
+import PipelineHeader from './components/header_component.vue';
Vue.use(VueApollo);
@@ -16,7 +16,7 @@ export const createPipelineHeaderApp = (elSelector, apolloProvider, graphqlResou
new Vue({
el,
components: {
- pipelineHeader,
+ PipelineHeader,
},
apolloProvider,
provide: {
diff --git a/app/assets/javascripts/profile/account/index.js b/app/assets/javascripts/profile/account/index.js
index f208280af27..2d31cf772e3 100644
--- a/app/assets/javascripts/profile/account/index.js
+++ b/app/assets/javascripts/profile/account/index.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import Translate from '~/vue_shared/translate';
-import deleteAccountModal from './components/delete_account_modal.vue';
+import DeleteAccountModal from './components/delete_account_modal.vue';
import UpdateUsername from './components/update_username.vue';
export default () => {
@@ -27,7 +27,7 @@ export default () => {
new Vue({
el: deleteAccountModalEl,
components: {
- deleteAccountModal,
+ DeleteAccountModal,
},
mounted() {
deleteAccountButton.disabled = false;
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index a79da00de43..6b14ebadacc 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -4,7 +4,7 @@ import Visibility from 'visibilityjs';
import createFlash from '~/flash';
import Poll from '~/lib/utils/poll';
import { __, s__, sprintf } from '~/locale';
-import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import CommitPipelineService from '../services/commit_pipeline_service';
export default {
@@ -12,7 +12,7 @@ export default {
GlTooltip: GlTooltipDirective,
},
components: {
- ciIcon,
+ CiIcon,
GlLoadingIcon,
},
props: {
diff --git a/app/assets/javascripts/related_issues/components/related_issuable_input.vue b/app/assets/javascripts/related_issues/components/related_issuable_input.vue
index 270d4632a54..09ecad2d90e 100644
--- a/app/assets/javascripts/related_issues/components/related_issuable_input.vue
+++ b/app/assets/javascripts/related_issues/components/related_issuable_input.vue
@@ -7,14 +7,14 @@ import {
inputPlaceholderTextMap,
issuableTypesMap,
} from '../constants';
-import issueToken from './issue_token.vue';
+import IssueToken from './issue_token.vue';
const SPACE_FACTOR = 1;
export default {
name: 'RelatedIssuableInput',
components: {
- issueToken,
+ IssueToken,
},
props: {
inputId: {
diff --git a/app/assets/javascripts/related_issues/components/related_issues_root.vue b/app/assets/javascripts/related_issues/components/related_issues_root.vue
index cad5037d7e4..ae40232df6f 100644
--- a/app/assets/javascripts/related_issues/components/related_issues_root.vue
+++ b/app/assets/javascripts/related_issues/components/related_issues_root.vue
@@ -40,7 +40,7 @@ import RelatedIssuesBlock from './related_issues_block.vue';
export default {
name: 'RelatedIssuesRoot',
components: {
- relatedIssuesBlock: RelatedIssuesBlock,
+ RelatedIssuesBlock,
},
props: {
endpoint: {
diff --git a/app/assets/javascripts/sidebar/components/lock/edit_form.vue b/app/assets/javascripts/sidebar/components/lock/edit_form.vue
index 65b51169420..c9e651370f9 100644
--- a/app/assets/javascripts/sidebar/components/lock/edit_form.vue
+++ b/app/assets/javascripts/sidebar/components/lock/edit_form.vue
@@ -1,10 +1,10 @@
<script>
import { GlSprintf } from '@gitlab/ui';
-import editFormButtons from './edit_form_buttons.vue';
+import EditFormButtons from './edit_form_buttons.vue';
export default {
components: {
- editFormButtons,
+ EditFormButtons,
GlSprintf,
},
props: {
diff --git a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
index 5f1808ff4da..286bd50f6dd 100644
--- a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
+++ b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
@@ -6,7 +6,7 @@ import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import createFlash from '~/flash';
import eventHub from '~/sidebar/event_hub';
import toast from '~/vue_shared/plugins/global_toast';
-import editForm from './edit_form.vue';
+import EditForm from './edit_form.vue';
export default {
issue: 'issue',
@@ -23,7 +23,7 @@ export default {
displayText: __('Unlocked'),
},
components: {
- editForm,
+ EditForm,
GlIcon,
},
directives: {
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index b8804de653f..0bb6696286c 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -1,7 +1,7 @@
<script>
import { GlButton, GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { __, n__, sprintf } from '~/locale';
-import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
+import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
export default {
directives: {
@@ -11,7 +11,7 @@ export default {
GlButton,
GlIcon,
GlLoadingIcon,
- userAvatarImage,
+ UserAvatarImage,
},
props: {
loading: {
diff --git a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
index 2aacce2fb00..cc5de5e4083 100644
--- a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { IssuableType } from '~/issues/constants';
import { parseBoolean } from '~/lib/utils/common_utils';
-import timeTracker from './components/time_tracking/time_tracker.vue';
+import TimeTracker from './components/time_tracking/time_tracker.vue';
export default class SidebarMilestone {
constructor() {
@@ -23,13 +23,13 @@ export default class SidebarMilestone {
el,
name: 'SidebarMilestoneRoot',
components: {
- timeTracker,
+ TimeTracker,
},
provide: {
issuableType: IssuableType.Milestone,
},
render: (createElement) =>
- createElement('timeTracker', {
+ createElement('time-tracker', {
props: {
limitToHours: parseBoolean(limitToHours),
issuableIid: iid.toString(),
diff --git a/app/assets/javascripts/surveys/merge_request_experience/app.vue b/app/assets/javascripts/surveys/merge_request_experience/app.vue
index 4e4ef49b1c6..d48bdf7d4ed 100644
--- a/app/assets/javascripts/surveys/merge_request_experience/app.vue
+++ b/app/assets/javascripts/surveys/merge_request_experience/app.vue
@@ -89,6 +89,19 @@ export default {
if (e.key !== 'Escape') return;
this.$emit('close');
this.$refs.dismisser?.dismiss();
+ this.trackDismissal();
+ },
+ close() {
+ this.trackDismissal();
+ this.$emit('close');
+ },
+ trackDismissal() {
+ this.track('survey:mr_experience', {
+ label: 'dismiss',
+ extra: {
+ accountAge: this.accountAge,
+ },
+ });
},
},
};
@@ -119,7 +132,7 @@ export default {
icon="close"
@click="
dismiss();
- $emit('close');
+ close();
"
/>
<div
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js b/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
index bc84459e298..d67ff11f297 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
@@ -24,7 +24,7 @@ const nonStandardEvents = {
},
issues: {
uniqueUser: {
- expand: ['i_testing_load_performance_widget_total'],
+ expand: ['i_testing_issues_widget_total'],
},
counter: {},
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
index 5b8acb4ebf8..cedbd0de7c1 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
@@ -1,10 +1,10 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
-import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
export default {
components: {
- ciIcon,
+ CiIcon,
GlLoadingIcon,
},
props: {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
index f74826f95d3..50146651a6b 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
@@ -1,10 +1,10 @@
<script>
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetArchived',
components: {
- statusIcon,
+ StatusIcon,
},
};
</script>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
index e2d87d8d536..4e5b3aa5bcf 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
@@ -1,10 +1,10 @@
<script>
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetChecking',
components: {
- statusIcon,
+ StatusIcon,
},
};
</script>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
index 61f7d26f51e..78b8fb1f03d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
@@ -1,12 +1,12 @@
<script>
import MrWidgetAuthorTime from '../mr_widget_author_time.vue';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetClosed',
components: {
MrWidgetAuthorTime,
- statusIcon,
+ StatusIcon,
},
props: {
/* TODO: This is providing all store and service down when it
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
index 18103ac4a0e..733117f5f4a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
@@ -3,14 +3,14 @@ import { GlButton } from '@gitlab/ui';
import { stripHtml } from '~/lib/utils/text_utility';
import { sprintf, s__, n__ } from '~/locale';
import eventHub from '../../event_hub';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetFailedToMerge',
components: {
GlButton,
- statusIcon,
+ StatusIcon,
},
props: {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
index c7574a41bb8..51ac2576f75 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
@@ -4,7 +4,7 @@ import simplePoll from '~/lib/utils/simple_poll';
import MergeRequest from '~/merge_request';
import eventHub from '../../event_hub';
import { MERGE_ACTIVE_STATUS_PHRASES, STATE_MACHINE } from '../../constants';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
const { transitions } = STATE_MACHINE;
const { MERGE_FAILURE } = transitions;
@@ -12,7 +12,7 @@ const { MERGE_FAILURE } = transitions;
export default {
name: 'MRWidgetMerging',
components: {
- statusIcon,
+ StatusIcon,
},
props: {
mr: {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
index 659d12d1160..133cb8459ec 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
@@ -9,7 +9,7 @@ import {
MR_WIDGET_MISSING_BRANCH_RESTORE,
MR_WIDGET_MISSING_BRANCH_MANUALCLI,
} from '../../i18n';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetMissingBranch',
@@ -19,7 +19,7 @@ export default {
components: {
GlIcon,
GlSprintf,
- statusIcon,
+ StatusIcon,
},
mixins: [glFeatureFlagMixin(), mergeRequestQueryVariablesMixin],
apollo: {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
index d507e5f232b..21abae43348 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
@@ -2,14 +2,14 @@
import { GlLink, GlSprintf } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'PipelineFailed',
components: {
GlLink,
GlSprintf,
- statusIcon,
+ StatusIcon,
},
computed: {
troubleshootingDocsPath() {
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index fef8b9ea7c6..77d6c51a3dc 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -59,28 +59,28 @@ export default {
Loading,
ExtensionsContainer,
WidgetContainer,
- 'mr-widget-suggest-pipeline': WidgetSuggestPipeline,
+ MrWidgetSuggestPipeline: WidgetSuggestPipeline,
MrWidgetPipelineContainer,
MrWidgetAlertMessage,
- 'mr-widget-merged': MergedState,
- 'mr-widget-closed': ClosedState,
- 'mr-widget-merging': MergingState,
- 'mr-widget-failed-to-merge': FailedToMerge,
- 'mr-widget-wip': WorkInProgressState,
- 'mr-widget-archived': ArchivedState,
- 'mr-widget-conflicts': ConflictsState,
- 'mr-widget-nothing-to-merge': NothingToMergeState,
- 'mr-widget-not-allowed': NotAllowedState,
- 'mr-widget-missing-branch': MissingBranchState,
- 'mr-widget-ready-to-merge': () => import('./components/states/new_ready_to_merge.vue'),
- 'sha-mismatch': ShaMismatch,
- 'mr-widget-checking': CheckingState,
- 'mr-widget-unresolved-discussions': UnresolvedDiscussionsState,
- 'mr-widget-pipeline-blocked': PipelineBlockedState,
- 'mr-widget-pipeline-failed': PipelineFailedState,
+ MrWidgetMerged: MergedState,
+ MrWidgetClosed: ClosedState,
+ MrWidgetMerging: MergingState,
+ MrWidgetFailedToMerge: FailedToMerge,
+ MrWidgetWip: WorkInProgressState,
+ MrWidgetArchived: ArchivedState,
+ MrWidgetConflicts: ConflictsState,
+ MrWidgetNothingToMerge: NothingToMergeState,
+ MrWidgetNotAllowed: NotAllowedState,
+ MrWidgetMissingBranch: MissingBranchState,
+ MrWidgetReadyToMerge: () => import('./components/states/new_ready_to_merge.vue'),
+ ShaMismatch,
+ MrWidgetChecking: CheckingState,
+ MrWidgetUnresolvedDiscussions: UnresolvedDiscussionsState,
+ MrWidgetPipelineBlocked: PipelineBlockedState,
+ MrWidgetPipelineFailed: PipelineFailedState,
MrWidgetAutoMergeEnabled,
- 'mr-widget-auto-merge-failed': AutoMergeFailed,
- 'mr-widget-rebase': RebaseState,
+ MrWidgetAutoMergeFailed: AutoMergeFailed,
+ MrWidgetRebase: RebaseState,
SourceBranchRemovalStatus,
GroupedCodequalityReportsApp: () =>
import('../reports/codequality_report/grouped_codequality_reports_app.vue'),
diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
index 3593ea16968..7e99f1b01b2 100644
--- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
@@ -29,7 +29,7 @@ import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_
import '~/behaviors/markdown/render_gfm';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
-import noteHeader from '~/notes/components/note_header.vue';
+import NoteHeader from '~/notes/components/note_header.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { spriteIcon } from '~/lib/utils/common_utils';
import TimelineEntryItem from './timeline_entry_item.vue';
@@ -43,7 +43,7 @@ export default {
name: 'SystemNote',
components: {
GlIcon,
- noteHeader,
+ NoteHeader,
TimelineEntryItem,
GlButton,
GlSkeletonLoader,
diff --git a/app/assets/javascripts/work_items/components/work_item_links/index.js b/app/assets/javascripts/work_items/components/work_item_links/index.js
index 4ac547f28f4..8f31b07b6a3 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/index.js
+++ b/app/assets/javascripts/work_items/components/work_item_links/index.js
@@ -24,7 +24,7 @@ export default function initWorkItemLinks() {
name: 'WorkItemLinksRoot',
apolloProvider,
components: {
- workItemLinks: WorkItemLinks,
+ WorkItemLinks,
},
provide: {
projectPath,
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 06feefb9059..81e4ab7014d 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -44,8 +44,7 @@ class MergeRequestsFinder < IssuableFinder
:reviewer_id,
:reviewer_username,
:target_branch,
- :wip,
- :attention
+ :wip
]
end
@@ -70,7 +69,6 @@ class MergeRequestsFinder < IssuableFinder
items = by_approvals(items)
items = by_deployments(items)
items = by_reviewer(items)
- items = by_attention(items)
by_source_project_id(items)
end
@@ -220,12 +218,6 @@ class MergeRequestsFinder < IssuableFinder
end
end
- def by_attention(items)
- return items unless params.attention?
-
- items.attention(params.attention)
- end
-
def parse_datetime(input)
# To work around http://www.ruby-lang.org/en/news/2021/11/15/date-parsing-method-regexp-dos-cve-2021-41817/
DateTime.parse(input.byteslice(0, 128)) if input
diff --git a/app/finders/merge_requests_finder/params.rb b/app/finders/merge_requests_finder/params.rb
index 1c6a425c8af..e44e96054d3 100644
--- a/app/finders/merge_requests_finder/params.rb
+++ b/app/finders/merge_requests_finder/params.rb
@@ -21,11 +21,5 @@ class MergeRequestsFinder
end
end
end
-
- def attention
- strong_memoize(:attention) do
- User.find_by_username(params[:attention])
- end
- end
end
end
diff --git a/app/models/concerns/merge_request_reviewer_state.rb b/app/models/concerns/merge_request_reviewer_state.rb
index 18ec1c253e1..5efe02101ff 100644
--- a/app/models/concerns/merge_request_reviewer_state.rb
+++ b/app/models/concerns/merge_request_reviewer_state.rb
@@ -6,20 +6,12 @@ module MergeRequestReviewerState
included do
enum state: {
unreviewed: 0,
- reviewed: 1,
- attention_requested: 2
+ reviewed: 1
+ # 2 was removed with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95446
}
validates :state,
presence: true,
inclusion: { in: self.states.keys }
-
- belongs_to :updated_state_by, class_name: 'User', foreign_key: :updated_state_by_user_id
-
- def attention_requested_by
- return unless attention_requested?
-
- updated_state_by
- end
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index e0d10030b9f..a1eece54f0c 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -153,7 +153,7 @@ class Group < Namespace
after_create :post_create_hook
after_destroy :post_destroy_hook
- after_save :update_two_factor_requirement
+ after_commit :update_two_factor_requirement
after_update :path_changed_hook, if: :saved_change_to_path?
after_create -> { create_or_load_association(:group_feature) }
@@ -898,6 +898,10 @@ class Group < Namespace
end
end
+ def update_two_factor_requirement_for_members
+ direct_and_indirect_members.find_each(&:update_two_factor_requirement)
+ end
+
private
def feature_flag_enabled_for_self_or_ancestor?(feature_flag)
@@ -920,7 +924,7 @@ class Group < Namespace
def update_two_factor_requirement
return unless saved_change_to_require_two_factor_authentication? || saved_change_to_two_factor_grace_period?
- direct_and_indirect_members.find_each(&:update_two_factor_requirement)
+ Groups::UpdateTwoFactorRequirementForMembersWorker.perform_async(self.id)
end
def path_changed_hook
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 906e1f1e632..800e7091d4e 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -417,17 +417,6 @@ class MergeRequest < ApplicationRecord
)
end
- scope :attention, ->(user) do
- # rubocop: disable Gitlab/Union
- union = Gitlab::SQL::Union.new([
- MergeRequestReviewer.select(:merge_request_id).where(user_id: user.id, state: MergeRequestReviewer.states[:attention_requested]),
- MergeRequestAssignee.select(:merge_request_id).where(user_id: user.id, state: MergeRequestAssignee.states[:attention_requested])
- ])
- # rubocop: enable Gitlab/Union
-
- with(Gitlab::SQL::CTE.new(:reviewers_and_assignees, union).to_arel).where('merge_requests.id in (select merge_request_id from reviewers_and_assignees)')
- end
-
def self.total_time_to_merge
join_metrics
.merge(MergeRequest::Metrics.with_valid_time_to_merge)
diff --git a/app/models/merge_request_assignee.rb b/app/models/merge_request_assignee.rb
index fd8e5860040..3e481e35deb 100644
--- a/app/models/merge_request_assignee.rb
+++ b/app/models/merge_request_assignee.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
class MergeRequestAssignee < ApplicationRecord
- include MergeRequestReviewerState
-
belongs_to :merge_request, touch: true
belongs_to :assignee, class_name: "User", foreign_key: :user_id, inverse_of: :merge_request_assignees
@@ -11,6 +9,6 @@ class MergeRequestAssignee < ApplicationRecord
scope :in_projects, ->(project_ids) { joins(:merge_request).where(merge_requests: { target_project_id: project_ids }) }
def cache_key
- [model_name.cache_key, id, state, assignee.cache_key]
+ [model_name.cache_key, id, assignee.cache_key]
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 940547baac0..5e8ef306317 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -2152,10 +2152,6 @@ class User < ApplicationRecord
end
end
- def mr_attention_requests_enabled?
- Feature.enabled?(:mr_attention_requests, self)
- end
-
def account_age_in_days
(Date.current - created_at.to_date).to_i
end
diff --git a/app/models/users/callout.rb b/app/models/users/callout.rb
index 7b5c7fef7ba..a84335c0908 100644
--- a/app/models/users/callout.rb
+++ b/app/models/users/callout.rb
@@ -47,8 +47,7 @@ module Users
storage_enforcement_banner_second_enforcement_threshold: 44,
storage_enforcement_banner_third_enforcement_threshold: 45,
storage_enforcement_banner_fourth_enforcement_threshold: 46,
- attention_requests_top_nav: 47,
- attention_requests_side_nav: 48,
+ # 47 and 48 were removed with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95446
# 49 was removed with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91533
# because the banner was no longer relevant.
# Records will be migrated with https://gitlab.com/gitlab-org/gitlab/-/issues/367293
diff --git a/app/serializers/merge_request_user_entity.rb b/app/serializers/merge_request_user_entity.rb
index 2e875af6531..36825d14062 100644
--- a/app/serializers/merge_request_user_entity.rb
+++ b/app/serializers/merge_request_user_entity.rb
@@ -17,7 +17,7 @@ class MergeRequestUserEntity < ::API::Entities::UserBasic
end
expose :reviewed, if: satisfies(:present?, :allows_reviewers?) do |user, options|
- find_reviewer_or_assignee(user, options)&.reviewed?
+ options[:merge_request].find_reviewer(user)&.reviewed?
end
expose :approved, if: satisfies(:present?) do |user, options|
@@ -25,16 +25,6 @@ class MergeRequestUserEntity < ::API::Entities::UserBasic
# makes one query per merge request, whereas #approved_by? makes one per user
options[:merge_request].approvals.any? { |app| app.user_id == user.id }
end
-
- private
-
- def find_reviewer_or_assignee(user, options)
- if options[:type] == :reviewers
- options[:merge_request].find_reviewer(user)
- else
- options[:merge_request].find_assignee(user)
- end
- end
end
MergeRequestUserEntity.prepend_mod_with('MergeRequestUserEntity')
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index bda8dc64ac0..6cefd9169f5 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -43,8 +43,6 @@ module MergeRequests
end
def handle_assignees_change(merge_request, old_assignees)
- bulk_update_assignees_state(merge_request, merge_request.assignees - old_assignees)
-
MergeRequests::HandleAssigneesChangeService
.new(project: project, current_user: current_user)
.async_execute(merge_request, old_assignees)
@@ -60,7 +58,6 @@ module MergeRequests
new_reviewers = merge_request.reviewers - old_reviewers
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
- bulk_update_reviewers_state(merge_request, new_reviewers)
end
def cleanup_environments(merge_request)
@@ -247,46 +244,6 @@ module MergeRequests
Milestones::MergeRequestsCountService.new(milestone).delete_cache
end
-
- def bulk_update_assignees_state(merge_request, new_assignees)
- return unless current_user.mr_attention_requests_enabled?
- return if new_assignees.empty?
-
- assignees_map = merge_request.merge_request_assignees_with(new_assignees).to_h do |assignee|
- state = if assignee.user_id == current_user&.id
- :unreviewed
- else
- merge_request.find_reviewer(assignee.assignee)&.state || :attention_requested
- end
-
- [
- assignee,
- { state: MergeRequestAssignee.states[state], updated_state_by_user_id: current_user.id }
- ]
- end
-
- ::Gitlab::Database::BulkUpdate.execute(%i[state updated_state_by_user_id], assignees_map)
- end
-
- def bulk_update_reviewers_state(merge_request, new_reviewers)
- return unless current_user.mr_attention_requests_enabled?
- return if new_reviewers.empty?
-
- reviewers_map = merge_request.merge_request_reviewers_with(new_reviewers).to_h do |reviewer|
- state = if reviewer.user_id == current_user&.id
- :unreviewed
- else
- merge_request.find_assignee(reviewer.reviewer)&.state || :attention_requested
- end
-
- [
- reviewer,
- { state: MergeRequestReviewer.states[state], updated_state_by_user_id: current_user.id }
- ]
- end
-
- ::Gitlab::Database::BulkUpdate.execute(%i[state updated_state_by_user_id], reviewers_map)
- end
end
end
diff --git a/app/services/merge_requests/update_assignees_service.rb b/app/services/merge_requests/update_assignees_service.rb
index a6b0235c525..a13db52e34b 100644
--- a/app/services/merge_requests/update_assignees_service.rb
+++ b/app/services/merge_requests/update_assignees_service.rb
@@ -20,8 +20,6 @@ module MergeRequests
attrs = update_attrs.merge(assignee_ids: new_ids)
merge_request.update!(**attrs)
- bulk_update_assignees_state(merge_request, merge_request.assignees - old_assignees)
-
# Defer the more expensive operations (handle_assignee_changes) to the background
MergeRequests::HandleAssigneesChangeService
.new(project: project, current_user: current_user)
diff --git a/app/views/groups/crm/organizations/index.html.haml b/app/views/groups/crm/organizations/index.html.haml
index ff1ba678de0..f7702889eef 100644
--- a/app/views/groups/crm/organizations/index.html.haml
+++ b/app/views/groups/crm/organizations/index.html.haml
@@ -5,4 +5,4 @@
= content_for :after_content do
#js-crm-form-portal
-#js-crm-organizations-app{ data: { base_path: group_crm_organizations_path(@group), can_admin_crm_organization: can?(current_user, :admin_crm_organization, @group).to_s, group_full_path: @group.full_path, group_id: @group.id, group_issues_path: issues_group_path(@group) } }
+#js-crm-organizations-app{ data: { base_path: group_crm_organizations_path(@group), can_admin_crm_organization: can?(current_user, :admin_crm_organization, @group).to_s, group_full_path: @group.full_path, group_id: @group.id, group_issues_path: issues_group_path(@group), text_query: params[:search] } }
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 8bba5e36b52..a1958f1b540 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -2415,6 +2415,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: groups_update_two_factor_requirement_for_members
+ :worker_name: Groups::UpdateTwoFactorRequirementForMembersWorker
+ :feature_category: :authentication_and_authorization
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: import_issues_csv
:worker_name: ImportIssuesCsvWorker
:feature_category: :team_planning
diff --git a/app/workers/groups/update_two_factor_requirement_for_members_worker.rb b/app/workers/groups/update_two_factor_requirement_for_members_worker.rb
new file mode 100644
index 00000000000..ac1d3589516
--- /dev/null
+++ b/app/workers/groups/update_two_factor_requirement_for_members_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+# Worker for updating two factor requirement for all group members
+module Groups
+ class UpdateTwoFactorRequirementForMembersWorker
+ include ApplicationWorker
+
+ data_consistency :always
+
+ idempotent!
+
+ feature_category :authentication_and_authorization
+
+ def perform(group_id)
+ group = Group.find_by_id(group_id)
+
+ return unless group
+
+ group.update_two_factor_requirement_for_members
+ end
+ end
+end
diff --git a/config/feature_flags/development/mr_attention_requests.yml b/config/feature_flags/development/mr_attention_requests.yml
deleted file mode 100644
index b80300d1acf..00000000000
--- a/config/feature_flags/development/mr_attention_requests.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: mr_attention_requests
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72773
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343528
-milestone: '14.4'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index ff7221b5d9e..bc07d2f7f12 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -221,6 +221,8 @@
- 1
- - groups_update_statistics
- 1
+- - groups_update_two_factor_requirement_for_members
+ - 1
- - hashed_storage
- 1
- - import_issues_csv
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 68afb17f485..28f23900839 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -20190,7 +20190,6 @@ State of a review of a GitLab merge request.
| Value | Description |
| ----- | ----------- |
-| <a id="mergerequestreviewstateattention_requested"></a>`ATTENTION_REQUESTED` | The merge request is attention_requested. |
| <a id="mergerequestreviewstatereviewed"></a>`REVIEWED` | The merge request is reviewed. |
| <a id="mergerequestreviewstateunreviewed"></a>`UNREVIEWED` | The merge request is unreviewed. |
@@ -20864,8 +20863,6 @@ Name of the feature that the callout is for.
| Value | Description |
| ----- | ----------- |
| <a id="usercalloutfeaturenameenumactive_user_count_threshold"></a>`ACTIVE_USER_COUNT_THRESHOLD` | Callout feature name for active_user_count_threshold. |
-| <a id="usercalloutfeaturenameenumattention_requests_side_nav"></a>`ATTENTION_REQUESTS_SIDE_NAV` | Callout feature name for attention_requests_side_nav. |
-| <a id="usercalloutfeaturenameenumattention_requests_top_nav"></a>`ATTENTION_REQUESTS_TOP_NAV` | Callout feature name for attention_requests_top_nav. |
| <a id="usercalloutfeaturenameenumbuy_pipeline_minutes_notification_dot"></a>`BUY_PIPELINE_MINUTES_NOTIFICATION_DOT` | Callout feature name for buy_pipeline_minutes_notification_dot. |
| <a id="usercalloutfeaturenameenumcanary_deployment"></a>`CANARY_DEPLOYMENT` | Callout feature name for canary_deployment. |
| <a id="usercalloutfeaturenameenumci_deprecation_warning_for_types_keyword"></a>`CI_DEPRECATION_WARNING_FOR_TYPES_KEYWORD` | Callout feature name for ci_deprecation_warning_for_types_keyword. |
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index a491756e5f0..66a9a19e691 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -851,14 +851,6 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
"web_url": "http://localhost/user1"
},
- "updated_state_by": {
- "id": 1,
- "name": "John Doe1",
- "username": "user1",
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
- "web_url": "http://localhost/user1"
- },
"state": "unreviewed",
"created_at": "2022-07-27T17:03:27.684Z"
},
@@ -871,14 +863,6 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon",
"web_url": "http://localhost/user2"
},
- "updated_state_by": {
- "id": 1,
- "name": "John Doe1",
- "username": "user1",
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
- "web_url": "http://localhost/user1"
- },
"state": "reviewed",
"created_at": "2022-07-27T17:03:27.684Z"
}
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index 1976caefc8e..08544a9e745 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -394,7 +394,7 @@ Information in FAQs belongs with other similar information, under an easily sear
## field
-Use **box** instead of **field** or **text box**.
+Use **text box** instead of **field** or **box**.
Use:
diff --git a/doc/user/analytics/code_review_analytics.md b/doc/user/analytics/code_review_analytics.md
index dc02512702a..67ec5c41951 100644
--- a/doc/user/analytics/code_review_analytics.md
+++ b/doc/user/analytics/code_review_analytics.md
@@ -6,53 +6,32 @@ info: To determine the technical writer assigned to the Stage/Group associated w
---
-# Code Review Analytics **(PREMIUM)**
+# Code review analytics **(PREMIUM)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38062) in GitLab 12.7.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
-Use Code Review Analytics to view the longest-running reviews among open merge
-requests, and:
+Use code review analytics to view review metrics per merge request and
+make improvements to your code review process:
-- Take action on individual merge requests.
-- Reduce overall cycle time.
+- A high number of comments or commits may indicate:
+ - The code is too complex.
+ - Authors who require more training.
+- A long review time may indicate:
+ - Types of work that move slower than other types.
+ - Opportunities to accelerate your development cycle.
+- Fewer comments and approvers may indicate staffing requirements.
-Code Review Analytics is available to users with at least the Reporter role, and displays a table of open merge requests that have at least one non-author comment. The review time is measured from the time the first non-author comment was submitted.
+Code review analytics displays a table of open merge requests that have at least one non-author comment.
+The review time is measured from when the first non-author comment was submitted.
-NOTE:
-Initially, no data appears. Data is populated as users comment on open merge requests.
+## View code review analytics
-![Code Review Analytics](img/code_review_analytics_v13_11.png "List of code reviews; oldest review first.")
+Prerequisite:
-The table is sorted by:
+- You must have at least the Reporter role.
-- **Review time**: Helping you to quickly find the longest-running reviews which may need intervention
- or to be broken down into smaller parts.
-- Other columns: Display the author, approvers, comment count, and line change (-/+) counts.
-
-## View Code Review Analytics
-
-To view Code Review Analytics:
+To view code review analytics:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Code Review**.
1. Filter merge requests by milestone and label.
-
-## Use cases
-
-This feature is designed for [development team leaders](https://about.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
-and others who want to understand broad code review dynamics, and identify patterns to explain them.
-
-You can use Code Review Analytics to:
-
-- Expose your team's unique challenges with code review.
-- Identify improvements that might substantially accelerate your development cycle.
-- Your team agrees that code review is moving too slow.
-- The [Value Stream Analytics feature](value_stream_analytics.md) shows that reviews are your team's most time-consuming step.
-- Analyze the patterns and trends of different types of work that are moving slow.
-
-For example:
-
-- Lots of comments or commits? Maybe the code is too complex.
-- A particular author is involved? Maybe more training is required.
-- Few comments and approvers? Maybe your team is understaffed.
diff --git a/doc/user/analytics/img/code_review_analytics_v13_11.png b/doc/user/analytics/img/code_review_analytics_v13_11.png
deleted file mode 100644
index b559b934a89..00000000000
--- a/doc/user/analytics/img/code_review_analytics_v13_11.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index ef734225fb4..61b4b311e96 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -44,13 +44,22 @@ To view the milestone list:
In a project, GitLab displays milestones that belong to the project.
In a group, GitLab displays milestones that belong to the group and all projects in the group.
-NOTE:
+### View milestones in a project with issues turned off
+
If a project has issue tracking
[turned off](../settings/index.md#configure-project-visibility-features-and-permissions),
you can get to the milestones page
-by going to its URL. To do so, add: `/-/milestones` to your project or group URL.
-For example `https://gitlab.com/gitlab-org/sample-data-templates/sample-gitlab-project/-/milestones`.
-This is tracked in [issue 339009](https://gitlab.com/gitlab-org/gitlab/-/issues/339009).
+by going to its URL.
+
+To do so:
+
+1. Go to your project.
+1. Add: `/-/milestones` to your project URL.
+ For example `https://gitlab.com/gitlab-org/sample-data-templates/sample-gitlab-project/-/milestones`.
+
+Alternatively, this project's issues are visible in the group's milestone page.
+
+Improving this experience is tracked in issue [339009](https://gitlab.com/gitlab-org/gitlab/-/issues/339009).
### View all milestones
@@ -59,6 +68,45 @@ You might not see some milestones because they're in projects or groups you're n
To do so, on the top bar select **Menu > Milestones**.
+### View milestone details
+
+To view more information about a milestone,
+in the milestone list select the title of the milestone you want to view.
+
+The milestone view shows the title and description.
+
+There are also tabs below these that show the following:
+
+- **Issues**: Shows all issues assigned to the milestone. These are displayed in three columns named:
+ - Unstarted Issues (open and unassigned)
+ - Ongoing Issues (open and assigned)
+ - Completed Issues (closed)
+- **Merge Requests**: Shows all merge requests assigned to the milestone. These are displayed in four columns named:
+ - Work in progress (open and unassigned)
+ - Waiting for merge (open and assigned)
+ - Rejected (closed)
+ - Merged
+- **Participants**: Shows all assignees of issues assigned to the milestone.
+- **Labels**: Shows all labels that are used in issues assigned to the milestone.
+
+#### Burndown charts
+
+The milestone view contains a [burndown and burnup chart](burndown_and_burnup_charts.md),
+showing the progress of completing a milestone.
+
+![burndown chart](img/burndown_and_burnup_charts_v15_3.png)
+
+#### Milestone sidebar
+
+The milestone sidebar on the milestone view shows the following:
+
+- Percentage complete, which is calculated as number of closed issues divided by total number of issues.
+- The start date and due date.
+- The total time spent on all issues and merge requests assigned to the milestone.
+- The total issue weight of all issues assigned to the milestone.
+
+![Project milestone page](img/milestones_project_milestone_page_sidebar_v13_11.png)
+
## Create a milestone
> [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/343889) the minimum user role from Developer to Reporter in GitLab 15.0.
@@ -153,13 +201,13 @@ To assign or unassign a milestone:
You can also use the `/assign` [quick action](../quick_actions.md) in a comment.
-## Filtering issues and merge requests by milestone
+## Filter issues and merge requests by milestone
-### Filtering in list pages
+### Filters in list pages
From the project and group issue/merge request list pages, you can filter by both group and project milestones.
-### Filtering in issue boards
+### Filters in issue boards
From [project issue boards](../issue_board.md), you can filter by both group milestones and project
milestones in:
@@ -181,42 +229,6 @@ When filtering by milestone, in addition to choosing a specific project mileston
- **Upcoming**: Show issues or merge requests that have been assigned the open milestone and has the nearest due date in the future.
- **Started**: Show issues or merge requests that have an open assigned milestone with a start date that is before today.
-## Milestone view
-
-The milestone view shows the title and description.
-
-There are also tabs below these that show the following:
-
-- **Issues**: Shows all issues assigned to the milestone. These are displayed in three columns named:
- - Unstarted Issues (open and unassigned)
- - Ongoing Issues (open and assigned)
- - Completed Issues (closed)
-- **Merge Requests**: Shows all merge requests assigned to the milestone. These are displayed in four columns named:
- - Work in progress (open and unassigned)
- - Waiting for merge (open and assigned)
- - Rejected (closed)
- - Merged
-- **Participants**: Shows all assignees of issues assigned to the milestone.
-- **Labels**: Shows all labels that are used in issues assigned to the milestone.
-
-### Burndown Charts
-
-The milestone view contains a [burndown and burnup chart](burndown_and_burnup_charts.md),
-showing the progress of completing a milestone.
-
-![burndown chart](img/burndown_and_burnup_charts_v15_3.png)
-
-### Milestone sidebar
-
-The milestone sidebar on the milestone view shows the following:
-
-- Percentage complete, which is calculated as number of closed issues divided by total number of issues.
-- The start date and due date.
-- The total time spent on all issues and merge requests assigned to the milestone.
-- The total issue weight of all issues assigned to the milestone.
-
-![Project milestone page](img/milestones_project_milestone_page_sidebar_v13_11.png)
-
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/lib/api/entities/merge_request_reviewer.rb b/lib/api/entities/merge_request_reviewer.rb
index 3bf2ccc36aa..a47321ef929 100644
--- a/lib/api/entities/merge_request_reviewer.rb
+++ b/lib/api/entities/merge_request_reviewer.rb
@@ -4,7 +4,6 @@ module API
module Entities
class MergeRequestReviewer < Grape::Entity
expose :reviewer, as: :user, using: Entities::UserBasic
- expose :updated_state_by, using: Entities::UserBasic
expose :state
expose :created_at
end
diff --git a/lib/gitlab/ci/build/rules/rule/clause/exists.rb b/lib/gitlab/ci/build/rules/rule/clause/exists.rb
index e2b54797dc8..aebd81e7b07 100644
--- a/lib/gitlab/ci/build/rules/rule/clause/exists.rb
+++ b/lib/gitlab/ci/build/rules/rule/clause/exists.rb
@@ -24,7 +24,7 @@ module Gitlab
private
def worktree_paths(context)
- return unless context.project
+ return [] unless context.project
if @top_level_only
context.top_level_worktree_paths
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 5f1802e323c..08a614edb4b 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -56,7 +56,6 @@ module Gitlab
push_frontend_feature_flag(:new_header_search)
push_frontend_feature_flag(:source_editor_toolbar)
push_frontend_feature_flag(:gl_avatar_for_all_user_avatars)
- push_frontend_feature_flag(:mr_attention_requests, current_user)
end
# Exposes the state of a feature flag to the frontend code.
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 588152d0d64..97370eaee60 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -17959,6 +17959,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17977,6 +17983,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+msgstr ""
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -17989,6 +18004,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
diff --git a/package.json b/package.json
index 13f1d3ab361..c4eb5f69cd5 100644
--- a/package.json
+++ b/package.json
@@ -113,7 +113,7 @@
"dateformat": "^5.0.1",
"deckar01-task_list": "^2.3.1",
"diff": "^3.4.0",
- "dompurify": "^2.3.10",
+ "dompurify": "^2.4.0",
"dropzone": "^4.2.0",
"editorconfig": "^0.15.3",
"emoji-regex": "^10.0.0",
diff --git a/qa/qa/support/api.rb b/qa/qa/support/api.rb
index a1bbe9f378a..b91f9b10dfa 100644
--- a/qa/qa/support/api.rb
+++ b/qa/qa/support/api.rb
@@ -21,7 +21,7 @@ module QA
}
RestClient::Request.execute(default_args.merge(args))
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -37,7 +37,7 @@ module QA
RestClient::Request.execute(
default_args.merge(args)
)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -49,7 +49,7 @@ module QA
url: url,
payload: payload,
verify_ssl: false)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -64,7 +64,7 @@ module QA
}
RestClient::Request.execute(default_args.merge(args))
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -75,7 +75,7 @@ module QA
method: :delete,
url: url,
verify_ssl: false)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -86,11 +86,15 @@ module QA
method: :head,
url: url,
verify_ssl: false)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
+ def masked_url(url)
+ url.sub(/private_token=[^&]*/, "private_token=[****]")
+ end
+
def with_retry_on_too_many_requests
response = nil
@@ -115,7 +119,7 @@ module QA
end
def return_response_or_raise(error)
- raise error unless error.respond_to?(:response) && error.response
+ raise error, masked_url(error.to_s) unless error.respond_to?(:response) && error.response
error.response
end
@@ -129,7 +133,7 @@ module QA
def with_paginated_response_body(url, attempts: 0)
not_ok_error = lambda do |resp|
- raise "Failed to GET #{QA::Runtime::API::Request.masked_url(url)} - (#{resp.code}): `#{resp}`."
+ raise "Failed to GET #{masked_url(url)} - (#{resp.code}): `#{resp}`."
end
loop do
diff --git a/spec/finders/merge_requests_finder/params_spec.rb b/spec/finders/merge_requests_finder/params_spec.rb
deleted file mode 100644
index 8c285972f48..00000000000
--- a/spec/finders/merge_requests_finder/params_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe MergeRequestsFinder::Params do
- let(:user) { create(:user) }
-
- subject { described_class.new(params, user, MergeRequest) }
-
- describe 'attention' do
- context 'attention param exists' do
- let(:params) { { attention: user.username } }
-
- it { expect(subject.attention).to eq(user) }
- end
-
- context 'attention param does not exist' do
- let(:params) { { attention: nil } }
-
- it { expect(subject.attention).to eq(nil) }
- end
- end
-end
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 96466e99105..0b6c438fd02 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -408,25 +408,6 @@ RSpec.describe MergeRequestsFinder do
end
end
- context 'attention' do
- subject { described_class.new(user, params).execute }
-
- before do
- reviewer = merge_request1.find_reviewer(user2)
- reviewer.update!(state: :reviewed)
-
- merge_request2.find_reviewer(user2).update!(state: :attention_requested)
- merge_request3.find_assignee(user2).update!(state: :attention_requested)
- end
-
- context 'by username' do
- let(:params) { { attention: user2.username } }
- let(:expected_mr) { [merge_request2, merge_request3] }
-
- it { is_expected.to contain_exactly(*expected_mr) }
- end
- end
-
context 'reviewer filtering' do
subject { described_class.new(user, params).execute }
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/project.json b/spec/fixtures/lib/gitlab/import_export/complex/project.json
index 5bcf6521471..df571ab1326 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/project.json
+++ b/spec/fixtures/lib/gitlab/import_export/complex/project.json
@@ -3164,7 +3164,7 @@
{
"user_id": 16,
"created_at": "2020-01-09T11:21:21.235Z",
- "state": "attention_requested"
+ "state": "reviewed"
},
{
"user_id": 6,
@@ -3186,7 +3186,7 @@
{
"user_id": 16,
"created_at": "2020-01-09T11:21:21.235Z",
- "state": "attention_requested"
+ "state": "reviewed"
},
{
"user_id": 6,
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson
index c14221adc1c..3388e315922 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson
+++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson
@@ -1,4 +1,4 @@
-{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n<ul><li>16ea4e20...074a2a32 - 2 commits from branch <code>master</code></li><li>ca223a02 - readme: fix typos</li></ul>\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"<p>something else entirely</p>","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-08-06T08:35:52.000+02:00","committed_date":"2014-08-06T08:35:52.000+02:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}],"merge_request_assignees":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"attention_requested"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"merge_request_reviewers":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"attention_requested"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"approvals":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":15,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":16,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":6,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"}]}
+{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n<ul><li>16ea4e20...074a2a32 - 2 commits from branch <code>master</code></li><li>ca223a02 - readme: fix typos</li></ul>\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"<p>something else entirely</p>","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-08-06T08:35:52.000+02:00","committed_date":"2014-08-06T08:35:52.000+02:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}],"merge_request_assignees":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"merge_request_reviewers":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"approvals":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":15,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":16,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":6,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"}]}
{"id":26,"target_branch":"master","source_branch":"feature","source_project_id":4,"author_id":1,"assignee_id":null,"title":"MR2","created_at":"2016-06-14T15:02:36.418Z","updated_at":"2016-06-14T15:02:57.013Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":8,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":679,"note":"Qui rerum totam nisi est.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.848Z","updated_at":"2016-06-14T15:02:56.848Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":680,"note":"Pariatur magni corrupti consequatur debitis minima error beatae voluptatem.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.871Z","updated_at":"2016-06-14T15:02:56.871Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":681,"note":"Qui quis ut modi eos rerum ratione.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.895Z","updated_at":"2016-06-14T15:02:56.895Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":682,"note":"Illum quidem expedita mollitia fugit.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.918Z","updated_at":"2016-06-14T15:02:56.918Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":683,"note":"Consectetur voluptate sit sint possimus veritatis quod.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.942Z","updated_at":"2016-06-14T15:02:56.942Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":684,"note":"Natus libero quibusdam rem assumenda deleniti accusamus sed earum.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.966Z","updated_at":"2016-06-14T15:02:56.966Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":685,"note":"Tenetur autem nihil rerum odit.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.989Z","updated_at":"2016-06-14T15:02:56.989Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":686,"note":"Quia maiores et odio sed.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:57.012Z","updated_at":"2016-06-14T15:02:57.012Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":26,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":26,"sha":"0b4bc9a49b562e85de7cc9e834518ea6828729b9","relative_order":0,"message":"Feature added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:26:01.000+01:00","committed_date":"2014-02-27T09:26:01.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":26,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,5 @@\n+class Feature\n+ def foo\n+ puts 'bar'\n+ end\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":26,"created_at":"2016-06-14T15:02:36.421Z","updated_at":"2016-06-14T15:02:36.474Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"1"},"events":[{"id":222,"target_type":"MergeRequest","target_id":26,"project_id":36,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1},{"id":186,"target_type":"MergeRequest","target_id":26,"project_id":5,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1}],"merge_request_assignees":[],"merge_request_reviewers":[],"approvals":[]}
{"id":15,"target_branch":"test-7","source_branch":"test-1","source_project_id":5,"author_id":22,"assignee_id":16,"title":"Qui accusantium et inventore facilis doloribus occaecati officiis.","created_at":"2016-06-14T15:02:25.168Z","updated_at":"2016-06-14T15:02:59.521Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":7,"description":"Et commodi deserunt aspernatur vero rerum. Ut non dolorum alias in odit est libero. Voluptatibus eos in et vitae repudiandae facilis ex mollitia.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":777,"note":"Pariatur voluptas placeat aspernatur culpa suscipit soluta.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.348Z","updated_at":"2016-06-14T15:02:59.348Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":778,"note":"Alias et iure mollitia suscipit molestiae voluptatum nostrum asperiores.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.372Z","updated_at":"2016-06-14T15:02:59.372Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":779,"note":"Laudantium qui eum qui sunt.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.395Z","updated_at":"2016-06-14T15:02:59.395Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":780,"note":"Quas rem est iusto ut delectus fugiat recusandae mollitia.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.418Z","updated_at":"2016-06-14T15:02:59.418Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":781,"note":"Repellendus ab et qui nesciunt.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.444Z","updated_at":"2016-06-14T15:02:59.444Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":782,"note":"Non possimus voluptatum odio qui ut.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.469Z","updated_at":"2016-06-14T15:02:59.469Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":783,"note":"Dolores repellendus eum ducimus quam ab dolorem quia.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.494Z","updated_at":"2016-06-14T15:02:59.494Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":784,"note":"Facilis dolorem aut corrupti id ratione occaecati.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.520Z","updated_at":"2016-06-14T15:02:59.520Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":15,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":15,"relative_order":0,"sha":"94b8d581c48d894b86661718582fecbc5e3ed2eb","message":"fixes #10\n","authored_date":"2016-01-19T13:22:56.000+01:00","committed_date":"2016-01-19T13:22:56.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}}],"merge_request_diff_files":[{"merge_request_diff_id":15,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":15,"created_at":"2016-06-14T15:02:25.171Z","updated_at":"2016-06-14T15:02:25.230Z","base_commit_sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","real_size":"1"},"events":[{"id":223,"target_type":"MergeRequest","target_id":15,"project_id":36,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":1},{"id":175,"target_type":"MergeRequest","target_id":15,"project_id":5,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":22}]}
{"id":14,"target_branch":"fix","source_branch":"test-3","source_project_id":5,"author_id":20,"assignee_id":20,"title":"In voluptas aut sequi voluptatem ullam vel corporis illum consequatur.","created_at":"2016-06-14T15:02:24.760Z","updated_at":"2016-06-14T15:02:59.749Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":6,"description":"Dicta magnam non voluptates nam dignissimos nostrum deserunt. Dolorum et suscipit iure quae doloremque. Necessitatibus saepe aut labore sed.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":785,"note":"Atque cupiditate necessitatibus deserunt minus natus odit.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.559Z","updated_at":"2016-06-14T15:02:59.559Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":786,"note":"Non dolorem provident mollitia nesciunt optio ex eveniet.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.587Z","updated_at":"2016-06-14T15:02:59.587Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":787,"note":"Similique officia nemo quasi commodi accusantium quae qui.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.621Z","updated_at":"2016-06-14T15:02:59.621Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":788,"note":"Et est et alias ad dolor qui.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.650Z","updated_at":"2016-06-14T15:02:59.650Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":789,"note":"Numquam temporibus ratione voluptatibus aliquid.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.675Z","updated_at":"2016-06-14T15:02:59.675Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":790,"note":"Ut ex aliquam consectetur perferendis est hic aut quia.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.703Z","updated_at":"2016-06-14T15:02:59.703Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":791,"note":"Esse eos quam quaerat aut ut asperiores officiis.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.726Z","updated_at":"2016-06-14T15:02:59.726Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":792,"note":"Sint facilis accusantium iure blanditiis.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.748Z","updated_at":"2016-06-14T15:02:59.748Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":14,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":14,"relative_order":0,"sha":"ddd4ff416a931589c695eb4f5b23f844426f6928","message":"fixes #10\n","authored_date":"2016-01-19T14:14:43.000+01:00","committed_date":"2016-01-19T14:14:43.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}},{"merge_request_diff_id":14,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","committed_date":"2015-12-07T12:52:12.000+01:00","commit_author":{"name":"Marin Jankovski","email":"marin@gitlab.com"},"committer":{"name":"Marin Jankovski","email":"marin@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","committed_date":"2015-12-07T11:54:28.000+01:00","commit_author":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"},"committer":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"}},{"merge_request_diff_id":14,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","committed_date":"2015-11-13T16:27:12.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","committed_date":"2015-11-13T08:50:17.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","committed_date":"2015-11-13T08:39:43.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","committed_date":"2015-11-13T07:21:40.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","committed_date":"2015-11-13T06:01:27.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","committed_date":"2015-11-13T06:00:16.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","committed_date":"2015-11-13T05:23:14.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","committed_date":"2015-11-13T05:08:45.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","committed_date":"2015-11-13T05:08:04.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","committed_date":"2015-08-25T17:53:12.000+02:00","commit_author":{"name":"Stan Hu","email":"stanhu@packetzoom.com"},"committer":{"name":"Stan Hu","email":"stanhu@packetzoom.com"}},{"merge_request_diff_id":14,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","committed_date":"2015-01-10T22:23:29.000+01:00","commit_author":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"},"committer":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","committed_date":"2015-01-10T21:28:18.000+01:00","commit_author":{"name":"marmis85","email":"marmis85@gmail.com"},"committer":{"name":"marmis85","email":"marmis85@gmail.com"}},{"merge_request_diff_id":14,"relative_order":15,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":16,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":17,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":18,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":19,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":14,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":3,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":5,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n+<svg width=\"1300px\" height=\"680px\" viewBox=\"0 0 1300 680\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\">\n+ <!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->\n+ <title>wm</title>\n+ <desc>Created with Sketch.</desc>\n+ <defs>\n+ <path id=\"path-1\" d=\"M-69.8,1023.54607 L1675.19996,1023.54607 L1675.19996,0 L-69.8,0 L-69.8,1023.54607 L-69.8,1023.54607 Z\"></path>\n+ </defs>\n+ <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\">\n+ <path d=\"M1300,680 L0,680 L0,0 L1300,0 L1300,680 L1300,680 Z\" id=\"bg\" fill=\"#30353E\" sketch:type=\"MSShapeGroup\"></path>\n+ <g id=\"gitlab_logo\" sketch:type=\"MSLayerGroup\" transform=\"translate(-262.000000, -172.000000)\">\n+ <g id=\"g10\" transform=\"translate(872.500000, 512.354581) scale(1, -1) translate(-872.500000, -512.354581) translate(0.000000, 0.290751)\">\n+ <g id=\"g12\" transform=\"translate(1218.022652, 440.744871)\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\">\n+ <path d=\"M-50.0233338,141.900706 L-69.07059,141.900706 L-69.0100967,0.155858152 L8.04444805,0.155858152 L8.04444805,17.6840847 L-49.9628405,17.6840847 L-50.0233338,141.900706 L-50.0233338,141.900706 Z\" id=\"path14\"></path>\n+ </g>\n+ <g id=\"g16\">\n+ <g id=\"g18-Clipped\">\n+ <mask id=\"mask-2\" sketch:name=\"path22\" fill=\"white\">\n+ <use xlink:href=\"#path-1\"></use>\n+ </mask>\n+ <g id=\"path22\"></g>\n+ <g id=\"g18\" mask=\"url(#mask-2)\">\n+ <g transform=\"translate(382.736659, 312.879425)\">\n+ <g id=\"g24\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(852.718192, 124.992771)\">\n+ <path d=\"M63.9833317,27.9148929 C59.2218085,22.9379001 51.2134221,17.9597442 40.3909323,17.9597442 C25.8888194,17.9597442 20.0453962,25.1013043 20.0453962,34.4074318 C20.0453962,48.4730484 29.7848226,55.1819277 50.5642821,55.1819277 C54.4602853,55.1819277 60.7364685,54.7492469 63.9833317,54.1002256 L63.9833317,27.9148929 L63.9833317,27.9148929 Z M44.2869356,113.827628 C28.9053426,113.827628 14.7975996,108.376082 3.78897657,99.301416 L10.5211864,87.6422957 C18.3131929,92.1866076 27.8374026,96.7320827 41.4728323,96.7320827 C57.0568452,96.7320827 63.9833317,88.7239978 63.9833317,75.3074024 L63.9833317,68.3821827 C60.9528485,69.0312039 54.6766653,69.4650479 50.7806621,69.4650479 C17.4476729,69.4650479 0.565379986,57.7791759 0.565379986,33.3245665 C0.565379986,11.4683685 13.9844297,0.43151772 34.3299658,0.43151772 C48.0351955,0.43151772 61.1692285,6.70771614 65.7143717,16.8780421 L69.1776149,3.02876588 L82.5978279,3.02876588 L82.5978279,75.5237428 C82.5978279,98.462806 72.6408582,113.827628 44.2869356,113.827628 L44.2869356,113.827628 Z\" id=\"path26\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g28\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(959.546624, 124.857151)\">\n+ <path d=\"M37.2266657,17.4468081 C30.0837992,17.4468081 23.8064527,18.3121698 19.0449295,20.4767371 L19.0449295,79.2306079 L19.0449295,86.0464943 C25.538656,91.457331 33.5470425,95.3526217 43.7203922,95.3526217 C62.1173451,95.3526217 69.2602116,82.3687072 69.2602116,61.3767077 C69.2602116,31.5135879 57.7885819,17.4468081 37.2266657,17.4468081 M45.2315622,113.963713 C28.208506,113.963713 19.0449295,102.384849 19.0449295,102.384849 L19.0449295,120.67143 L18.9844362,144.908535 L10.3967097,144.908535 L0.371103324,144.908535 L0.431596656,6.62629771 C9.73826309,2.73100702 22.5081728,0.567602823 36.3611458,0.567602823 C71.8579349,0.567602823 88.9566078,23.2891625 88.9566078,62.4584098 C88.9566078,93.4043948 73.1527248,113.963713 45.2315622,113.963713\" id=\"path30\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g32\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(509.576747, 125.294950)\">\n+ <path d=\"M68.636665,129.10638 C85.5189579,129.10638 96.3414476,123.480366 103.484314,117.853189 L111.669527,132.029302 C100.513161,141.811145 85.5073245,147.06845 69.5021849,147.06845 C29.0274926,147.06845 0.673569983,122.3975 0.673569983,72.6252464 C0.673569983,20.4709215 31.2622559,0.12910638 66.2553217,0.12910638 C83.7879179,0.12910638 98.7227909,4.24073748 108.462217,8.35236859 L108.063194,64.0763105 L108.063194,70.6502677 L108.063194,81.6057001 L56.1168719,81.6057001 L56.1168719,64.0763105 L89.2323178,64.0763105 L89.6313411,21.7701271 C85.3025779,19.6055598 77.7269514,17.8748364 67.554765,17.8748364 C39.4172223,17.8748364 20.5863462,35.5717154 20.5863462,72.8415868 C20.5863462,110.711628 40.0663623,129.10638 68.636665,129.10638\" id=\"path34\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g36\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(692.388992, 124.376085)\">\n+ <path d=\"M19.7766662,145.390067 L1.16216997,145.390067 L1.2226633,121.585642 L1.2226633,111.846834 L1.2226633,106.170806 L1.2226633,96.2656714 L1.2226633,39.5681976 L1.2226633,39.3518572 C1.2226633,16.4127939 11.1796331,1.04797161 39.5335557,1.04797161 C43.4504989,1.04797161 47.2836822,1.40388649 51.0051854,2.07965952 L51.0051854,18.7925385 C48.3109055,18.3796307 45.4351455,18.1446804 42.3476589,18.1446804 C26.763646,18.1446804 19.8371595,26.1516022 19.8371595,39.5681976 L19.8371595,96.2656714 L51.0051854,96.2656714 L51.0051854,111.846834 L19.8371595,111.846834 L19.7766662,145.390067 L19.7766662,145.390067 Z\" id=\"path38\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <path d=\"M646.318899,128.021188 L664.933395,128.021188 L664.933395,236.223966 L646.318899,236.223966 L646.318899,128.021188 L646.318899,128.021188 Z\" id=\"path40\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ <path d=\"M646.318899,251.154944 L664.933395,251.154944 L664.933395,269.766036 L646.318899,269.766036 L646.318899,251.154944 L646.318899,251.154944 Z\" id=\"path42\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ <g id=\"g44\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(0.464170, 0.676006)\">\n+ <path d=\"M429.269989,169.815599 L405.225053,243.802859 L357.571431,390.440955 C355.120288,397.984955 344.444378,397.984955 341.992071,390.440955 L294.337286,243.802859 L136.094873,243.802859 L88.4389245,390.440955 C85.9877812,397.984955 75.3118715,397.984955 72.8595648,390.440955 L25.2059427,243.802859 L1.16216997,169.815599 C-1.03187664,163.067173 1.37156997,155.674379 7.11261982,151.503429 L215.215498,0.336141836 L423.319539,151.503429 C429.060589,155.674379 431.462873,163.067173 429.269989,169.815599\" id=\"path46\" fill=\"#FC6D26\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g48\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(135.410135, 1.012147)\">\n+ <path d=\"M80.269998,0 L80.269998,0 L159.391786,243.466717 L1.14820997,243.466717 L80.269998,0 L80.269998,0 Z\" id=\"path50\" fill=\"#E24329\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g52\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012147)\">\n+ <g id=\"path54\"></g>\n+ </g>\n+ <g id=\"g56\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(24.893471, 1.012613)\">\n+ <path d=\"M190.786662,0 L111.664874,243.465554 L0.777106647,243.465554 L190.786662,0 L190.786662,0 Z\" id=\"path58\" fill=\"#FC6D26\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g60\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012613)\">\n+ <g id=\"path62\"></g>\n+ </g>\n+ <g id=\"g64\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(0.077245, 0.223203)\">\n+ <path d=\"M25.5933327,244.255313 L25.5933327,244.255313 L1.54839663,170.268052 C-0.644486651,163.519627 1.75779662,156.126833 7.50000981,151.957046 L215.602888,0.789758846 L25.5933327,244.255313 L25.5933327,244.255313 Z\" id=\"path66\" fill=\"#FCA326\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g68\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012147)\">\n+ <g id=\"path70\"></g>\n+ </g>\n+ <g id=\"g72\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(25.670578, 244.478283)\">\n+ <path d=\"M0,0 L110.887767,0 L63.2329818,146.638096 C60.7806751,154.183259 50.1047654,154.183259 47.6536221,146.638096 L0,0 L0,0 Z\" id=\"path74\" fill=\"#E24329\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g76\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012613)\">\n+ <path d=\"M0,0 L79.121788,243.465554 L190.009555,243.465554 L0,0 L0,0 Z\" id=\"path78\" fill=\"#FC6D26\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g80\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(214.902910, 0.223203)\">\n+ <path d=\"M190.786662,244.255313 L190.786662,244.255313 L214.831598,170.268052 C217.024481,163.519627 214.622198,156.126833 208.879985,151.957046 L0.777106647,0.789758846 L190.786662,244.255313 L190.786662,244.255313 Z\" id=\"path82\" fill=\"#FCA326\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g84\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(294.009575, 244.478283)\">\n+ <path d=\"M111.679997,0 L0.79222998,0 L48.4470155,146.638096 C50.8993221,154.183259 61.5752318,154.183259 64.0263751,146.638096 L111.679997,0 L111.679997,0 Z\" id=\"path86\" fill=\"#E24329\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+</svg>\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":8,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":9,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":10,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":11,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":12,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":13,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":14,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":14,"created_at":"2016-06-14T15:02:24.770Z","updated_at":"2016-06-14T15:02:25.007Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"15"},"events":[{"id":224,"target_type":"MergeRequest","target_id":14,"project_id":36,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":1},{"id":174,"target_type":"MergeRequest","target_id":14,"project_id":5,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":20}]}
diff --git a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
index e5019e3261e..94a1c5e16e7 100644
--- a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js
@@ -11,6 +11,7 @@ import {
EVENT_ACTION,
ENVIRONMENT_SCOPE_LINK_TITLE,
instanceString,
+ variableOptions,
} from '~/ci_variable_list/constants';
import { mockVariablesWithScopes } from '../mocks';
import ModalStub from '../stubs';
@@ -72,6 +73,7 @@ describe('Ci variable modal', () => {
const findValueField = () => wrapper.find('#ci-variable-value');
const findEnvScopeLink = () => wrapper.findByTestId('environment-scope-link');
const findEnvScopeInput = () => wrapper.findByTestId('environment-scope').find(GlFormInput);
+ const findVariableTypeDropdown = () => wrapper.find('#ci-variable-type');
afterEach(() => {
wrapper.destroy();
@@ -284,6 +286,21 @@ describe('Ci variable modal', () => {
});
});
+ describe('variable type dropdown', () => {
+ describe('default behaviour', () => {
+ beforeEach(() => {
+ createComponent({ mountFn: mountExtended });
+ });
+
+ it('adds each option as a dropdown item', () => {
+ expect(findVariableTypeDropdown().findAll('option')).toHaveLength(variableOptions.length);
+ variableOptions.forEach((v) => {
+ expect(findVariableTypeDropdown().text()).toContain(v.text);
+ });
+ });
+ });
+ });
+
describe('Validations', () => {
const maskError = 'This variable can not be masked.';
diff --git a/spec/frontend/ci_variable_list/mocks.js b/spec/frontend/ci_variable_list/mocks.js
index b030005cb8d..6d633c8b740 100644
--- a/spec/frontend/ci_variable_list/mocks.js
+++ b/spec/frontend/ci_variable_list/mocks.js
@@ -16,8 +16,8 @@ export const mockVariables = (kind) => {
key: 'my-var',
masked: false,
protected: true,
- value: 'env_val',
- variableType: variableTypes.variableType,
+ value: 'variable_value',
+ variableType: variableTypes.envType,
},
{
__typename: `Ci${kind}Variable`,
@@ -25,7 +25,7 @@ export const mockVariables = (kind) => {
key: 'secret',
masked: true,
protected: false,
- value: 'the_secret_value',
+ value: 'another_value',
variableType: variableTypes.fileType,
},
];
diff --git a/spec/frontend/crm/mock_data.js b/spec/frontend/crm/mock_data.js
index a2e2e88ac60..a19ee01c2a5 100644
--- a/spec/frontend/crm/mock_data.js
+++ b/spec/frontend/crm/mock_data.js
@@ -102,6 +102,13 @@ export const getGroupOrganizationsQueryResponse = {
active: true,
},
],
+ pageInfo: {
+ __typename: 'PageInfo',
+ hasNextPage: false,
+ endCursor: 'eyJsYXN0X25hbWUiOiJMZWRuZXIiLCJpZCI6IjE3OSJ9',
+ hasPreviousPage: false,
+ startCursor: 'eyJsYXN0X25hbWUiOiJCYXJ0b24iLCJpZCI6IjE5MyJ9',
+ },
},
},
},
@@ -155,6 +162,21 @@ export const updateContactMutationResponse = {
},
};
+export const getGroupOrganizationsCountQueryResponse = {
+ data: {
+ group: {
+ __typename: 'Group',
+ id: 'gid://gitlab/Group/26',
+ organizationStateCounts: {
+ all: 24,
+ active: 21,
+ inactive: 3,
+ __typename: 'OrganizationStateCountsType',
+ },
+ },
+ },
+};
+
export const updateContactMutationErrorResponse = {
data: {
customerRelationsContactUpdate: {
diff --git a/spec/frontend/crm/organizations_root_spec.js b/spec/frontend/crm/organizations_root_spec.js
index 1780a5945a6..a0b56596177 100644
--- a/spec/frontend/crm/organizations_root_spec.js
+++ b/spec/frontend/crm/organizations_root_spec.js
@@ -1,14 +1,19 @@
-import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
-import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import OrganizationsRoot from '~/crm/organizations/components/organizations_root.vue';
import routes from '~/crm/organizations/routes';
import getGroupOrganizationsQuery from '~/crm/organizations/components/graphql/get_group_organizations.query.graphql';
-import { getGroupOrganizationsQueryResponse } from './mock_data';
+import getGroupOrganizationsCountByStateQuery from '~/crm/organizations/components/graphql/get_group_organizations_count_by_state.query.graphql';
+import PaginatedTableWithSearchAndTabs from '~/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue';
+import {
+ getGroupOrganizationsQueryResponse,
+ getGroupOrganizationsCountQueryResponse,
+} from './mock_data';
describe('Customer relations organizations root app', () => {
Vue.use(VueApollo);
@@ -21,23 +26,31 @@ describe('Customer relations organizations root app', () => {
const findRowByName = (rowName) => wrapper.findAllByRole('row', { name: rowName });
const findIssuesLinks = () => wrapper.findAllByTestId('issues-link');
const findNewOrganizationButton = () => wrapper.findByTestId('new-organization-button');
- const findError = () => wrapper.findComponent(GlAlert);
+ const findTable = () => wrapper.findComponent(PaginatedTableWithSearchAndTabs);
const successQueryHandler = jest.fn().mockResolvedValue(getGroupOrganizationsQueryResponse);
+ const successCountQueryHandler = jest
+ .fn()
+ .mockResolvedValue(getGroupOrganizationsCountQueryResponse);
const basePath = '/groups/flightjs/-/crm/organizations';
const mountComponent = ({
queryHandler = successQueryHandler,
- mountFunction = shallowMountExtended,
+ countQueryHandler = successCountQueryHandler,
canAdminCrmOrganization = true,
+ textQuery = null,
} = {}) => {
- fakeApollo = createMockApollo([[getGroupOrganizationsQuery, queryHandler]]);
- wrapper = mountFunction(OrganizationsRoot, {
+ fakeApollo = createMockApollo([
+ [getGroupOrganizationsQuery, queryHandler],
+ [getGroupOrganizationsCountByStateQuery, countQueryHandler],
+ ]);
+ wrapper = mountExtended(OrganizationsRoot, {
router,
provide: {
canAdminCrmOrganization,
groupFullPath: 'flightjs',
groupIssuesPath: '/issues',
+ textQuery,
},
apolloProvider: fakeApollo,
});
@@ -57,9 +70,33 @@ describe('Customer relations organizations root app', () => {
router = null;
});
- it('should render loading spinner', () => {
+ it('should render table with default props and loading spinner', () => {
mountComponent();
+ expect(findTable().props()).toMatchObject({
+ items: [],
+ itemsCount: {},
+ pageInfo: {},
+ statusTabs: [
+ { title: 'Active', status: 'ACTIVE', filters: 'active' },
+ { title: 'Inactive', status: 'INACTIVE', filters: 'inactive' },
+ { title: 'All', status: 'ALL', filters: 'all' },
+ ],
+ showItems: true,
+ showErrorMsg: false,
+ trackViewsOptions: { category: 'Customer Relations', action: 'view_organizations_list' },
+ i18n: {
+ emptyText: 'No organizations found',
+ issuesButtonLabel: 'View issues',
+ editButtonLabel: 'Edit',
+ title: 'Customer relations organizations',
+ newOrganization: 'New organization',
+ errorText: 'Something went wrong. Please try again.',
+ },
+ serverErrorMessage: '',
+ filterSearchKey: 'organizations',
+ filterSearchTokens: [],
+ });
expect(findLoadingIcon().exists()).toBe(true);
});
@@ -77,11 +114,25 @@ describe('Customer relations organizations root app', () => {
});
});
- it('should render error message on reject', async () => {
- mountComponent({ queryHandler: jest.fn().mockRejectedValue('ERROR') });
- await waitForPromises();
+ describe('error', () => {
+ it('should render on reject', async () => {
+ mountComponent({ queryHandler: jest.fn().mockRejectedValue('ERROR') });
+ await waitForPromises();
+
+ expect(wrapper.text()).toContain('Something went wrong. Please try again.');
+ });
+
+ it('should be removed on error-alert-dismissed event', async () => {
+ mountComponent({ queryHandler: jest.fn().mockRejectedValue('ERROR') });
+ await waitForPromises();
- expect(findError().exists()).toBe(true);
+ expect(wrapper.text()).toContain('Something went wrong. Please try again.');
+
+ findTable().vm.$emit('error-alert-dismissed');
+ await waitForPromises();
+
+ expect(wrapper.text()).not.toContain('Something went wrong. Please try again.');
+ });
});
describe('on successful load', () => {
@@ -89,20 +140,27 @@ describe('Customer relations organizations root app', () => {
mountComponent();
await waitForPromises();
- expect(findError().exists()).toBe(false);
+ expect(wrapper.text()).not.toContain('Something went wrong. Please try again.');
});
it('renders correct results', async () => {
- mountComponent({ mountFunction: mountExtended });
+ mountComponent();
await waitForPromises();
expect(findRowByName(/Test Inc/i)).toHaveLength(1);
expect(findRowByName(/VIP/i)).toHaveLength(1);
expect(findRowByName(/120/i)).toHaveLength(1);
- const issueLink = findIssuesLinks().at(0);
- expect(issueLink.exists()).toBe(true);
- expect(issueLink.attributes('href')).toBe('/issues?crm_organization_id=2');
+ expect(findIssuesLinks()).toHaveLength(3);
+
+ const links = findIssuesLinks().wrappers.map((w) => w.attributes('href'));
+ expect(links).toEqual(
+ expect.arrayContaining([
+ '/issues?crm_organization_id=1',
+ '/issues?crm_organization_id=2',
+ '/issues?crm_organization_id=3',
+ ]),
+ );
});
});
});
diff --git a/spec/frontend/header_search/mock_data.js b/spec/frontend/header_search/mock_data.js
index 8ccd7fb17e3..3a8624ad9dd 100644
--- a/spec/frontend/header_search/mock_data.js
+++ b/spec/frontend/header_search/mock_data.js
@@ -223,6 +223,20 @@ export const MOCK_AUTOCOMPLETE_OPTIONS = [
export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [
{
+ category: 'Groups',
+ data: [
+ {
+ category: 'Groups',
+ html_id: 'autocomplete-Groups-1',
+
+ id: 1,
+ label: 'Gitlab Org / MockGroup1',
+ value: 'MockGroup1',
+ url: 'group/1',
+ },
+ ],
+ },
+ {
category: 'Projects',
data: [
{
@@ -246,20 +260,6 @@ export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [
],
},
{
- category: 'Groups',
- data: [
- {
- category: 'Groups',
- html_id: 'autocomplete-Groups-1',
-
- id: 1,
- label: 'Gitlab Org / MockGroup1',
- value: 'MockGroup1',
- url: 'group/1',
- },
- ],
- },
- {
category: 'Help',
data: [
{
@@ -275,6 +275,14 @@ export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [
export const MOCK_SORTED_AUTOCOMPLETE_OPTIONS = [
{
+ category: 'Groups',
+ html_id: 'autocomplete-Groups-1',
+ id: 1,
+ label: 'Gitlab Org / MockGroup1',
+ value: 'MockGroup1',
+ url: 'group/1',
+ },
+ {
category: 'Projects',
html_id: 'autocomplete-Projects-0',
id: 1,
@@ -291,14 +299,6 @@ export const MOCK_SORTED_AUTOCOMPLETE_OPTIONS = [
url: 'project/2',
},
{
- category: 'Groups',
- html_id: 'autocomplete-Groups-1',
- id: 1,
- label: 'Gitlab Org / MockGroup1',
- value: 'MockGroup1',
- url: 'group/1',
- },
- {
category: 'Help',
html_id: 'autocomplete-Help-3',
label: 'GitLab Help',
diff --git a/spec/frontend/header_search/store/actions_spec.js b/spec/frontend/header_search/store/actions_spec.js
index 1748d89a6d3..1ae149128ca 100644
--- a/spec/frontend/header_search/store/actions_spec.js
+++ b/spec/frontend/header_search/store/actions_spec.js
@@ -2,9 +2,18 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import * as actions from '~/header_search/store/actions';
import * as types from '~/header_search/store/mutation_types';
-import createState from '~/header_search/store/state';
+import initState from '~/header_search/store/state';
import axios from '~/lib/utils/axios_utils';
-import { MOCK_SEARCH, MOCK_AUTOCOMPLETE_OPTIONS_RES } from '../mock_data';
+import {
+ MOCK_SEARCH,
+ MOCK_AUTOCOMPLETE_OPTIONS_RES,
+ MOCK_AUTOCOMPLETE_PATH,
+ MOCK_PROJECT,
+ MOCK_SEARCH_CONTEXT,
+ MOCK_SEARCH_PATH,
+ MOCK_MR_PATH,
+ MOCK_ISSUE_PATH,
+} from '../mock_data';
jest.mock('~/flash');
@@ -12,10 +21,15 @@ describe('Header Search Store Actions', () => {
let state;
let mock;
- beforeEach(() => {
- state = createState({});
- mock = new MockAdapter(axios);
- });
+ const createState = (initialState) =>
+ initState({
+ searchPath: MOCK_SEARCH_PATH,
+ issuesPath: MOCK_ISSUE_PATH,
+ mrPath: MOCK_MR_PATH,
+ autocompletePath: MOCK_AUTOCOMPLETE_PATH,
+ searchContext: MOCK_SEARCH_CONTEXT,
+ ...initialState,
+ });
afterEach(() => {
state = null;
@@ -24,12 +38,14 @@ describe('Header Search Store Actions', () => {
describe.each`
axiosMock | type | expectedMutations
- ${{ method: 'onGet', code: 200, res: MOCK_AUTOCOMPLETE_OPTIONS_RES }} | ${'success'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_SUCCESS, payload: MOCK_AUTOCOMPLETE_OPTIONS_RES }]}
- ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_ERROR }]}
+ ${{ method: 'onGet', code: 200, res: MOCK_AUTOCOMPLETE_OPTIONS_RES }} | ${'success'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_SUCCESS, payload: MOCK_AUTOCOMPLETE_OPTIONS_RES }, { type: types.RECEIVE_AUTOCOMPLETE_SUCCESS, payload: MOCK_AUTOCOMPLETE_OPTIONS_RES }]}
+ ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_ERROR }, { type: types.RECEIVE_AUTOCOMPLETE_ERROR }]}
`('fetchAutocompleteOptions', ({ axiosMock, type, expectedMutations }) => {
describe(`on ${type}`, () => {
beforeEach(() => {
- mock[axiosMock.method]().replyOnce(axiosMock.code, axiosMock.res);
+ state = createState({});
+ mock = new MockAdapter(axios);
+ mock[axiosMock.method]().reply(axiosMock.code, axiosMock.res);
});
it(`should dispatch the correct mutations`, () => {
return testAction({
@@ -41,7 +57,35 @@ describe('Header Search Store Actions', () => {
});
});
+ describe.each`
+ project | ref | fetchType | expectedPath
+ ${null} | ${null} | ${null} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}`}
+ ${MOCK_PROJECT} | ${null} | ${'generic'} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_id=${MOCK_PROJECT.id}&filter=generic`}
+ ${null} | ${MOCK_PROJECT.id} | ${'generic'} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_ref=${MOCK_PROJECT.id}&filter=generic`}
+ ${MOCK_PROJECT} | ${MOCK_PROJECT.id} | ${'search'} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_id=${MOCK_PROJECT.id}&project_ref=${MOCK_PROJECT.id}&filter=search`}
+ `('autocompleteQuery', ({ project, ref, fetchType, expectedPath }) => {
+ describe(`when project is ${project?.name} and project ref is ${ref}`, () => {
+ beforeEach(() => {
+ state = createState({
+ search: MOCK_SEARCH,
+ searchContext: {
+ project,
+ ref,
+ },
+ });
+ });
+
+ it(`should return ${expectedPath}`, () => {
+ expect(actions.autocompleteQuery({ state, fetchType })).toBe(expectedPath);
+ });
+ });
+ });
+
describe('clearAutocomplete', () => {
+ beforeEach(() => {
+ state = createState({});
+ });
+
it('calls the CLEAR_AUTOCOMPLETE mutation', () => {
return testAction({
action: actions.clearAutocomplete,
@@ -52,6 +96,10 @@ describe('Header Search Store Actions', () => {
});
describe('setSearch', () => {
+ beforeEach(() => {
+ state = createState({});
+ });
+
it('calls the SET_SEARCH mutation', () => {
return testAction({
action: actions.setSearch,
diff --git a/spec/frontend/header_search/store/getters_spec.js b/spec/frontend/header_search/store/getters_spec.js
index c76be3c0360..a1d9481b5cc 100644
--- a/spec/frontend/header_search/store/getters_spec.js
+++ b/spec/frontend/header_search/store/getters_spec.js
@@ -73,30 +73,6 @@ describe('Header Search Store Getters', () => {
});
describe.each`
- project | ref | expectedPath
- ${null} | ${null} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}`}
- ${MOCK_PROJECT} | ${null} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_id=${MOCK_PROJECT.id}`}
- ${null} | ${MOCK_PROJECT.id} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_ref=${MOCK_PROJECT.id}`}
- ${MOCK_PROJECT} | ${MOCK_PROJECT.id} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_id=${MOCK_PROJECT.id}&project_ref=${MOCK_PROJECT.id}`}
- `('autocompleteQuery', ({ project, ref, expectedPath }) => {
- describe(`when project is ${project?.name} and project ref is ${ref}`, () => {
- beforeEach(() => {
- createState({
- searchContext: {
- project,
- ref,
- },
- });
- state.search = MOCK_SEARCH;
- });
-
- it(`should return ${expectedPath}`, () => {
- expect(getters.autocompleteQuery(state)).toBe(expectedPath);
- });
- });
- });
-
- describe.each`
group | group_metadata | project | project_metadata | expectedPath
${null} | ${null} | ${null} | ${null} | ${MOCK_ISSUE_PATH}
${{ name: 'Test Group' }} | ${{ issues_path: 'group/path' }} | ${null} | ${null} | ${'group/path'}
diff --git a/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js b/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js
index d899bc4f7d8..ee6ed694285 100644
--- a/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js
@@ -1,6 +1,6 @@
import Vue, { nextTick } from 'vue';
import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
-import radioGroup from '~/ide/components/commit_sidebar/radio_group.vue';
+import RadioGroup from '~/ide/components/commit_sidebar/radio_group.vue';
import { createStore } from '~/ide/stores';
describe('IDE commit sidebar radio group', () => {
@@ -10,7 +10,7 @@ describe('IDE commit sidebar radio group', () => {
beforeEach(async () => {
store = createStore();
- const Component = Vue.extend(radioGroup);
+ const Component = Vue.extend(RadioGroup);
store.state.commit.commitAction = '2';
@@ -38,7 +38,7 @@ describe('IDE commit sidebar radio group', () => {
vm = new Vue({
components: {
- radioGroup,
+ RadioGroup,
},
store,
render: (createElement) =>
@@ -62,7 +62,7 @@ describe('IDE commit sidebar radio group', () => {
beforeEach(async () => {
vm.$destroy();
- const Component = Vue.extend(radioGroup);
+ const Component = Vue.extend(RadioGroup);
store.state.commit.commitAction = '1';
store.state.commit.newBranchName = 'test-123';
diff --git a/spec/frontend/surveys/merge_request_performance/app_spec.js b/spec/frontend/surveys/merge_request_performance/app_spec.js
index cd549155914..1d84685bf88 100644
--- a/spec/frontend/surveys/merge_request_performance/app_spec.js
+++ b/spec/frontend/surveys/merge_request_performance/app_spec.js
@@ -33,12 +33,12 @@ describe('MergeRequestExperienceSurveyApp', () => {
GlSprintf,
},
});
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
};
describe('when user callout is visible', () => {
beforeEach(() => {
createWrapper();
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
});
it('shows survey', async () => {
@@ -47,14 +47,27 @@ describe('MergeRequestExperienceSurveyApp', () => {
expect(wrapper.emitted().close).toBe(undefined);
});
- it('triggers user callout on close', async () => {
- findCloseButton().vm.$emit('click');
- expect(dismiss).toHaveBeenCalledTimes(1);
- });
+ describe('when close button clicked', () => {
+ beforeEach(() => {
+ findCloseButton().vm.$emit('click');
+ });
- it('emits close event on close button click', async () => {
- findCloseButton().vm.$emit('click');
- expect(wrapper.emitted()).toMatchObject({ close: [[]] });
+ it('triggers user callout on close', async () => {
+ expect(dismiss).toHaveBeenCalledTimes(1);
+ });
+
+ it('emits close event on close button click', async () => {
+ expect(wrapper.emitted()).toMatchObject({ close: [[]] });
+ });
+
+ it('tracks dismissal', async () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'survey:mr_experience', {
+ label: 'dismiss',
+ extra: {
+ accountAge: 0,
+ },
+ });
+ });
});
it('applies correct feature name for user callout', () => {
@@ -148,5 +161,14 @@ describe('MergeRequestExperienceSurveyApp', () => {
expect(wrapper.emitted()).toMatchObject({ close: [[]] });
expect(dismiss).toHaveBeenCalledTimes(1);
});
+
+ it('tracks dismissal', async () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'survey:mr_experience', {
+ label: 'dismiss',
+ extra: {
+ accountAge: 0,
+ },
+ });
+ });
});
});
diff --git a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
index 819841317f9..330f5f6e036 100644
--- a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
+++ b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
@@ -1133,7 +1133,7 @@ describe('MrWidgetOptions', () => {
widgetName | nonStandardEvent
${'WidgetCodeQuality'} | ${'i_testing_code_quality_widget_total'}
${'WidgetTerraform'} | ${'i_testing_terraform_widget_total'}
- ${'WidgetIssues'} | ${'i_testing_load_performance_widget_total'}
+ ${'WidgetIssues'} | ${'i_testing_issues_widget_total'}
${'WidgetTestReport'} | ${'i_testing_summary_widget_total'}
`(
"sends non-standard events for the '$widgetName' widget",
diff --git a/spec/graphql/types/merge_request_review_state_enum_spec.rb b/spec/graphql/types/merge_request_review_state_enum_spec.rb
index 407a1ae3c1f..486e1c4f502 100644
--- a/spec/graphql/types/merge_request_review_state_enum_spec.rb
+++ b/spec/graphql/types/merge_request_review_state_enum_spec.rb
@@ -12,10 +12,6 @@ RSpec.describe GitlabSchema.types['MergeRequestReviewState'] do
'UNREVIEWED' => have_attributes(
description: 'The merge request is unreviewed.',
value: 'unreviewed'
- ),
- 'ATTENTION_REQUESTED' => have_attributes(
- description: 'The merge request is attention_requested.',
- value: 'attention_requested'
)
)
end
diff --git a/spec/graphql/types/user_merge_request_interaction_type_spec.rb b/spec/graphql/types/user_merge_request_interaction_type_spec.rb
index 4782a1faf8d..3cd9750debb 100644
--- a/spec/graphql/types/user_merge_request_interaction_type_spec.rb
+++ b/spec/graphql/types/user_merge_request_interaction_type_spec.rb
@@ -76,11 +76,8 @@ RSpec.describe GitlabSchema.types['UserMergeRequestInteraction'] do
context 'when the user has been asked to review the MR' do
before do
merge_request.reviewers << user
- merge_request.find_reviewer(user).update!(state: :attention_requested)
end
- it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUESTED'].value) }
-
it 'implies not reviewed' do
expect(resolve(:reviewed)).to be false
end
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb
index f192862c1c4..f9ebab149a5 100644
--- a/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb
@@ -3,44 +3,54 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists do
- shared_examples 'an exists rule with a context' do
- subject { described_class.new(globs).satisfied_by?(pipeline, context) }
+ describe '#satisfied_by?' do
+ shared_examples 'an exists rule with a context' do
+ it_behaves_like 'a glob matching rule' do
+ let(:project) { create(:project, :custom_repo, files: files) }
+ end
- it_behaves_like 'a glob matching rule' do
- let(:project) { create(:project, :custom_repo, files: files) }
- end
+ context 'after pattern comparision limit is reached' do
+ let(:globs) { ['*definitely_not_a_matching_glob*'] }
+ let(:project) { create(:project, :repository) }
- context 'after pattern comparision limit is reached' do
- let(:globs) { ['*definitely_not_a_matching_glob*'] }
- let(:project) { create(:project, :repository) }
+ before do
+ stub_const('Gitlab::Ci::Build::Rules::Rule::Clause::Exists::MAX_PATTERN_COMPARISONS', 2)
+ expect(File).to receive(:fnmatch?).twice.and_call_original
+ end
- before do
- stub_const('Gitlab::Ci::Build::Rules::Rule::Clause::Exists::MAX_PATTERN_COMPARISONS', 2)
- expect(File).to receive(:fnmatch?).twice.and_call_original
+ it { is_expected.to be_truthy }
end
-
- it { is_expected.to be_truthy }
end
- end
- describe '#satisfied_by?' do
- let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.head_commit.sha) }
+ subject(:satisfied_by?) { described_class.new(globs).satisfied_by?(nil, context) }
context 'when context is Build::Context::Build' do
it_behaves_like 'an exists rule with a context' do
- let(:context) { Gitlab::Ci::Build::Context::Build.new(pipeline, sha: 'abc1234') }
+ let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.commit.sha) }
+ let(:context) { Gitlab::Ci::Build::Context::Build.new(pipeline, sha: project.repository.commit.sha) }
end
end
context 'when context is Build::Context::Global' do
it_behaves_like 'an exists rule with a context' do
+ let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.commit.sha) }
let(:context) { Gitlab::Ci::Build::Context::Global.new(pipeline, yaml_variables: {}) }
end
end
context 'when context is Config::External::Context' do
+ let(:context) { Gitlab::Ci::Config::External::Context.new(project: project, sha: sha) }
+
it_behaves_like 'an exists rule with a context' do
- let(:context) { Gitlab::Ci::Config::External::Context.new(project: project, sha: project.repository.tree.sha) }
+ let(:sha) { project.repository.commit.sha }
+ end
+
+ context 'when context has no project' do
+ let(:globs) { ['Dockerfile'] }
+ let(:project) {}
+ let(:sha) { 'abc1234' }
+
+ it { is_expected.to eq(false) }
end
end
end
diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
index 45dfea636f3..c22afb32756 100644
--- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -219,4 +219,43 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do
)
}
end
+
+ describe '#to_hash' do
+ subject(:to_hash) { remote_file.to_hash }
+
+ before do
+ stub_full_request(location).to_return(body: remote_file_content)
+ end
+
+ context 'with a valid remote file' do
+ it 'returns the content as a hash' do
+ expect(to_hash).to eql(
+ before_script: ["apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs",
+ "ruby -v",
+ "which ruby",
+ "bundle install --jobs $(nproc) \"${FLAGS[@]}\""]
+ )
+ end
+ end
+
+ context 'when it has `include` with rules:exists' do
+ let(:remote_file_content) do
+ <<~HEREDOC
+ include:
+ - local: another-file.yml
+ rules:
+ - exists: [Dockerfile]
+ HEREDOC
+ end
+
+ it 'returns the content as a hash' do
+ expect(to_hash).to eql(
+ include: [
+ { local: 'another-file.yml',
+ rules: [{ exists: ['Dockerfile'] }] }
+ ]
+ )
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb
index 841a46e197d..b1dff6f9723 100644
--- a/spec/lib/gitlab/ci/config/external/processor_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb
@@ -94,6 +94,36 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
end
end
+ context 'when the remote file has `include` with rules:exists' do
+ let(:remote_file) { 'https://gitlab.com/gitlab-org/gitlab-foss/blob/1234/.gitlab-ci-1.yml' }
+ let(:values) { { include: remote_file, image: 'image:1.0' } }
+ let(:external_file_content) do
+ <<-HEREDOC
+ include:
+ - local: another-file.yml
+ rules:
+ - exists: [Dockerfile]
+
+ rspec:
+ script:
+ - bundle exec rspec
+ HEREDOC
+ end
+
+ before do
+ stub_full_request(remote_file).to_return(body: external_file_content)
+ end
+
+ it 'evaluates the rule as false' do
+ output = processor.perform
+ expect(output.keys).to match_array([:image, :rspec])
+ end
+
+ it "removes the 'include' keyword" do
+ expect(processor.perform[:include]).to be_nil
+ end
+ end
+
context 'with a valid local external file is defined' do
let(:values) { { include: '/lib/gitlab/ci/templates/template.yml', image: 'image:1.0' } }
let(:local_file_content) do
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 9aec3271913..c0be5b01cef 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -654,11 +654,9 @@ search_data:
merge_request_assignees:
- merge_request
- assignee
-- updated_state_by
merge_request_reviewers:
- merge_request
- reviewer
-- updated_state_by
lfs_file_locks:
- user
project_badges:
diff --git a/spec/lib/gitlab/utils/delegator_override_spec.rb b/spec/lib/gitlab/utils/delegator_override_spec.rb
index af4c7fa5d8e..2dafa75e344 100644
--- a/spec/lib/gitlab/utils/delegator_override_spec.rb
+++ b/spec/lib/gitlab/utils/delegator_override_spec.rb
@@ -31,6 +31,7 @@ RSpec.describe Gitlab::Utils::DelegatorOverride do
before do
stub_env('STATIC_VERIFICATION', 'true')
+ described_class.validators.clear
end
describe '.delegator_target' do
diff --git a/spec/lib/gitlab/utils/nokogiri_spec.rb b/spec/lib/gitlab/utils/nokogiri_spec.rb
index 90f137f53c8..7b4c63f9168 100644
--- a/spec/lib/gitlab/utils/nokogiri_spec.rb
+++ b/spec/lib/gitlab/utils/nokogiri_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
+require 'rspec-parameterized'
RSpec.describe Gitlab::Utils::Nokogiri do
describe '#css_to_xpath' do
diff --git a/spec/lib/gitlab/utils/sanitize_node_link_spec.rb b/spec/lib/gitlab/utils/sanitize_node_link_spec.rb
index 3ab592dfc62..1fc10bc3aa8 100644
--- a/spec/lib/gitlab/utils/sanitize_node_link_spec.rb
+++ b/spec/lib/gitlab/utils/sanitize_node_link_spec.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
+require 'html/pipeline'
+require 'addressable'
RSpec.describe Gitlab::Utils::SanitizeNodeLink do
let(:klass) do
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index a36b656a864..b89d21924cb 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -1857,56 +1857,31 @@ RSpec.describe Group do
end
end
- describe '#update_two_factor_requirement' do
- let(:user) { create(:user) }
+ describe '#update_two_factor_requirement_for_members' do
+ let_it_be_with_reload(:user) { create(:user) }
context 'group membership' do
- before do
+ it 'enables two_factor_requirement for group members' do
group.add_member(user, GroupMember::OWNER)
- end
-
- it 'is called when require_two_factor_authentication is changed' do
- expect_any_instance_of(User).to receive(:update_two_factor_requirement)
-
group.update!(require_two_factor_authentication: true)
- end
-
- it 'is called when two_factor_grace_period is changed' do
- expect_any_instance_of(User).to receive(:update_two_factor_requirement)
-
- group.update!(two_factor_grace_period: 23)
- end
- it 'is not called when other attributes are changed' do
- expect_any_instance_of(User).not_to receive(:update_two_factor_requirement)
+ group.update_two_factor_requirement_for_members
- group.update!(description: 'foobar')
+ expect(user.reload.require_two_factor_authentication_from_group).to be_truthy
end
- it 'calls #update_two_factor_requirement on each group member' do
- other_user = create(:user)
- group.add_member(other_user, GroupMember::OWNER)
-
- calls = 0
- allow_any_instance_of(User).to receive(:update_two_factor_requirement) do
- calls += 1
- end
+ it 'disables two_factor_requirement for group members' do
+ user.update!(require_two_factor_authentication_from_group: true)
+ group.add_member(user, GroupMember::OWNER)
+ group.update!(require_two_factor_authentication: false)
- group.update!(require_two_factor_authentication: true, two_factor_grace_period: 23)
+ group.update_two_factor_requirement_for_members
- expect(calls).to eq 2
+ expect(user.reload.require_two_factor_authentication_from_group).to be_falsey
end
end
context 'sub groups and projects' do
- it 'enables two_factor_requirement for group member' do
- group.add_member(user, GroupMember::OWNER)
-
- group.update!(require_two_factor_authentication: true)
-
- expect(user.reload.require_two_factor_authentication_from_group).to be_truthy
- end
-
context 'expanded group members' do
let(:indirect_user) { create(:user) }
@@ -1915,9 +1890,10 @@ RSpec.describe Group do
it 'enables two_factor_requirement for subgroup member' do
subgroup = create(:group, :nested, parent: group)
subgroup.add_member(indirect_user, GroupMember::OWNER)
-
group.update!(require_two_factor_authentication: true)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_truthy
end
end
@@ -1926,9 +1902,10 @@ RSpec.describe Group do
it 'enables two_factor_requirement for subgroup member' do
subgroup = create(:group, :nested, parent: group, require_two_factor_authentication: true)
subgroup.add_member(indirect_user, GroupMember::OWNER)
-
group.update!(require_two_factor_authentication: false)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_truthy
end
@@ -1936,9 +1913,10 @@ RSpec.describe Group do
ancestor_group = create(:group)
ancestor_group.add_member(indirect_user, GroupMember::OWNER)
group.update!(parent: ancestor_group)
-
group.update!(require_two_factor_authentication: true)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_truthy
end
end
@@ -1949,9 +1927,10 @@ RSpec.describe Group do
it 'enables two_factor_requirement for subgroup member' do
subgroup = create(:group, :nested, parent: group)
subgroup.add_member(indirect_user, GroupMember::OWNER)
-
group.update!(require_two_factor_authentication: true)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_truthy
end
end
@@ -1960,9 +1939,10 @@ RSpec.describe Group do
it 'disables two_factor_requirement for subgroup member' do
subgroup = create(:group, :nested, parent: group)
subgroup.add_member(indirect_user, GroupMember::OWNER)
-
group.update!(require_two_factor_authentication: false)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_falsey
end
@@ -1970,9 +1950,10 @@ RSpec.describe Group do
ancestor_group = create(:group, require_two_factor_authentication: false)
indirect_user.update!(require_two_factor_authentication_from_group: true)
ancestor_group.add_member(indirect_user, GroupMember::OWNER)
-
group.update!(require_two_factor_authentication: false)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_falsey
end
end
@@ -1983,9 +1964,10 @@ RSpec.describe Group do
it 'does not enable two_factor_requirement for child project member' do
project = create(:project, group: group)
project.add_maintainer(user)
-
group.update!(require_two_factor_authentication: true)
+ group.update_two_factor_requirement_for_members
+
expect(user.reload.require_two_factor_authentication_from_group).to be_falsey
end
@@ -1993,15 +1975,36 @@ RSpec.describe Group do
subgroup = create(:group, :nested, parent: group)
project = create(:project, group: subgroup)
project.add_maintainer(user)
-
group.update!(require_two_factor_authentication: true)
+ group.update_two_factor_requirement_for_members
+
expect(user.reload.require_two_factor_authentication_from_group).to be_falsey
end
end
end
end
+ describe '#update_two_factor_requirement' do
+ it 'enqueues a job when require_two_factor_authentication is changed' do
+ expect(Groups::UpdateTwoFactorRequirementForMembersWorker).to receive(:perform_async).with(group.id)
+
+ group.update!(require_two_factor_authentication: true)
+ end
+
+ it 'enqueues a job when two_factor_grace_period is changed' do
+ expect(Groups::UpdateTwoFactorRequirementForMembersWorker).to receive(:perform_async).with(group.id)
+
+ group.update!(two_factor_grace_period: 23)
+ end
+
+ it 'does not enqueue a job when other attributes are changed' do
+ expect(Groups::UpdateTwoFactorRequirementForMembersWorker).not_to receive(:perform_async).with(group.id)
+
+ group.update!(description: 'foobar')
+ end
+ end
+
describe '#path_changed_hook' do
let(:system_hook_service) { SystemHooksService.new }
diff --git a/spec/models/merge_request_assignee_spec.rb b/spec/models/merge_request_assignee_spec.rb
index 387d17d7823..73bf7d02468 100644
--- a/spec/models/merge_request_assignee_spec.rb
+++ b/spec/models/merge_request_assignee_spec.rb
@@ -38,26 +38,4 @@ RSpec.describe MergeRequestAssignee do
end
end
end
-
- it_behaves_like 'having unique enum values'
-
- describe '#attention_requested_by' do
- let(:current_user) { create(:user) }
-
- before do
- subject.update!(updated_state_by: current_user, state: :attention_requested)
- end
-
- context 'attention requested' do
- it { expect(subject.attention_requested_by).to eq(current_user) }
- end
-
- context 'attention requested' do
- before do
- subject.update!(state: :reviewed)
- end
-
- it { expect(subject.attention_requested_by).to eq(nil) }
- end
- end
end
diff --git a/spec/models/merge_request_reviewer_spec.rb b/spec/models/merge_request_reviewer_spec.rb
index 4df2dba3a7d..5a29966e4b9 100644
--- a/spec/models/merge_request_reviewer_spec.rb
+++ b/spec/models/merge_request_reviewer_spec.rb
@@ -14,24 +14,4 @@ RSpec.describe MergeRequestReviewer do
it { is_expected.to belong_to(:merge_request).class_name('MergeRequest') }
it { is_expected.to belong_to(:reviewer).class_name('User').inverse_of(:merge_request_reviewers) }
end
-
- describe '#attention_requested_by' do
- let(:current_user) { create(:user) }
-
- before do
- subject.update!(updated_state_by: current_user, state: :attention_requested)
- end
-
- context 'attention requested' do
- it { expect(subject.attention_requested_by).to eq(current_user) }
- end
-
- context 'attention requested' do
- before do
- subject.update!(state: :reviewed)
- end
-
- it { expect(subject.attention_requested_by).to eq(nil) }
- end
- end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 19e9e2493a3..4729de8777d 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -144,22 +144,6 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
- describe '.attention' do
- let_it_be(:merge_request5) { create(:merge_request, :unique_branches, assignees: [user2]) }
- let_it_be(:merge_request6) { create(:merge_request, :unique_branches, assignees: [user2]) }
-
- before do
- assignee = merge_request6.find_assignee(user2)
- assignee.update!(state: :reviewed)
- merge_request2.find_reviewer(user2).update!(state: :attention_requested)
- merge_request5.find_assignee(user2).update!(state: :attention_requested)
- end
-
- it 'returns MRs that have any attention requests' do
- expect(described_class.attention(user2)).to eq([merge_request2, merge_request5])
- end
- end
-
describe '.drafts' do
it 'returns MRs where draft == true' do
expect(described_class.drafts).to eq([merge_request4])
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 801cc586b76..537e680102a 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -7396,25 +7396,6 @@ RSpec.describe User do
let(:factory_name) { :user }
end
- describe 'mr_attention_requests_enabled?' do
- let(:user) { create(:user) }
-
- before do
- stub_feature_flags(mr_attention_requests: false)
- end
-
- it { expect(user.mr_attention_requests_enabled?).to be(false) }
-
- it 'feature flag is enabled for user' do
- stub_feature_flags(mr_attention_requests: user)
-
- another_user = create(:user)
-
- expect(user.mr_attention_requests_enabled?).to be(true)
- expect(another_user.mr_attention_requests_enabled?).to be(false)
- end
- end
-
describe 'user age' do
let(:user) { create(:user, created_at: Date.yesterday) }
diff --git a/spec/models/users/merge_request_interaction_spec.rb b/spec/models/users/merge_request_interaction_spec.rb
index a499a7c68e8..0b1888bd9a6 100644
--- a/spec/models/users/merge_request_interaction_spec.rb
+++ b/spec/models/users/merge_request_interaction_spec.rb
@@ -59,11 +59,8 @@ RSpec.describe ::Users::MergeRequestInteraction do
context 'when the user has been asked to review the MR' do
before do
merge_request.reviewers << user
- merge_request.find_reviewer(user).update!(state: :attention_requested)
end
- it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUESTED'].value) }
-
it 'implies not reviewed' do
expect(interaction).not_to be_reviewed
end
diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb
index d2f34080be3..6a59df81405 100644
--- a/spec/requests/api/graphql/project/merge_request_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request_spec.rb
@@ -365,7 +365,7 @@ RSpec.describe 'getting merge request information nested in a project' do
expect(interaction_data).to contain_exactly a_hash_including(
'canMerge' => false,
'canUpdate' => can_update,
- 'reviewState' => attention_requested,
+ 'reviewState' => unreviewed,
'reviewed' => false,
'approved' => false
)
@@ -398,8 +398,8 @@ RSpec.describe 'getting merge request information nested in a project' do
describe 'scalability' do
let_it_be(:other_users) { create_list(:user, 3) }
- let(:attention_requested) do
- { 'reviewState' => 'ATTENTION_REQUESTED' }
+ let(:unreviewed) do
+ { 'reviewState' => 'UNREVIEWED' }
end
let(:reviewed) do
@@ -425,15 +425,15 @@ RSpec.describe 'getting merge request information nested in a project' do
other_users.each do |user|
assign_user(user)
- merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user, state: :attention_requested)
+ merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user)
end
expect { post_graphql(query) }.not_to exceed_query_limit(baseline)
expect(interaction_data).to contain_exactly(
- include(attention_requested),
- include(attention_requested),
- include(attention_requested),
+ include(unreviewed),
+ include(unreviewed),
+ include(unreviewed),
include(reviewed)
)
end
@@ -462,17 +462,17 @@ RSpec.describe 'getting merge request information nested in a project' do
it_behaves_like 'when requesting information about MR interactions' do
let(:field) { :reviewers }
- let(:attention_requested) { 'ATTENTION_REQUESTED' }
+ let(:unreviewed) { 'UNREVIEWED' }
let(:can_update) { false }
def assign_user(user)
- merge_request.merge_request_reviewers.create!(reviewer: user, state: :attention_requested)
+ merge_request.merge_request_reviewers.create!(reviewer: user)
end
end
it_behaves_like 'when requesting information about MR interactions' do
let(:field) { :assignees }
- let(:attention_requested) { nil }
+ let(:unreviewed) { nil }
let(:can_update) { true } # assignees can update MRs
def assign_user(user)
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 97b5133d4e6..f70e029bfd4 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -1566,7 +1566,6 @@ RSpec.describe API::MergeRequests do
expect(json_response.last['user']['name']).to eq(reviewer.name)
expect(json_response.last['user']['username']).to eq(reviewer.username)
expect(json_response.last['state']).to eq('unreviewed')
- expect(json_response.last['updated_state_by']).to be_nil
expect(json_response.last['created_at']).to be_present
end
diff --git a/spec/services/merge_requests/update_assignees_service_spec.rb b/spec/services/merge_requests/update_assignees_service_spec.rb
index f5f6f0ca301..3a0b17c2768 100644
--- a/spec/services/merge_requests/update_assignees_service_spec.rb
+++ b/spec/services/merge_requests/update_assignees_service_spec.rb
@@ -113,49 +113,6 @@ RSpec.describe MergeRequests::UpdateAssigneesService do
expect { service.execute(merge_request) }
.to issue_fewer_queries_than { update_service.execute(other_mr) }
end
-
- context 'setting state of assignees' do
- before do
- stub_feature_flags(mr_attention_requests: false)
- end
-
- it 'does not set state as attention_requested if feature flag is disabled' do
- update_merge_request
-
- expect(merge_request.merge_request_assignees[0].state).not_to eq('attention_requested')
- end
-
- context 'feature flag is enabled for current_user' do
- before do
- stub_feature_flags(mr_attention_requests: user)
- end
-
- it 'sets state as attention_requested' do
- update_merge_request
-
- expect(merge_request.merge_request_assignees[0].state).to eq('attention_requested')
- expect(merge_request.merge_request_assignees[0].updated_state_by).to eq(user)
- end
-
- it 'uses reviewers state if it is same user as new assignee' do
- merge_request.reviewers << user2
-
- update_merge_request
-
- expect(merge_request.merge_request_assignees[0].state).to eq('unreviewed')
- end
-
- context 'when assignee_ids matches existing assignee' do
- let(:opts) { { assignee_ids: [user3.id] } }
-
- it 'keeps original assignees state' do
- update_merge_request
-
- expect(merge_request.find_assignee(user3).state).to eq('unreviewed')
- end
- end
- end
- end
end
end
end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index b7fb48718d8..606fd066f1c 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -215,14 +215,6 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
MergeRequests::UpdateService.new(project: project, current_user: user, params: opts).execute(merge_request)
end
-
- it 'updates attention requested by of reviewer' do
- opts[:reviewers] = [user2]
-
- MergeRequests::UpdateService.new(project: project, current_user: user, params: opts).execute(merge_request)
-
- expect(merge_request.find_reviewer(user2).updated_state_by).to eq(user)
- end
end
context 'when reviewers did not change' do
@@ -328,49 +320,6 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
update_merge_request(reviewer_ids: [user.id])
end
-
- context 'setting state of reviewers' do
- before do
- stub_feature_flags(mr_attention_requests: false)
- end
-
- it 'does not set state as attention_requested if feature flag is disabled' do
- update_merge_request(reviewer_ids: [user.id])
-
- expect(merge_request.merge_request_reviewers[0].state).not_to eq('attention_requested')
- end
-
- context 'feature flag is enabled for current_user' do
- before do
- stub_feature_flags(mr_attention_requests: user)
- end
-
- it 'sets state as attention_requested' do
- update_merge_request(reviewer_ids: [user2.id])
-
- expect(merge_request.merge_request_reviewers[0].state).to eq('attention_requested')
- expect(merge_request.merge_request_reviewers[0].updated_state_by).to eq(user)
- end
-
- it 'keeps original reviewers state' do
- merge_request.find_reviewer(user2).update!(state: :unreviewed)
-
- update_merge_request({
- reviewer_ids: [user2.id]
- })
-
- expect(merge_request.find_reviewer(user2).state).to eq('unreviewed')
- end
-
- it 'uses reviewers state if it is same user as new assignee' do
- merge_request.assignees << user
-
- update_merge_request(reviewer_ids: [user.id])
-
- expect(merge_request.merge_request_reviewers[0].state).to eq('unreviewed')
- end
- end
- end
end
it 'creates a resource label event' do
@@ -1133,53 +1082,6 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
end
end
end
-
- context 'setting state of assignees' do
- before do
- stub_feature_flags(mr_attention_requests: false)
- end
-
- it 'does not set state as attention_requested if feature flag is disabled' do
- update_merge_request({
- assignee_ids: [user2.id]
- })
-
- expect(merge_request.merge_request_assignees[0].state).not_to eq('attention_requested')
- end
-
- context 'feature flag is enabled for current_user' do
- before do
- stub_feature_flags(mr_attention_requests: user)
- end
-
- it 'sets state as attention_requested' do
- update_merge_request({
- assignee_ids: [user2.id]
- })
-
- expect(merge_request.merge_request_assignees[0].state).to eq('attention_requested')
- expect(merge_request.merge_request_assignees[0].updated_state_by).to eq(user)
- end
-
- it 'keeps original assignees state' do
- update_merge_request({
- assignee_ids: [user3.id]
- })
-
- expect(merge_request.find_assignee(user3).state).to eq('unreviewed')
- end
-
- it 'uses reviewers state if it is same user as new assignee' do
- merge_request.reviewers << user2
-
- update_merge_request({
- assignee_ids: [user2.id]
- })
-
- expect(merge_request.merge_request_assignees[0].state).to eq('unreviewed')
- end
- end
- end
end
context 'when adding time spent' do
diff --git a/spec/support/helpers/javascript_form_helper.rb b/spec/support/helpers/javascript_form_helper.rb
new file mode 100644
index 00000000000..41c5ba4373b
--- /dev/null
+++ b/spec/support/helpers/javascript_form_helper.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module JavascriptFormHelper
+ def prevent_submit_for(query_selector)
+ execute_script("document.querySelector('#{query_selector}').addEventListener('submit', e => e.preventDefault())")
+ end
+end
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 6c9e0944c54..bcd83c431e4 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -7709,28 +7709,6 @@
- './spec/lib/gitlab/usage/service_ping_report_spec.rb'
- './spec/lib/gitlab/user_access_snippet_spec.rb'
- './spec/lib/gitlab/user_access_spec.rb'
-- './spec/lib/gitlab/utils/batch_loader_spec.rb'
-- './spec/lib/gitlab/utils/deep_size_spec.rb'
-- './spec/lib/gitlab/utils/delegator_override/error_spec.rb'
-- './spec/lib/gitlab/utils/delegator_override_spec.rb'
-- './spec/lib/gitlab/utils/delegator_override/validator_spec.rb'
-- './spec/lib/gitlab/utils/gzip_spec.rb'
-- './spec/lib/gitlab/utils/inline_hash_spec.rb'
-- './spec/lib/gitlab/utils/json_size_estimator_spec.rb'
-- './spec/lib/gitlab/utils/lazy_attributes_spec.rb'
-- './spec/lib/gitlab/utils/link_header_parser_spec.rb'
-- './spec/lib/gitlab/utils/log_limited_array_spec.rb'
-- './spec/lib/gitlab/utils/markdown_spec.rb'
-- './spec/lib/gitlab/utils/measuring_spec.rb'
-- './spec/lib/gitlab/utils/merge_hash_spec.rb'
-- './spec/lib/gitlab/utils/mime_type_spec.rb'
-- './spec/lib/gitlab/utils/nokogiri_spec.rb'
-- './spec/lib/gitlab/utils/override_spec.rb'
-- './spec/lib/gitlab/utils/safe_inline_hash_spec.rb'
-- './spec/lib/gitlab/utils/sanitize_node_link_spec.rb'
-- './spec/lib/gitlab/utils_spec.rb'
-- './spec/lib/gitlab/utils/strong_memoize_spec.rb'
-- './spec/lib/gitlab/utils/usage_data_spec.rb'
- './spec/lib/gitlab/uuid_spec.rb'
- './spec/lib/gitlab/verify/job_artifacts_spec.rb'
- './spec/lib/gitlab/verify/lfs_objects_spec.rb'
diff --git a/spec/workers/disallow_two_factor_for_group_worker_spec.rb b/spec/workers/disallow_two_factor_for_group_worker_spec.rb
index f30b12dd7f4..3a875727cce 100644
--- a/spec/workers/disallow_two_factor_for_group_worker_spec.rb
+++ b/spec/workers/disallow_two_factor_for_group_worker_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe DisallowTwoFactorForGroupWorker do
expect(group.reload.require_two_factor_authentication).to eq(false)
end
- it "updates group members" do
+ it "updates group members", :sidekiq_inline do
group.add_member(user, GroupMember::DEVELOPER)
described_class.new.perform(group.id)
diff --git a/spec/workers/groups/update_two_factor_requirement_for_members_worker_spec.rb b/spec/workers/groups/update_two_factor_requirement_for_members_worker_spec.rb
new file mode 100644
index 00000000000..9d202b9452f
--- /dev/null
+++ b/spec/workers/groups/update_two_factor_requirement_for_members_worker_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::UpdateTwoFactorRequirementForMembersWorker do
+ let_it_be(:group) { create(:group) }
+
+ let(:worker) { described_class.new }
+
+ describe '#perform' do
+ it 'calls #update_two_factor_requirement_for_members' do
+ allow(Group).to receive(:find_by_id).with(group.id).and_return(group)
+ expect(group).to receive(:update_two_factor_requirement_for_members)
+
+ worker.perform(group.id)
+ end
+
+ context 'when group not found' do
+ it 'returns nil' do
+ expect(worker.perform(non_existing_record_id)).to be_nil
+ end
+ end
+
+ include_examples 'an idempotent worker' do
+ let(:subject) { described_class.new.perform(group.id) }
+
+ it 'requires 2fa for group members correctly' do
+ group.update!(require_two_factor_authentication: true)
+ user = create(:user, require_two_factor_authentication_from_group: false)
+ group.add_member(user, GroupMember::OWNER)
+
+ # Using subject inside this block will process the job multiple times
+ subject
+
+ expect(user.reload.require_two_factor_authentication_from_group).to be true
+ end
+ end
+ end
+end
diff --git a/workhorse/internal/headers/content_headers.go b/workhorse/internal/headers/content_headers.go
index 8cca3d97e82..3e2b2bf82e6 100644
--- a/workhorse/internal/headers/content_headers.go
+++ b/workhorse/internal/headers/content_headers.go
@@ -43,6 +43,10 @@ const (
func SafeContentHeaders(data []byte, contentDisposition string) (string, string) {
contentType := safeContentType(data)
contentDisposition = safeContentDisposition(contentType, contentDisposition)
+
+ if attachmentRegex.MatchString(contentDisposition) {
+ contentType = "application/octet-stream"
+ }
return contentType, contentDisposition
}
diff --git a/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go b/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
index ce7f7921589..5dd64bb54aa 100644
--- a/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
+++ b/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
@@ -74,19 +74,19 @@ func TestSetProperContentTypeAndDisposition(t *testing.T) {
},
{
desc: "SVG type",
- contentType: "image/svg+xml",
+ contentType: "application/octet-stream",
contentDisposition: "attachment",
body: testhelper.LoadFile(t, "testdata/image.svg"),
},
{
desc: "Partial SVG type",
- contentType: "image/svg+xml",
+ contentType: "application/octet-stream",
contentDisposition: "attachment",
body: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 330 82\"><title>SVG logo combined with the W3C logo, set horizontally</title><desc>The logo combines three entities displayed horizontall</desc><metadata>",
},
{
desc: "Application type",
- contentType: "application/pdf",
+ contentType: "application/octet-stream",
contentDisposition: "attachment",
body: testhelper.LoadFile(t, "testdata/file.pdf"),
},
@@ -110,7 +110,7 @@ func TestSetProperContentTypeAndDisposition(t *testing.T) {
},
{
desc: "Audio type",
- contentType: "audio/mpeg",
+ contentType: "application/octet-stream",
contentDisposition: "attachment",
body: testhelper.LoadFile(t, "testdata/audio.mp3"),
},
@@ -152,16 +152,22 @@ func TestSetProperContentTypeAndDisposition(t *testing.T) {
},
{
desc: "Sketch file",
- contentType: "application/zip",
+ contentType: "application/octet-stream",
contentDisposition: "attachment",
body: testhelper.LoadFile(t, "testdata/file.sketch"),
},
{
desc: "PDF file with non-ASCII characters in filename",
- contentType: "application/pdf",
+ contentType: "application/octet-stream",
contentDisposition: `attachment; filename="file-ä.pdf"; filename*=UTF-8''file-%c3.pdf`,
body: testhelper.LoadFile(t, "testdata/file-ä.pdf"),
},
+ {
+ desc: "Microsoft Word file",
+ contentType: "application/octet-stream",
+ contentDisposition: `attachment`,
+ body: testhelper.LoadFile(t, "testdata/file.docx"),
+ },
}
for _, tc := range testCases {
diff --git a/workhorse/internal/upstream/routes.go b/workhorse/internal/upstream/routes.go
index c889f87ed96..311028750a5 100644
--- a/workhorse/internal/upstream/routes.go
+++ b/workhorse/internal/upstream/routes.go
@@ -287,6 +287,9 @@ func configureRoutes(u *upstream) {
// Debian Artifact Repository
u.route("PUT", apiProjectPattern+`packages/debian/`, requestBodyUploader),
+ // RPM Artifact Repository
+ u.route("POST", apiProjectPattern+`packages/rpm/`, requestBodyUploader),
+
// Gem Artifact Repository
u.route("POST", apiProjectPattern+`packages/rubygems/`, requestBodyUploader),
diff --git a/workhorse/testdata/file.docx b/workhorse/testdata/file.docx
new file mode 100644
index 00000000000..7976343a631
--- /dev/null
+++ b/workhorse/testdata/file.docx
Binary files differ
diff --git a/workhorse/upload_test.go b/workhorse/upload_test.go
index 9947059770f..2d7806b1790 100644
--- a/workhorse/upload_test.go
+++ b/workhorse/upload_test.go
@@ -536,11 +536,13 @@ func TestPackageFilesUpload(t *testing.T) {
{"PUT", "/api/v4/projects/2412/packages/generic/mypackage/0.0.1/myfile.tar.gz"},
{"PUT", "/api/v4/projects/2412/packages/debian/libsample0_1.2.3~alpha2-1_amd64.deb"},
{"POST", "/api/v4/projects/2412/packages/rubygems/api/v1/gems/sample.gem"},
+ {"POST", "/api/v4/projects/2412/packages/rpm/sample-4.23.fc21.x86_64.rpm"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/conan/v1/files"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/maven/v1/files"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/generic/mypackage/0.0.1/myfile.tar.gz"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/debian/libsample0_1.2.3~alpha2-1_amd64.deb"},
{"POST", "/api/v4/projects/group%2Fproject/packages/rubygems/api/v1/gems/sample.gem"},
+ {"POST", "/api/v4/projects/group%2Fproject/packages/rpm/sample-4.23.fc21.x86_64.rpm"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/terraform/modules/mymodule/mysystem/0.0.1/file"},
}
diff --git a/yarn.lock b/yarn.lock
index 0af3b375914..57d638f320d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4840,10 +4840,10 @@ dompurify@2.3.8:
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f"
integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==
-dompurify@^2.3.10:
- version "2.3.10"
- resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.10.tgz#901f7390ffe16a91a5a556b94043314cd4850385"
- integrity sha512-o7Fg/AgC7p/XpKjf/+RC3Ok6k4St5F7Q6q6+Nnm3p2zGWioAY6dh0CbbuwOhH2UcSzKsdniE/YnE2/92JcsA+g==
+dompurify@^2.3.10, dompurify@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.0.tgz#c9c88390f024c2823332615c9e20a453cf3825dd"
+ integrity sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==
domutils@^2.5.2, domutils@^2.6.0:
version "2.6.0"