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--.gitlab/ci/rules.gitlab-ci.yml2
-rw-r--r--Gemfile1
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/images/auth_buttons/atlassian_64.pngbin0 -> 1512 bytes
-rw-r--r--app/assets/javascripts/admin/dev_ops_report/components/usage_ping_disabled.vue (renamed from app/assets/javascripts/admin/dev_ops_score/components/usage_ping_disabled.vue)0
-rw-r--r--app/assets/javascripts/issue_show/components/incidents/highlight_bar/graphql/queries/get_highlight_bar_info.graphql12
-rw-r--r--app/assets/javascripts/issue_show/components/incidents/highlight_bar/higlight_bar.vue51
-rw-r--r--app/assets/javascripts/issue_show/components/incidents/incident_tabs.vue (renamed from app/assets/javascripts/issue_show/components/incident_tabs.vue)11
-rw-r--r--app/assets/javascripts/issue_show/incident.js17
-rw-r--r--app/assets/javascripts/pages/admin/dev_ops_report/index.js (renamed from app/assets/javascripts/pages/admin/dev_ops_score/index.js)2
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue94
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue122
-rw-r--r--app/assets/javascripts/sidebar/components/severity/constants.js43
-rw-r--r--app/assets/javascripts/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql9
-rw-r--r--app/assets/javascripts/sidebar/components/severity/severity.vue42
-rw-r--r--app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue195
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js32
-rw-r--r--app/assets/stylesheets/_page_specific_files.scss2
-rw-r--r--app/assets/stylesheets/pages/alert_management/severity-icons.scss1
-rw-r--r--app/assets/stylesheets/pages/dev_ops_report.scss (renamed from app/assets/stylesheets/pages/dev_ops_score.scss)0
-rw-r--r--app/controllers/admin/dev_ops_report_controller.rb (renamed from app/controllers/admin/dev_ops_score_controller.rb)4
-rw-r--r--app/controllers/admin/groups_controller.rb6
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb4
-rw-r--r--app/controllers/profiles/accounts_controller.rb10
-rw-r--r--app/controllers/projects/static_site_editor_controller.rb8
-rw-r--r--app/finders/group_members_finder.rb4
-rw-r--r--app/finders/members_finder.rb4
-rw-r--r--app/helpers/auth_helper.rb4
-rw-r--r--app/helpers/dev_ops_report_helper.rb (renamed from app/helpers/dev_ops_score_helper.rb)2
-rw-r--r--app/helpers/issuables_helper.rb4
-rw-r--r--app/helpers/nav_helper.rb2
-rw-r--r--app/helpers/system_note_helper.rb2
-rw-r--r--app/models/concerns/issuable.rb4
-rw-r--r--app/models/concerns/loaded_in_group_list.rb3
-rw-r--r--app/models/dev_ops_report/card.rb (renamed from app/models/dev_ops_score/card.rb)2
-rw-r--r--app/models/dev_ops_report/idea_to_production_step.rb (renamed from app/models/dev_ops_score/idea_to_production_step.rb)2
-rw-r--r--app/models/dev_ops_report/metric.rb (renamed from app/models/dev_ops_score/metric.rb)2
-rw-r--r--app/models/group.rb9
-rw-r--r--app/models/member.rb5
-rw-r--r--app/models/members/group_member.rb9
-rw-r--r--app/models/user.rb2
-rw-r--r--app/presenters/dev_ops_score/metric_presenter.rb2
-rw-r--r--app/serializers/issuable_sidebar_basic_entity.rb1
-rw-r--r--app/serializers/issue_sidebar_basic_entity.rb1
-rw-r--r--app/services/submit_usage_ping_service.rb4
-rw-r--r--app/views/admin/dashboard/index.html.haml42
-rw-r--r--app/views/admin/dev_ops_report/_callout.html.haml (renamed from app/views/admin/dev_ops_score/_callout.html.haml)4
-rw-r--r--app/views/admin/dev_ops_report/_card.html.haml (renamed from app/views/admin/dev_ops_score/_card.html.haml)0
-rw-r--r--app/views/admin/dev_ops_report/_no_data.html.haml (renamed from app/views/admin/dev_ops_score/_no_data.html.haml)2
-rw-r--r--app/views/admin/dev_ops_report/show.html.haml (renamed from app/views/admin/dev_ops_score/show.html.haml)2
-rw-r--r--app/views/admin/groups/_group.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml8
-rw-r--r--app/views/projects/static_site_editor/show.html.haml2
-rw-r--r--app/views/shared/icons/_dev_ops_report_no_data.svg (renamed from app/views/shared/icons/_dev_ops_score_no_data.svg)0
-rw-r--r--app/views/shared/icons/_dev_ops_report_no_index.svg (renamed from app/views/shared/icons/_dev_ops_score_no_index.svg)0
-rw-r--r--app/views/shared/icons/_dev_ops_report_overview.svg (renamed from app/views/shared/icons/_dev_ops_score_overview.svg)0
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml3
-rw-r--r--changelogs/unreleased/212274-remove-virtual-list-from-test-report.yml5
-rw-r--r--changelogs/unreleased/229402-incidents-severity-widget.yml5
-rw-r--r--changelogs/unreleased/Incident-highlight-bar.yml5
-rw-r--r--changelogs/unreleased/dblessing-atlassian-integration-sign-in.yml5
-rw-r--r--changelogs/unreleased/i18n-admin-dashboard.yml5
-rw-r--r--config/gitlab.yml.example5
-rw-r--r--config/routes.rb4
-rw-r--r--config/routes/admin.rb4
-rw-r--r--db/fixtures/development/21_dev_ops_report_metrics.rb (renamed from db/fixtures/development/21_dev_ops_score_metrics.rb)6
-rw-r--r--doc/administration/auth/README.md1
-rw-r--r--doc/administration/auth/atlassian.md86
-rw-r--r--doc/administration/packages/container_registry.md10
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql35
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json81
-rw-r--r--doc/ci/examples/artifactory_and_gitlab/index.md5
-rw-r--r--doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md7
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md6
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md6
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/index.md7
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md6
-rw-r--r--doc/ci/ssh_keys/README.md1
-rw-r--r--doc/install/postgresql_extensions.md4
-rw-r--r--doc/topics/git/lfs/migrate_to_git_lfs.md2
-rw-r--r--doc/update/mysql_to_postgresql.md4
-rw-r--r--lib/gitlab/access.rb13
-rw-r--r--lib/gitlab/auth/atlassian/auth_hash.rb31
-rw-r--r--lib/gitlab/auth/atlassian/identity_linker.rb30
-rw-r--r--lib/gitlab/auth/atlassian/user.rb35
-rw-r--r--lib/gitlab/auth/o_auth/provider.rb9
-rw-r--r--lib/gitlab/pages/settings.rb6
-rw-r--r--lib/gitlab/project_authorizations.rb2
-rw-r--r--lib/gitlab/static_site_editor/config.rb63
-rw-r--r--lib/gitlab/static_site_editor/config/combined_config.rb35
-rw-r--r--lib/gitlab/static_site_editor/config/file_config.rb16
-rw-r--r--lib/gitlab/static_site_editor/config/generated_config.rb68
-rw-r--r--locale/gitlab.pot89
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/auto_devops_templates_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb2
-rw-r--r--rubocop/cop/avoid_route_redirect_leading_slash.rb4
-rw-r--r--spec/controllers/admin/dev_ops_report_controller_spec.rb (renamed from spec/controllers/admin/dev_ops_score_controller_spec.rb)2
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb45
-rw-r--r--spec/controllers/profiles/accounts_controller_spec.rb13
-rw-r--r--spec/controllers/projects/static_site_editor_controller_spec.rb11
-rw-r--r--spec/factories/dev_ops_report_metrics.rb (renamed from spec/factories/dev_ops_score_metrics.rb)2
-rw-r--r--spec/factories/group_members.rb6
-rw-r--r--spec/factories/users.rb10
-rw-r--r--spec/features/admin/admin_dev_ops_report_spec.rb (renamed from spec/features/admin/admin_dev_ops_score_spec.rb)12
-rw-r--r--spec/finders/group_members_finder_spec.rb1
-rw-r--r--spec/finders/groups_finder_spec.rb8
-rw-r--r--spec/finders/members_finder_spec.rb12
-rw-r--r--spec/fixtures/api/schemas/entities/issue_sidebar.json3
-rw-r--r--spec/frontend/issue_show/components/app_spec.js9
-rw-r--r--spec/frontend/issue_show/components/highlight_bar_spec.js60
-rw-r--r--spec/frontend/issue_show/components/incident_tabs_spec.js7
-rw-r--r--spec/frontend/sidebar/components/severity/severity_spec.js57
-rw-r--r--spec/frontend/sidebar/components/severity/sidebar_severity_spec.js166
-rw-r--r--spec/helpers/auth_helper_spec.rb40
-rw-r--r--spec/helpers/issuables_helper_spec.rb2
-rw-r--r--spec/lib/gitlab/auth/atlassian/auth_hash_spec.rb50
-rw-r--r--spec/lib/gitlab/auth/atlassian/identity_linker_spec.rb71
-rw-r--r--spec/lib/gitlab/auth/atlassian/user_spec.rb60
-rw-r--r--spec/lib/gitlab/pages/settings_spec.rb21
-rw-r--r--spec/lib/gitlab/project_authorizations_spec.rb60
-rw-r--r--spec/lib/gitlab/static_site_editor/config/combined_config_spec.rb37
-rw-r--r--spec/lib/gitlab/static_site_editor/config/file_config_spec.rb17
-rw-r--r--spec/lib/gitlab/static_site_editor/config/generated_config_spec.rb (renamed from spec/lib/gitlab/static_site_editor/config_spec.rb)24
-rw-r--r--spec/models/concerns/issuable_spec.rb18
-rw-r--r--spec/models/dev_ops_report/metric_spec.rb (renamed from spec/models/dev_ops_score/metric_spec.rb)4
-rw-r--r--spec/models/group_spec.rb1
-rw-r--r--spec/models/member_spec.rb39
-rw-r--r--spec/presenters/dev_ops_report/metric_presenter_spec.rb (renamed from spec/presenters/dev_ops_score/metric_presenter_spec.rb)4
-rw-r--r--spec/routing/admin_routing_spec.rb14
-rw-r--r--spec/routing/instance_statistics_routing_spec.rb4
-rw-r--r--spec/services/authorized_project_update/project_create_service_spec.rb17
-rw-r--r--spec/services/authorized_project_update/project_group_link_create_service_spec.rb11
-rw-r--r--spec/services/submit_usage_ping_service_spec.rb8
-rw-r--r--spec/support/shared_examples/lib/gitlab/auth/atlassian_identity_shared_examples.rb10
-rw-r--r--spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb2
137 files changed, 2071 insertions, 386 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index aedcd4f71a0..b478539846c 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -584,6 +584,8 @@
rules:
- <<: *if-not-ee
when: never
+ - <<: *if-merge-request
+ changes: *code-backstage-patterns
- <<: *if-master-schedule-2-hourly
- <<: *if-merge-request-title-run-all-rspec
diff --git a/Gemfile b/Gemfile
index 5bd568066fc..8825c529a9d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -45,6 +45,7 @@ gem 'omniauth_crowd', '~> 2.4.0'
gem 'omniauth-authentiq', '~> 0.3.3'
gem 'omniauth_openid_connect', '~> 0.3.5'
gem 'omniauth-salesforce', '~> 1.0.5'
+gem 'omniauth-atlassian-oauth2', '~> 0.2.0'
gem 'rack-oauth2', '~> 1.9.3'
gem 'jwt', '~> 2.1.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 4b97e6cf24e..75e5cfd5508 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -737,6 +737,9 @@ GEM
omniauth (1.9.0)
hashie (>= 3.4.6, < 3.7.0)
rack (>= 1.6.2, < 3)
+ omniauth-atlassian-oauth2 (0.2.0)
+ omniauth (>= 1.1.1)
+ omniauth-oauth2 (>= 1.5)
omniauth-auth0 (2.0.0)
omniauth-oauth2 (~> 1.4)
omniauth-authentiq (0.3.3)
@@ -1393,6 +1396,7 @@ DEPENDENCIES
octokit (~> 4.15)
oj (~> 3.10.6)
omniauth (~> 1.8)
+ omniauth-atlassian-oauth2 (~> 0.2.0)
omniauth-auth0 (~> 2.0.0)
omniauth-authentiq (~> 0.3.3)
omniauth-azure-oauth2 (~> 0.0.9)
diff --git a/app/assets/images/auth_buttons/atlassian_64.png b/app/assets/images/auth_buttons/atlassian_64.png
new file mode 100644
index 00000000000..548f1c93318
--- /dev/null
+++ b/app/assets/images/auth_buttons/atlassian_64.png
Binary files differ
diff --git a/app/assets/javascripts/admin/dev_ops_score/components/usage_ping_disabled.vue b/app/assets/javascripts/admin/dev_ops_report/components/usage_ping_disabled.vue
index 5429ec403d3..5429ec403d3 100644
--- a/app/assets/javascripts/admin/dev_ops_score/components/usage_ping_disabled.vue
+++ b/app/assets/javascripts/admin/dev_ops_report/components/usage_ping_disabled.vue
diff --git a/app/assets/javascripts/issue_show/components/incidents/highlight_bar/graphql/queries/get_highlight_bar_info.graphql b/app/assets/javascripts/issue_show/components/incidents/highlight_bar/graphql/queries/get_highlight_bar_info.graphql
new file mode 100644
index 00000000000..fe299adf53e
--- /dev/null
+++ b/app/assets/javascripts/issue_show/components/incidents/highlight_bar/graphql/queries/get_highlight_bar_info.graphql
@@ -0,0 +1,12 @@
+query getHighlightBarInfo($iid: String!, $fullPath: ID!) {
+ project(fullPath: $fullPath) {
+ issue(iid: $iid) {
+ alertManagementAlert {
+ title
+ detailsUrl
+ createdAt
+ eventCount
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/issue_show/components/incidents/highlight_bar/higlight_bar.vue b/app/assets/javascripts/issue_show/components/incidents/highlight_bar/higlight_bar.vue
new file mode 100644
index 00000000000..dbef301856d
--- /dev/null
+++ b/app/assets/javascripts/issue_show/components/incidents/highlight_bar/higlight_bar.vue
@@ -0,0 +1,51 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+import { formatDate } from '~/lib/utils/datetime_utility';
+import getHighlightBarInfo from './graphql/queries/get_highlight_bar_info.graphql';
+
+export default {
+ components: {
+ GlLink,
+ },
+ inject: ['fullPath', 'iid'],
+ apollo: {
+ alert: {
+ query: getHighlightBarInfo,
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ iid: this.iid,
+ };
+ },
+ update: data => data.project?.issue?.alertManagementAlert,
+ },
+ },
+ computed: {
+ startTime() {
+ return formatDate(this.alert.createdAt, 'yyyy-mm-dd Z');
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ v-if="alert"
+ class="gl-border-solid gl-border-1 gl-border-gray-100 gl-p-5 gl-mb-3 gl-rounded-base gl-display-flex gl-justify-content-space-between"
+ >
+ <div class="text-truncate gl-pr-3">
+ <span class="gl-font-weight-bold">{{ s__('HighlightBar|Original alert:') }}</span>
+ <gl-link :href="alert.detailsUrl">{{ alert.title }}</gl-link>
+ </div>
+
+ <div class="gl-pr-3 gl-white-space-nowrap">
+ <span class="gl-font-weight-bold">{{ s__('HighlightBar|Alert start time:') }}</span>
+ {{ startTime }}
+ </div>
+
+ <div class="gl-white-space-nowrap">
+ <span class="gl-font-weight-bold">{{ s__('HighlightBar|Alert events:') }}</span>
+ <span>{{ alert.eventCount }}</span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/issue_show/components/incident_tabs.vue b/app/assets/javascripts/issue_show/components/incidents/incident_tabs.vue
index f6e82cfaa74..e5dde1aaca6 100644
--- a/app/assets/javascripts/issue_show/components/incident_tabs.vue
+++ b/app/assets/javascripts/issue_show/components/incidents/incident_tabs.vue
@@ -1,24 +1,23 @@
<script>
import { GlTab, GlTabs } from '@gitlab/ui';
-import DescriptionComponent from './description.vue';
+import DescriptionComponent from '../description.vue';
+import HighlightBar from './highlight_bar/higlight_bar.vue';
export default {
components: {
GlTab,
GlTabs,
DescriptionComponent,
+ HighlightBar,
},
};
</script>
<template>
<div>
- <gl-tabs
- content-class="gl-reset-line-height gl-mt-3"
- class="gl-mt-n3"
- data-testid="incident-tabs"
- >
+ <gl-tabs content-class="gl-reset-line-height" class="gl-mt-n3" data-testid="incident-tabs">
<gl-tab :title="__('Summary')">
+ <highlight-bar />
<description-component v-bind="$attrs" />
</gl-tab>
</gl-tabs>
diff --git a/app/assets/javascripts/issue_show/incident.js b/app/assets/javascripts/issue_show/incident.js
index 82b862a2195..a34e75ee64a 100644
--- a/app/assets/javascripts/issue_show/incident.js
+++ b/app/assets/javascripts/issue_show/incident.js
@@ -1,13 +1,28 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
import issuableApp from './components/app.vue';
-import incidentTabs from './components/incident_tabs.vue';
+import incidentTabs from './components/incidents/incident_tabs.vue';
+
+Vue.use(VueApollo);
export default function initIssuableApp(issuableData = {}) {
+ const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+ });
+
+ const { projectNamespace, projectPath, iid } = issuableData;
+
return new Vue({
el: document.getElementById('js-issuable-app'),
+ apolloProvider,
components: {
issuableApp,
},
+ provide: {
+ fullPath: `${projectNamespace}/${projectPath}`,
+ iid,
+ },
render(createElement) {
return createElement('issuable-app', {
props: {
diff --git a/app/assets/javascripts/pages/admin/dev_ops_score/index.js b/app/assets/javascripts/pages/admin/dev_ops_report/index.js
index 9d018408b88..643497003ba 100644
--- a/app/assets/javascripts/pages/admin/dev_ops_score/index.js
+++ b/app/assets/javascripts/pages/admin/dev_ops_report/index.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import UserCallout from '~/user_callout';
-import UsagePingDisabled from '~/admin/dev_ops_score/components/usage_ping_disabled.vue';
+import UsagePingDisabled from '~/admin/dev_ops_report/components/usage_ping_disabled.vue';
document.addEventListener('DOMContentLoaded', () => {
// eslint-disable-next-line no-new
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
index 589636c620b..aa53c5040e8 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
@@ -2,13 +2,11 @@
import { mapGetters } from 'vuex';
import { GlTooltipDirective, GlFriendlyWrap, GlIcon } from '@gitlab/ui';
import { __ } from '~/locale';
-import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
export default {
name: 'TestsSuiteTable',
components: {
GlIcon,
- SmartVirtualList,
GlFriendlyWrap,
},
directives: {
@@ -27,8 +25,6 @@ export default {
return this.getSuiteTests.length > 0;
},
},
- maxShownRows: 30,
- typicalRowHeight: 75,
wrapSymbols: ['::', '#', '.', '_', '-', '/', '\\'],
};
</script>
@@ -60,66 +56,60 @@ export default {
</div>
</div>
- <smart-virtual-list
- :length="getSuiteTests.length"
- :remain="$options.maxShownRows"
- :size="$options.typicalRowHeight"
+ <div
+ v-for="(testCase, index) in getSuiteTests"
+ :key="index"
+ class="gl-responsive-table-row rounded align-items-md-start mt-xs-3 js-case-row"
>
- <div
- v-for="(testCase, index) in getSuiteTests"
- :key="index"
- class="gl-responsive-table-row rounded align-items-md-start mt-xs-3 js-case-row"
- >
- <div class="table-section section-20 section-wrap">
- <div role="rowheader" class="table-mobile-header">{{ __('Suite') }}</div>
- <div class="table-mobile-content pr-md-1 gl-overflow-wrap-break">
- <gl-friendly-wrap :symbols="$options.wrapSymbols" :text="testCase.classname" />
- </div>
+ <div class="table-section section-20 section-wrap">
+ <div role="rowheader" class="table-mobile-header">{{ __('Suite') }}</div>
+ <div class="table-mobile-content pr-md-1 gl-overflow-wrap-break">
+ <gl-friendly-wrap :symbols="$options.wrapSymbols" :text="testCase.classname" />
</div>
+ </div>
- <div class="table-section section-20 section-wrap">
- <div role="rowheader" class="table-mobile-header">{{ __('Name') }}</div>
- <div class="table-mobile-content pr-md-1 gl-overflow-wrap-break">
- <gl-friendly-wrap
- data-testid="caseName"
- :symbols="$options.wrapSymbols"
- :text="testCase.name"
- />
- </div>
+ <div class="table-section section-20 section-wrap">
+ <div role="rowheader" class="table-mobile-header">{{ __('Name') }}</div>
+ <div class="table-mobile-content pr-md-1 gl-overflow-wrap-break">
+ <gl-friendly-wrap
+ data-testid="caseName"
+ :symbols="$options.wrapSymbols"
+ :text="testCase.name"
+ />
</div>
+ </div>
- <div class="table-section section-10 section-wrap">
- <div role="rowheader" class="table-mobile-header">{{ __('Status') }}</div>
- <div class="table-mobile-content text-center">
- <div
- class="add-border ci-status-icon d-flex align-items-center justify-content-end justify-content-md-center"
- :class="`ci-status-icon-${testCase.status}`"
- >
- <gl-icon :size="24" :name="testCase.icon" />
- </div>
+ <div class="table-section section-10 section-wrap">
+ <div role="rowheader" class="table-mobile-header">{{ __('Status') }}</div>
+ <div class="table-mobile-content text-center">
+ <div
+ class="add-border ci-status-icon d-flex align-items-center justify-content-end justify-content-md-center"
+ :class="`ci-status-icon-${testCase.status}`"
+ >
+ <gl-icon :size="24" :name="testCase.icon" />
</div>
</div>
+ </div>
- <div class="table-section flex-grow-1">
- <div role="rowheader" class="table-mobile-header">{{ __('Trace'), }}</div>
- <div class="table-mobile-content">
- <pre
- v-if="testCase.system_output"
- class="build-trace build-trace-rounded text-left"
- ><code class="bash p-0">{{testCase.system_output}}</code></pre>
- </div>
+ <div class="table-section flex-grow-1">
+ <div role="rowheader" class="table-mobile-header">{{ __('Trace'), }}</div>
+ <div class="table-mobile-content">
+ <pre
+ v-if="testCase.system_output"
+ class="build-trace build-trace-rounded text-left"
+ ><code class="bash p-0">{{testCase.system_output}}</code></pre>
</div>
+ </div>
- <div class="table-section section-10 section-wrap">
- <div role="rowheader" class="table-mobile-header">
- {{ __('Duration') }}
- </div>
- <div class="table-mobile-content text-right pr-sm-1">
- {{ testCase.formattedTime }}
- </div>
+ <div class="table-section section-10 section-wrap">
+ <div role="rowheader" class="table-mobile-header">
+ {{ __('Duration') }}
+ </div>
+ <div class="table-mobile-content text-right pr-sm-1">
+ {{ testCase.formattedTime }}
</div>
</div>
- </smart-virtual-list>
+ </div>
</div>
<div v-else>
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
index e774fe06fbe..5f9c0be3ccc 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
@@ -2,13 +2,11 @@
import { mapGetters } from 'vuex';
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
-import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
export default {
name: 'TestsSummaryTable',
components: {
GlIcon,
- SmartVirtualList,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -31,8 +29,6 @@ export default {
this.$emit('row-click', index);
},
},
- maxShownRows: 20,
- typicalRowHeight: 55,
};
</script>
@@ -69,83 +65,77 @@ export default {
</div>
</div>
- <smart-virtual-list
- :length="getTestSuites.length"
- :remain="$options.maxShownRows"
- :size="$options.typicalRowHeight"
+ <div
+ v-for="(testSuite, index) in getTestSuites"
+ :key="index"
+ role="row"
+ class="gl-responsive-table-row test-reports-summary-row rounded js-suite-row"
+ :class="{
+ 'gl-responsive-table-row-clickable cursor-pointer': !testSuite.suite_error,
+ }"
+ @click="tableRowClick(index)"
>
- <div
- v-for="(testSuite, index) in getTestSuites"
- :key="index"
- role="row"
- class="gl-responsive-table-row test-reports-summary-row rounded js-suite-row"
- :class="{
- 'gl-responsive-table-row-clickable cursor-pointer': !testSuite.suite_error,
- }"
- @click="tableRowClick(index)"
- >
- <div class="table-section section-25">
- <div role="rowheader" class="table-mobile-header font-weight-bold">
- {{ __('Suite') }}
- </div>
- <div class="table-mobile-content underline cgray pl-3">
- {{ testSuite.name }}
- <gl-icon
- v-if="testSuite.suite_error"
- ref="suiteErrorIcon"
- v-gl-tooltip
- name="error"
- :title="testSuite.suite_error"
- class="vertical-align-middle"
- />
- </div>
+ <div class="table-section section-25">
+ <div role="rowheader" class="table-mobile-header font-weight-bold">
+ {{ __('Suite') }}
</div>
+ <div class="table-mobile-content underline cgray pl-3">
+ {{ testSuite.name }}
+ <gl-icon
+ v-if="testSuite.suite_error"
+ ref="suiteErrorIcon"
+ v-gl-tooltip
+ name="error"
+ :title="testSuite.suite_error"
+ class="vertical-align-middle"
+ />
+ </div>
+ </div>
- <div class="table-section section-25">
- <div role="rowheader" class="table-mobile-header font-weight-bold">
- {{ __('Duration') }}
- </div>
- <div class="table-mobile-content text-md-left">
- {{ testSuite.formattedTime }}
- </div>
+ <div class="table-section section-25">
+ <div role="rowheader" class="table-mobile-header font-weight-bold">
+ {{ __('Duration') }}
+ </div>
+ <div class="table-mobile-content text-md-left">
+ {{ testSuite.formattedTime }}
</div>
+ </div>
- <div class="table-section section-10 text-center">
- <div role="rowheader" class="table-mobile-header font-weight-bold">
- {{ __('Failed') }}
- </div>
- <div class="table-mobile-content">{{ testSuite.failed_count }}</div>
+ <div class="table-section section-10 text-center">
+ <div role="rowheader" class="table-mobile-header font-weight-bold">
+ {{ __('Failed') }}
</div>
+ <div class="table-mobile-content">{{ testSuite.failed_count }}</div>
+ </div>
- <div class="table-section section-10 text-center">
- <div role="rowheader" class="table-mobile-header font-weight-bold">
- {{ __('Errors') }}
- </div>
- <div class="table-mobile-content">{{ testSuite.error_count }}</div>
+ <div class="table-section section-10 text-center">
+ <div role="rowheader" class="table-mobile-header font-weight-bold">
+ {{ __('Errors') }}
</div>
+ <div class="table-mobile-content">{{ testSuite.error_count }}</div>
+ </div>
- <div class="table-section section-10 text-center">
- <div role="rowheader" class="table-mobile-header font-weight-bold">
- {{ __('Skipped') }}
- </div>
- <div class="table-mobile-content">{{ testSuite.skipped_count }}</div>
+ <div class="table-section section-10 text-center">
+ <div role="rowheader" class="table-mobile-header font-weight-bold">
+ {{ __('Skipped') }}
</div>
+ <div class="table-mobile-content">{{ testSuite.skipped_count }}</div>
+ </div>
- <div class="table-section section-10 text-center">
- <div role="rowheader" class="table-mobile-header font-weight-bold">
- {{ __('Passed') }}
- </div>
- <div class="table-mobile-content">{{ testSuite.success_count }}</div>
+ <div class="table-section section-10 text-center">
+ <div role="rowheader" class="table-mobile-header font-weight-bold">
+ {{ __('Passed') }}
</div>
+ <div class="table-mobile-content">{{ testSuite.success_count }}</div>
+ </div>
- <div class="table-section section-10 text-right pr-md-3">
- <div role="rowheader" class="table-mobile-header font-weight-bold">
- {{ __('Total') }}
- </div>
- <div class="table-mobile-content">{{ testSuite.total_count }}</div>
+ <div class="table-section section-10 text-right pr-md-3">
+ <div role="rowheader" class="table-mobile-header font-weight-bold">
+ {{ __('Total') }}
</div>
+ <div class="table-mobile-content">{{ testSuite.total_count }}</div>
</div>
- </smart-virtual-list>
+ </div>
</div>
<div v-else>
diff --git a/app/assets/javascripts/sidebar/components/severity/constants.js b/app/assets/javascripts/sidebar/components/severity/constants.js
new file mode 100644
index 00000000000..c7d6a51ea1c
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/severity/constants.js
@@ -0,0 +1,43 @@
+import { __, s__ } from '~/locale';
+
+export const INCIDENT_SEVERITY = {
+ CRITICAL: {
+ value: 'CRITICAL',
+ icon: 'critical',
+ label: s__('IncidentManagement|Critical - S1'),
+ },
+ HIGH: {
+ value: 'HIGH',
+ icon: 'high',
+ label: s__('IncidentManagement|High - S2'),
+ },
+ MEDIUM: {
+ value: 'MEDIUM',
+ icon: 'medium',
+ label: s__('IncidentManagement|Medium - S3'),
+ },
+ LOW: {
+ value: 'LOW',
+ icon: 'low',
+ label: s__('IncidentManagement|Low - S4'),
+ },
+ UNKNOWN: {
+ value: 'UNKNOWN',
+ icon: 'unknown',
+ label: s__('IncidentManagement|Unknown'),
+ },
+};
+
+export const ISSUABLE_TYPES = {
+ INCIDENT: 'incident',
+};
+
+export const SIDEBAR_ANIMATION_DURATION = 300;
+
+export const I18N = {
+ UPDATE_SEVERITY_ERROR: s__('SeverityWidget|There was an error while updating severity.'),
+ TRY_AGAIN: __('Please try again'),
+ EDIT: __('Edit'),
+ SEVERITY: s__('SeverityWidget|Severity'),
+ SEVERITY_VALUE: s__('SeverityWidget|Severity: %{severity}'),
+};
diff --git a/app/assets/javascripts/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql b/app/assets/javascripts/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql
new file mode 100644
index 00000000000..750e757971f
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql
@@ -0,0 +1,9 @@
+mutation updateIssuableSeverity($projectPath: ID!, $severity: IssuableSeverity!, $iid: String!) {
+ issueSetSeverity(input: { iid: $iid, severity: $severity, projectPath: $projectPath }) {
+ errors
+ issue {
+ iid
+ severity
+ }
+ }
+}
diff --git a/app/assets/javascripts/sidebar/components/severity/severity.vue b/app/assets/javascripts/sidebar/components/severity/severity.vue
new file mode 100644
index 00000000000..7e7d62256c9
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/severity/severity.vue
@@ -0,0 +1,42 @@
+<script>
+import { GlIcon } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlIcon,
+ },
+ props: {
+ severity: {
+ type: Object,
+ required: true,
+ validator(severity) {
+ const { value, label, icon } = severity;
+ return value && label && icon;
+ },
+ },
+ iconSize: {
+ type: Number,
+ required: false,
+ default: 12,
+ },
+ iconOnly: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ class="incident-severity gl-display-inline-flex gl-align-items-center gl-justify-content-between"
+ >
+ <gl-icon
+ :size="iconSize"
+ :name="`severity-${severity.icon}`"
+ :class="[`icon-${severity.icon}`, { 'gl-mr-3': !iconOnly }]"
+ />
+ <span v-if="!iconOnly">{{ severity.label }}</span>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
new file mode 100644
index 00000000000..65ff7a6d2ac
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
@@ -0,0 +1,195 @@
+<script>
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlLoadingIcon,
+ GlTooltip,
+ GlSprintf,
+ GlLink,
+} from '@gitlab/ui';
+import { INCIDENT_SEVERITY, ISSUABLE_TYPES, SIDEBAR_ANIMATION_DURATION, I18N } from './constants';
+import updateIssuableSeverity from './graphql/mutations/update_issuable_severity.mutation.graphql';
+import SeverityToken from './severity.vue';
+import createFlash from '~/flash';
+
+export default {
+ i18n: I18N,
+ components: {
+ GlLoadingIcon,
+ GlTooltip,
+ GlSprintf,
+ GlDropdown,
+ GlDropdownItem,
+ GlLink,
+ SeverityToken,
+ },
+ props: {
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ iid: {
+ type: String,
+ required: true,
+ },
+ initialSeverity: {
+ type: String,
+ required: false,
+ default: INCIDENT_SEVERITY.UNKNOWN.value,
+ },
+ issuableType: {
+ type: String,
+ required: false,
+ default: ISSUABLE_TYPES.INCIDENT,
+ validator: value => {
+ // currently severity is supported only for incidents, but this list might be extended
+ return [ISSUABLE_TYPES.INCIDENT].includes(value);
+ },
+ },
+ },
+ data() {
+ return {
+ isDropdownShowing: false,
+ isUpdating: false,
+ severity: this.initialSeverity,
+ };
+ },
+ computed: {
+ severitiesList() {
+ switch (this.issuableType) {
+ case ISSUABLE_TYPES.INCIDENT:
+ return Object.values(INCIDENT_SEVERITY);
+ default:
+ return [];
+ }
+ },
+ dropdownClass() {
+ return this.isDropdownShowing ? 'show' : 'gl-display-none';
+ },
+ selectedItem() {
+ return this.severitiesList.find(severity => severity.value === this.severity);
+ },
+ },
+ mounted() {
+ document.addEventListener('click', this.handleOffClick);
+ },
+ beforeDestroy() {
+ document.removeEventListener('click', this.handleOffClick);
+ },
+ methods: {
+ handleOffClick(event) {
+ if (!this.isDropdownShowing) {
+ return;
+ }
+
+ if (!this.$refs.sidebarSeverity.contains(event.target)) {
+ this.hideDropdown();
+ }
+ },
+ hideDropdown() {
+ this.isDropdownShowing = false;
+ const event = new Event('hidden.gl.dropdown');
+ this.$el.dispatchEvent(event);
+ },
+ toggleFormDropdown(collapsedSidebar) {
+ this.isDropdownShowing = !this.isDropdownShowing;
+ const timeout = collapsedSidebar ? SIDEBAR_ANIMATION_DURATION : 0;
+ setTimeout(() => {
+ const { dropdown } = this.$refs;
+ if (dropdown && this.isDropdownShowing) {
+ dropdown.show();
+ }
+ }, timeout);
+ },
+ updateSeverity(value) {
+ this.hideDropdown();
+ this.isUpdating = true;
+ this.$apollo
+ .mutate({
+ mutation: updateIssuableSeverity,
+ variables: {
+ iid: this.iid,
+ severity: value,
+ projectPath: this.projectPath,
+ },
+ })
+ .then(resp => {
+ const {
+ data: {
+ issueSetSeverity: {
+ errors = [],
+ issue: { severity },
+ },
+ },
+ } = resp;
+
+ if (errors[0]) {
+ throw errors[0];
+ }
+ this.severity = severity;
+ })
+ .catch(() =>
+ createFlash({
+ message: `${this.$options.i18n.UPDATE_SEVERITY_ERROR} ${this.$options.i18n.TRY_AGAIN}`,
+ }),
+ )
+ .finally(() => {
+ this.isUpdating = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div ref="sidebarSeverity" class="block">
+ <div ref="severity" class="sidebar-collapsed-icon" @click="toggleFormDropdown(true)">
+ <severity-token :severity="selectedItem" :icon-size="14" :icon-only="true" />
+ <gl-tooltip :target="() => $refs.severity" boundary="viewport" placement="left">
+ <gl-sprintf :message="$options.i18n.SEVERITY_VALUE">
+ <template #severity>
+ {{ selectedItem.label }}
+ </template>
+ </gl-sprintf>
+ </gl-tooltip>
+ </div>
+
+ <div class="hide-collapsed">
+ <p class="title gl-display-flex gl-justify-content-space-between">
+ {{ $options.i18n.SEVERITY }}
+ <gl-link
+ data-testid="editButton"
+ href="#"
+ @click="toggleFormDropdown"
+ @keydown.esc="hideDropdown"
+ >
+ {{ $options.i18n.EDIT }}
+ </gl-link>
+ </p>
+
+ <gl-dropdown
+ ref="dropdown"
+ :class="dropdownClass"
+ block
+ :text="selectedItem.label"
+ toggle-class="dropdown-menu-toggle gl-mb-2"
+ @keydown.esc.native="hideDropdown"
+ >
+ <gl-dropdown-item
+ v-for="option in severitiesList"
+ :key="option.value"
+ data-testid="severityDropdownItem"
+ :is-check-item="true"
+ :is-checked="option.value === severity"
+ @click="updateSeverity(option.value)"
+ >
+ <severity-token :severity="option" />
+ </gl-dropdown-item>
+ </gl-dropdown>
+
+ <gl-loading-icon v-if="isUpdating" :inline="true" />
+
+ <severity-token v-else-if="!isDropdownShowing" :severity="selectedItem" />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index 015219200db..e7520cb096a 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -8,6 +8,7 @@ import SidebarMoveIssue from './lib/sidebar_move_issue';
import IssuableLockForm from './components/lock/issuable_lock_form.vue';
import sidebarParticipants from './components/participants/sidebar_participants.vue';
import sidebarSubscriptions from './components/subscriptions/sidebar_subscriptions.vue';
+import SidebarSeverity from './components/severity/sidebar_severity.vue';
import Translate from '../vue_shared/translate';
import createDefaultClient from '~/lib/graphql';
import { store } from '~/notes/stores';
@@ -159,6 +160,35 @@ function mountTimeTrackingComponent() {
});
}
+function mountSeverityComponent() {
+ const severityContainerEl = document.querySelector('#js-severity');
+
+ if (!severityContainerEl) {
+ return false;
+ }
+ const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+ });
+
+ const { fullPath, iid, severity } = getSidebarOptions();
+
+ return new Vue({
+ el: severityContainerEl,
+ apolloProvider,
+ components: {
+ SidebarSeverity,
+ },
+ render: createElement =>
+ createElement('sidebar-severity', {
+ props: {
+ projectPath: fullPath,
+ iid: String(iid),
+ initialSeverity: severity.toUpperCase(),
+ },
+ }),
+ });
+}
+
export function mountSidebar(mediator) {
mountAssigneesComponent(mediator);
mountConfidentialComponent(mediator);
@@ -173,6 +203,8 @@ export function mountSidebar(mediator) {
).init();
mountTimeTrackingComponent();
+
+ mountSeverityComponent();
}
export { getSidebarOptions };
diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss
index b27602f6b4a..f706b615e7e 100644
--- a/app/assets/stylesheets/_page_specific_files.scss
+++ b/app/assets/stylesheets/_page_specific_files.scss
@@ -10,7 +10,7 @@
@import './pages/cycle_analytics';
@import './pages/deploy_keys';
@import './pages/detail_page';
-@import './pages/dev_ops_score';
+@import './pages/dev_ops_report';
@import './pages/diff';
@import './pages/editor';
@import './pages/environment_logs';
diff --git a/app/assets/stylesheets/pages/alert_management/severity-icons.scss b/app/assets/stylesheets/pages/alert_management/severity-icons.scss
index 6004697b3e1..f58ad87a673 100644
--- a/app/assets/stylesheets/pages/alert_management/severity-icons.scss
+++ b/app/assets/stylesheets/pages/alert_management/severity-icons.scss
@@ -1,3 +1,4 @@
+.incident-severity,
.incident-management-list,
.alert-management-details {
.icon-critical {
diff --git a/app/assets/stylesheets/pages/dev_ops_score.scss b/app/assets/stylesheets/pages/dev_ops_report.scss
index a6bc9b9332b..a6bc9b9332b 100644
--- a/app/assets/stylesheets/pages/dev_ops_score.scss
+++ b/app/assets/stylesheets/pages/dev_ops_report.scss
diff --git a/app/controllers/admin/dev_ops_score_controller.rb b/app/controllers/admin/dev_ops_report_controller.rb
index 3e012e7d76b..bed0d51c331 100644
--- a/app/controllers/admin/dev_ops_score_controller.rb
+++ b/app/controllers/admin/dev_ops_report_controller.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-class Admin::DevOpsScoreController < Admin::ApplicationController
+class Admin::DevOpsReportController < Admin::ApplicationController
include Analytics::UniqueVisitsHelper
track_unique_visits :show, target_id: 'i_analytics_dev_ops_score'
# rubocop: disable CodeReuse/ActiveRecord
def show
- @metric = DevOpsScore::Metric.order(:created_at).last&.present
+ @metric = DevOpsReport::Metric.order(:created_at).last&.present
end
# rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 0245c00aacb..6414792dd43 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -19,7 +19,7 @@ class Admin::GroupsController < Admin::ApplicationController
# the Group with statistics).
@group = Group.with_statistics.find(group&.id)
@members = present_members(
- @group.members.order("access_level DESC").page(params[:members_page]))
+ group_members.order("access_level DESC").page(params[:members_page]))
@requesters = present_members(
AccessRequestsFinder.new(@group).execute(current_user))
@projects = @group.projects.with_statistics.page(params[:projects_page])
@@ -82,6 +82,10 @@ class Admin::GroupsController < Admin::ApplicationController
@group ||= Group.find_by_full_path(params[:id])
end
+ def group_members
+ @group.members
+ end
+
def group_params
params.require(:group).permit(allowed_group_params)
end
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index a558b01f0c6..b798d6680bc 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -83,6 +83,10 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
end
+ def atlassian_oauth2
+ omniauth_flow(Gitlab::Auth::Atlassian)
+ end
+
private
def log_failed_login(user, provider)
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index 95e055a44db..b19285e98bb 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -7,10 +7,9 @@ class Profiles::AccountsController < Profiles::ApplicationController
render(locals: show_view_variables)
end
- # rubocop: disable CodeReuse/ActiveRecord
def unlink
provider = params[:provider]
- identity = current_user.identities.find_by(provider: provider)
+ identity = find_identity(provider)
return render_404 unless identity
@@ -22,13 +21,18 @@ class Profiles::AccountsController < Profiles::ApplicationController
redirect_to profile_account_path
end
- # rubocop: enable CodeReuse/ActiveRecord
private
def show_view_variables
{}
end
+
+ def find_identity(provider)
+ return current_user.atlassian_identity if provider == 'atlassian_oauth2'
+
+ current_user.identities.find_by(provider: provider) # rubocop: disable CodeReuse/ActiveRecord
+ end
end
Profiles::AccountsController.prepend_if_ee('EE::Profiles::AccountsController')
diff --git a/app/controllers/projects/static_site_editor_controller.rb b/app/controllers/projects/static_site_editor_controller.rb
index 9ec50ff8196..3251814bac8 100644
--- a/app/controllers/projects/static_site_editor_controller.rb
+++ b/app/controllers/projects/static_site_editor_controller.rb
@@ -14,7 +14,13 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
end
def show
- @config = Gitlab::StaticSiteEditor::Config.new(@repository, @ref, @path, params[:return_url])
+ config = Gitlab::StaticSiteEditor::Config::CombinedConfig.new(
+ @repository,
+ @ref,
+ @path,
+ params[:return_url]
+ )
+ @data = config.data
end
private
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index a4b00588368..ce0d52ad97a 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -27,7 +27,7 @@ class GroupMembersFinder < UnionFinder
relations << group_members if include_relations.include?(:direct)
if include_relations.include?(:inherited) && group.parent
- parents_members = GroupMember.non_request
+ parents_members = GroupMember.non_request.non_minimal_access
.where(source_id: group.ancestors.select(:id))
.where.not(user_id: group.users.select(:id))
@@ -35,7 +35,7 @@ class GroupMembersFinder < UnionFinder
end
if include_relations.include?(:descendants)
- descendant_members = GroupMember.non_request
+ descendant_members = GroupMember.non_request.non_minimal_access
.where(source_id: group.descendants.select(:id))
.where.not(user_id: group.users.select(:id))
diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb
index ce9137f91bb..013ed03a789 100644
--- a/app/finders/members_finder.rb
+++ b/app/finders/members_finder.rb
@@ -63,7 +63,7 @@ class MembersFinder
def direct_group_members(include_descendants)
requested_relations = [:inherited, :direct]
requested_relations << :descendants if include_descendants
- GroupMembersFinder.new(group).execute(include_relations: requested_relations).non_invite # rubocop: disable CodeReuse/Finder
+ GroupMembersFinder.new(group).execute(include_relations: requested_relations).non_invite.non_minimal_access # rubocop: disable CodeReuse/Finder
end
def project_invited_groups_members
@@ -73,7 +73,7 @@ class MembersFinder
.public_or_visible_to_user(current_user)
.select(:id)
- GroupMember.with_source_id(invited_groups_ids_including_ancestors)
+ GroupMember.with_source_id(invited_groups_ids_including_ancestors).non_minimal_access
end
def distinct_union_of_members(union_members)
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index a57e27d23c8..7f8cb66a84f 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module AuthHelper
- PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq salesforce).freeze
+ PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq salesforce atlassian_oauth2).freeze
LDAP_PROVIDER = /\Aldap/.freeze
def ldap_enabled?
@@ -133,6 +133,8 @@ module AuthHelper
# rubocop: disable CodeReuse/ActiveRecord
def auth_active?(provider)
+ return current_user.atlassian_identity.present? if provider == :atlassian_oauth2
+
current_user.identities.exists?(provider: provider.to_s)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/helpers/dev_ops_score_helper.rb b/app/helpers/dev_ops_report_helper.rb
index 9a673998149..ab7e56fc1a2 100644
--- a/app/helpers/dev_ops_score_helper.rb
+++ b/app/helpers/dev_ops_report_helper.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-module DevOpsScoreHelper
+module DevOpsReportHelper
def score_level(score)
if score < 33.33
'low'
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 398e76b6697..54fa3ca57b8 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -294,7 +294,8 @@ module IssuablesHelper
hasClosingMergeRequest: issuable.merge_requests_count(current_user) != 0,
issueType: issuable.issue_type,
zoomMeetingUrl: ZoomMeeting.canonical_meeting_url(issuable),
- sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier # rubocop:disable CodeReuse/ActiveRecord
+ sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier, # rubocop:disable CodeReuse/ActiveRecord
+ iid: issuable.iid.to_s
}
end
@@ -465,6 +466,7 @@ module IssuablesHelper
rootPath: root_path,
fullPath: issuable[:project_full_path],
iid: issuable[:iid],
+ severity: issuable[:severity],
timeTrackingLimitToHours: Gitlab::CurrentSettings.time_tracking_limit_to_hours
}
end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index ae9612308e4..2318596c63a 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -63,7 +63,7 @@ module NavHelper
end
def admin_analytics_nav_links
- %w(dev_ops_score cohorts)
+ %w(dev_ops_report cohorts)
end
def group_issues_sub_menu_items
diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb
index 4bbfa499919..0227ad1092d 100644
--- a/app/helpers/system_note_helper.rb
+++ b/app/helpers/system_note_helper.rb
@@ -7,7 +7,7 @@ module SystemNoteHelper
'description' => 'pencil-square',
'merge' => 'git-merge',
'merged' => 'git-merge',
- 'opened' => 'issue-open',
+ 'opened' => 'issues',
'closed' => 'issue-close',
'time_tracking' => 'timer',
'assignee' => 'user',
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 09b1dbaba58..888e1b384a2 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -185,6 +185,10 @@ module Issuable
is_a?(TimeTrackable) && !incident?
end
+ def supports_severity?
+ incident?
+ end
+
def incident?
is_a?(Issue) && super
end
diff --git a/app/models/concerns/loaded_in_group_list.rb b/app/models/concerns/loaded_in_group_list.rb
index 79ff82d9f99..e624b9aa356 100644
--- a/app/models/concerns/loaded_in_group_list.rb
+++ b/app/models/concerns/loaded_in_group_list.rb
@@ -54,6 +54,7 @@ module LoadedInGroupList
.where(members[:source_type].eq(Namespace.name))
.where(members[:source_id].eq(namespaces[:id]))
.where(members[:requested_at].eq(nil))
+ .where(members[:access_level].gt(Gitlab::Access::MINIMAL_ACCESS))
end
end
@@ -70,7 +71,7 @@ module LoadedInGroupList
end
def member_count
- @member_count ||= try(:preloaded_member_count) || users.count
+ @member_count ||= try(:preloaded_member_count) || members.count
end
end
diff --git a/app/models/dev_ops_score/card.rb b/app/models/dev_ops_report/card.rb
index b1894cf4138..4060cb1e5b6 100644
--- a/app/models/dev_ops_score/card.rb
+++ b/app/models/dev_ops_report/card.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-module DevOpsScore
+module DevOpsReport
class Card
attr_accessor :metric, :title, :description, :feature, :blog, :docs
diff --git a/app/models/dev_ops_score/idea_to_production_step.rb b/app/models/dev_ops_report/idea_to_production_step.rb
index d892793cf97..2503d5949e5 100644
--- a/app/models/dev_ops_score/idea_to_production_step.rb
+++ b/app/models/dev_ops_report/idea_to_production_step.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-module DevOpsScore
+module DevOpsReport
class IdeaToProductionStep
attr_accessor :metric, :title, :features
diff --git a/app/models/dev_ops_score/metric.rb b/app/models/dev_ops_report/metric.rb
index a9133128ce9..14eff725433 100644
--- a/app/models/dev_ops_score/metric.rb
+++ b/app/models/dev_ops_report/metric.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-module DevOpsScore
+module DevOpsReport
class Metric < ApplicationRecord
include Presentable
diff --git a/app/models/group.rb b/app/models/group.rb
index f8cbaa2495c..5c6e946b02e 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -20,8 +20,10 @@ class Group < Namespace
UpdateSharedRunnersError = Class.new(StandardError)
- has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
+ has_many :all_group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent
+ has_many :group_members, -> { where(requested_at: nil).where.not(members: { access_level: Gitlab::Access::MINIMAL_ACCESS }) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
alias_method :members, :group_members
+
has_many :users, through: :group_members
has_many :owners,
-> { where(members: { access_level: Gitlab::Access::OWNER }) },
@@ -395,6 +397,10 @@ class Group < Namespace
])
end
+ def users_count
+ members.count
+ end
+
# Returns all users that are members of projects
# belonging to the current group or sub-groups
def project_users_with_descendants
@@ -630,6 +636,7 @@ class Group < Namespace
.where(group_member_table[:requested_at].eq(nil))
.where(group_member_table[:source_id].eq(group_group_link_table[:shared_with_group_id]))
.where(group_member_table[:source_type].eq('Namespace'))
+ .non_minimal_access
end
def smallest_value_arel(args, column_alias)
diff --git a/app/models/member.rb b/app/models/member.rb
index 55bb78b7f94..5a084a3a2e6 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -25,7 +25,6 @@ class Member < ApplicationRecord
validates :user_id, uniqueness: { scope: [:source_type, :source_id],
message: "already exists in source",
allow_nil: true }
- validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
validate :higher_access_level_than_group, unless: :importing?
validates :invite_email,
presence: {
@@ -60,6 +59,7 @@ class Member < ApplicationRecord
left_join_users
.where(user_ok)
.where(requested_at: nil)
+ .non_minimal_access
.reorder(nil)
end
@@ -68,6 +68,8 @@ class Member < ApplicationRecord
left_join_users
.where(users: { state: 'active' })
.non_request
+ .non_invite
+ .non_minimal_access
.reorder(nil)
end
@@ -85,6 +87,7 @@ class Member < ApplicationRecord
scope :developers, -> { active.where(access_level: DEVELOPER) }
scope :maintainers, -> { active.where(access_level: MAINTAINER) }
scope :non_guests, -> { where('members.access_level > ?', GUEST) }
+ scope :non_minimal_access, -> { where('members.access_level > ?', MINIMAL_ACCESS) }
scope :owners, -> { active.where(access_level: OWNER) }
scope :owners_and_maintainers, -> { active.where(access_level: [OWNER, MAINTAINER]) }
scope :with_user, -> (user) { where(user: user) }
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 8c224dea88f..34958936c9f 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -13,6 +13,9 @@ class GroupMember < Member
# Make sure group member points only to group as it source
default_value_for :source_type, SOURCE_TYPE
validates :source_type, format: { with: /\ANamespace\z/ }
+ validates :access_level, presence: true
+ validate :access_level_inclusion
+
default_scope { where(source_type: SOURCE_TYPE) } # rubocop:disable Cop/DefaultScope
scope :of_groups, ->(groups) { where(source_id: groups.select(:id)) }
@@ -45,6 +48,12 @@ class GroupMember < Member
private
+ def access_level_inclusion
+ return if access_level.in?(Gitlab::Access.all_values)
+
+ errors.add(:access_level, "is not included in the list")
+ end
+
def send_invite
run_after_commit_or_now { notification_service.invite_group_member(self, @raw_invite_token) }
diff --git a/app/models/user.rb b/app/models/user.rb
index 9c4af91ad7e..9b8fe4881ad 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -120,7 +120,7 @@ class User < ApplicationRecord
# Groups
has_many :members
- has_many :group_members, -> { where(requested_at: nil) }, source: 'GroupMember'
+ has_many :group_members, -> { where(requested_at: nil).where("access_level >= ?", Gitlab::Access::GUEST) }, source: 'GroupMember'
has_many :groups, through: :group_members
has_many :owned_groups, -> { where(members: { access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :group
has_many :maintainers_groups, -> { where(members: { access_level: Gitlab::Access::MAINTAINER }) }, through: :group_members, source: :group
diff --git a/app/presenters/dev_ops_score/metric_presenter.rb b/app/presenters/dev_ops_score/metric_presenter.rb
index d22beefee54..e7363293435 100644
--- a/app/presenters/dev_ops_score/metric_presenter.rb
+++ b/app/presenters/dev_ops_score/metric_presenter.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-module DevOpsScore
+module DevOpsReport
class MetricPresenter < Gitlab::View::Presenter::Simple
def cards
[
diff --git a/app/serializers/issuable_sidebar_basic_entity.rb b/app/serializers/issuable_sidebar_basic_entity.rb
index 11df60ade70..53c123c06fd 100644
--- a/app/serializers/issuable_sidebar_basic_entity.rb
+++ b/app/serializers/issuable_sidebar_basic_entity.rb
@@ -105,6 +105,7 @@ class IssuableSidebarBasicEntity < Grape::Entity
expose :supports_time_tracking?, as: :supports_time_tracking
expose :supports_milestone?, as: :supports_milestone
+ expose :supports_severity?, as: :supports_severity
private
diff --git a/app/serializers/issue_sidebar_basic_entity.rb b/app/serializers/issue_sidebar_basic_entity.rb
index 165dc462cfe..e9e05718af9 100644
--- a/app/serializers/issue_sidebar_basic_entity.rb
+++ b/app/serializers/issue_sidebar_basic_entity.rb
@@ -3,6 +3,7 @@
class IssueSidebarBasicEntity < IssuableSidebarBasicEntity
expose :due_date
expose :confidential
+ expose :severity
end
IssueSidebarBasicEntity.prepend_if_ee('EE::IssueSidebarBasicEntity')
diff --git a/app/services/submit_usage_ping_service.rb b/app/services/submit_usage_ping_service.rb
index 9191943caa7..2fbeaf4405c 100644
--- a/app/services/submit_usage_ping_service.rb
+++ b/app/services/submit_usage_ping_service.rb
@@ -51,11 +51,11 @@ class SubmitUsagePingService
end
def store_metrics(response)
- metrics = response['conv_index'] || response['dev_ops_score']
+ metrics = response['conv_index'] || response['dev_ops_score'] # leaving dev_ops_score here, as the response data comes from the gitlab-version-com
return unless metrics.present?
- DevOpsScore::Metric.create!(
+ DevOpsReport::Metric.create!(
metrics.slice(*METRICS)
)
end
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index b6ac5db4d2d..4acfc96caf2 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -17,37 +17,34 @@
.well-segment.well-centered
= link_to admin_projects_path do
%h3.text-center
- Projects:
- = approximate_count_with_delimiters(@counts, Project)
+ = s_('AdminArea|Projects: %{number_of_projects}') % { number_of_projects: approximate_count_with_delimiters(@counts, Project) }
%hr
- = link_to('New project', new_project_path, class: "btn btn-success gl-w-full")
+ = link_to(s_('AdminArea|New project'), new_project_path, class: "btn btn-success gl-w-full")
.col-sm-4
.info-well.dark-well
.well-segment.well-centered
= link_to admin_users_path do
%h3.text-center
- Users:
- = approximate_count_with_delimiters(@counts, User)
+ = s_('AdminArea|Users: %{number_of_users}') % { number_of_users: approximate_count_with_delimiters(@counts, User) }
%hr
.btn-group.d-flex{ role: 'group' }
- = link_to 'New user', new_admin_user_path, class: "btn btn-success gl-w-full"
+ = link_to s_('AdminArea|New user'), new_admin_user_path, class: "btn btn-success gl-w-full"
= link_to s_('AdminArea|Users statistics'), admin_dashboard_stats_path, class: 'btn btn-primary gl-w-full'
.col-sm-4
.info-well.dark-well
.well-segment.well-centered
= link_to admin_groups_path do
%h3.text-center
- Groups:
- = approximate_count_with_delimiters(@counts, Group)
+ = s_('AdminArea|Groups: %{number_of_groups}') % { number_of_groups: approximate_count_with_delimiters(@counts, Group) }
%hr
- = link_to 'New group', new_admin_group_path, class: "btn btn-success gl-w-full"
+ = link_to s_('AdminArea|New group'), new_admin_group_path, class: "btn btn-success gl-w-full"
.row
.col-md-4
#js-admin-statistics-container
.col-md-4
.info-well
.well-segment.admin-well.admin-well-features
- %h4 Features
+ %h4= s_('AdminArea|Features')
= feature_entry(_('Sign up'),
href: general_admin_application_settings_path(anchor: 'js-signup-settings'),
enabled: allow_signup?)
@@ -87,42 +84,41 @@
.info-well
.well-segment.admin-well
%h4
- Components
+ = s_('AdminArea|Components')
- if Gitlab::CurrentSettings.version_check_enabled
.float-right
= version_status_badge
%p
- %a{ href: general_admin_application_settings_path }
- GitLab
+ = link_to _('GitLab'), general_admin_application_settings_path
%span.float-right
= Gitlab::VERSION
= "(#{Gitlab.revision})"
%p
- GitLab Shell
+ = _('GitLab Shell')
%span.float-right
= Gitlab::Shell.version
%p
- GitLab Workhorse
+ = _('GitLab Workhorse')
%span.float-right
= gitlab_workhorse_version
%p
- GitLab API
+ = _('GitLab API')
%span.float-right
= API::API::version
- if Gitlab.config.pages.enabled
%p
- GitLab Pages
+ = _('GitLab Pages')
%span.float-right
= Gitlab::Pages::VERSION
= render_if_exists 'admin/dashboard/geo'
%p
- Ruby
+ = _('Ruby')
%span.float-right
#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
%p
- Rails
+ = _('Rails')
%span.float-right
#{Rails::VERSION::STRING}
%p
@@ -130,12 +126,12 @@
%span.float-right
= Gitlab::Database.version
%p
- = link_to "Gitaly Servers", admin_gitaly_servers_path
+ = link_to _("Gitaly Servers"), admin_gitaly_servers_path
.row
.col-md-4
.info-well
.well-segment.admin-well
- %h4 Latest projects
+ %h4= s_('AdminArea|Latest projects')
- @projects.each do |project|
%p
= link_to project.full_name, admin_project_path(project), class: 'str-truncated-60'
@@ -144,7 +140,7 @@
.col-md-4
.info-well
.well-segment.admin-well
- %h4 Latest users
+ %h4= s_('AdminArea|Latest users')
- @users.each do |user|
%p
= link_to [:admin, user], class: 'str-truncated-60' do
@@ -154,7 +150,7 @@
.col-md-4
.info-well
.well-segment.admin-well
- %h4 Latest groups
+ %h4= s_('AdminArea|Latest groups')
- @groups.each do |group|
%p
= link_to [:admin, group], class: 'str-truncated-60' do
diff --git a/app/views/admin/dev_ops_score/_callout.html.haml b/app/views/admin/dev_ops_report/_callout.html.haml
index 1b50c27834d..220456a4295 100644
--- a/app/views/admin/dev_ops_score/_callout.html.haml
+++ b/app/views/admin/dev_ops_report/_callout.html.haml
@@ -1,5 +1,5 @@
.gl-mt-3
-.user-callout{ data: { uid: 'dev_ops_score_intro_callout_dismissed' } }
+.user-callout{ data: { uid: 'dev_ops_report_intro_callout_dismissed' } }
.bordered-box.landing.content-block
%button.btn.btn-default.close.js-close-callout{ type: 'button',
'aria-label' => _('Dismiss DevOps Report introduction') }
@@ -10,4 +10,4 @@
%p
= _('Your DevOps Report gives an overview of how you are using GitLab from a feature perspective. View how you compare with other organizations, discover features you are not using, and learn best practices through blog posts and white papers.')
.svg-container.devops
- = custom_icon('dev_ops_score_overview')
+ = custom_icon('dev_ops_report_overview')
diff --git a/app/views/admin/dev_ops_score/_card.html.haml b/app/views/admin/dev_ops_report/_card.html.haml
index dd6e5c0f108..dd6e5c0f108 100644
--- a/app/views/admin/dev_ops_score/_card.html.haml
+++ b/app/views/admin/dev_ops_report/_card.html.haml
diff --git a/app/views/admin/dev_ops_score/_no_data.html.haml b/app/views/admin/dev_ops_report/_no_data.html.haml
index 8ea3797b1cc..585aa878b0b 100644
--- a/app/views/admin/dev_ops_score/_no_data.html.haml
+++ b/app/views/admin/dev_ops_report/_no_data.html.haml
@@ -1,6 +1,6 @@
.container.devops-empty
.col-sm-12.justify-content-center.text-center
- = custom_icon('dev_ops_score_no_data')
+ = custom_icon('dev_ops_report_no_data')
%h4= _('Data is still calculating...')
%p
= _('In order to gather accurate feature usage data, it can take 1 to 2 weeks to see your index.')
diff --git a/app/views/admin/dev_ops_score/show.html.haml b/app/views/admin/dev_ops_report/show.html.haml
index 3a7fb9d7c8c..57d80bc8c85 100644
--- a/app/views/admin/dev_ops_score/show.html.haml
+++ b/app/views/admin/dev_ops_report/show.html.haml
@@ -2,7 +2,7 @@
- usage_ping_enabled = Gitlab::CurrentSettings.usage_ping_enabled
.container
- - if usage_ping_enabled && show_callout?('dev_ops_score_intro_callout_dismissed')
+ - if usage_ping_enabled && show_callout?('dev_ops_report_intro_callout_dismissed')
= render 'callout'
.gl-mt-3
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index ab817b2ef6e..3a82f3803bd 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -27,7 +27,7 @@
%span.gl-ml-5
= sprite_icon('users', css_class: 'gl-vertical-align-text-bottom')
- = number_with_delimiter(group.users.count)
+ = number_with_delimiter(group.users_count)
%span.gl-ml-5.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) }
= visibility_level_icon(group.visibility_level)
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 960a4a93019..cb5277c02f0 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -49,7 +49,7 @@
= _('Gitaly Servers')
= nav_link(controller: admin_analytics_nav_links) do
- = link_to admin_dev_ops_score_path, data: { qa_selector: 'admin_analytics_link' } do
+ = link_to admin_dev_ops_report_path, data: { qa_selector: 'admin_analytics_link' } do
.nav-icon-container
= sprite_icon('chart')
%span.nav-item-name
@@ -57,12 +57,12 @@
%ul.sidebar-sub-level-items{ data: { qa_selector: 'admin_sidebar_analytics_submenu_content' } }
= nav_link(controller: admin_analytics_nav_links, html_options: { class: "fly-out-top-item" }) do
- = link_to admin_dev_ops_score_path do
+ = link_to admin_dev_ops_report_path do
%strong.fly-out-top-item-name
= _('Analytics')
%li.divider.fly-out-top-item
- = nav_link(controller: :dev_ops_score) do
- = link_to admin_dev_ops_score_path, title: _('DevOps Report') do
+ = nav_link(controller: :dev_ops_report) do
+ = link_to admin_dev_ops_report_path, title: _('DevOps Report') do
%span
= _('DevOps Report')
= nav_link(controller: :cohorts) do
diff --git a/app/views/projects/static_site_editor/show.html.haml b/app/views/projects/static_site_editor/show.html.haml
index 2d817912335..cbe27cefba3 100644
--- a/app/views/projects/static_site_editor/show.html.haml
+++ b/app/views/projects/static_site_editor/show.html.haml
@@ -1 +1 @@
-#static-site-editor{ data: @config.payload.merge({ merge_requests_illustration_path: image_path('illustrations/merge_requests.svg') }) }
+#static-site-editor{ data: @data }
diff --git a/app/views/shared/icons/_dev_ops_score_no_data.svg b/app/views/shared/icons/_dev_ops_report_no_data.svg
index 5de929859ae..5de929859ae 100644
--- a/app/views/shared/icons/_dev_ops_score_no_data.svg
+++ b/app/views/shared/icons/_dev_ops_report_no_data.svg
diff --git a/app/views/shared/icons/_dev_ops_score_no_index.svg b/app/views/shared/icons/_dev_ops_report_no_index.svg
index 0577efca93f..0577efca93f 100644
--- a/app/views/shared/icons/_dev_ops_score_no_index.svg
+++ b/app/views/shared/icons/_dev_ops_report_no_index.svg
diff --git a/app/views/shared/icons/_dev_ops_score_overview.svg b/app/views/shared/icons/_dev_ops_report_overview.svg
index 2f31113bad7..2f31113bad7 100644
--- a/app/views/shared/icons/_dev_ops_score_overview.svg
+++ b/app/views/shared/icons/_dev_ops_report_overview.svg
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index dc985f96e3f..28bf64841fc 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -133,6 +133,9 @@
= render_if_exists 'shared/issuable/sidebar_weight', issuable_sidebar: issuable_sidebar
+ - if issuable_sidebar[:supports_severity]
+ #js-severity
+
- if issuable_sidebar.dig(:features_available, :health_status)
.js-sidebar-status-entry-point
diff --git a/changelogs/unreleased/212274-remove-virtual-list-from-test-report.yml b/changelogs/unreleased/212274-remove-virtual-list-from-test-report.yml
new file mode 100644
index 00000000000..e66665a4dd0
--- /dev/null
+++ b/changelogs/unreleased/212274-remove-virtual-list-from-test-report.yml
@@ -0,0 +1,5 @@
+---
+title: Remove virtual scroll list from pipeline test report
+merge_request: 41935
+author:
+type: fixed
diff --git a/changelogs/unreleased/229402-incidents-severity-widget.yml b/changelogs/unreleased/229402-incidents-severity-widget.yml
new file mode 100644
index 00000000000..f2003681c45
--- /dev/null
+++ b/changelogs/unreleased/229402-incidents-severity-widget.yml
@@ -0,0 +1,5 @@
+---
+title: Incident severity widget
+merge_request: 39859
+author:
+type: added
diff --git a/changelogs/unreleased/Incident-highlight-bar.yml b/changelogs/unreleased/Incident-highlight-bar.yml
new file mode 100644
index 00000000000..ddd746801cb
--- /dev/null
+++ b/changelogs/unreleased/Incident-highlight-bar.yml
@@ -0,0 +1,5 @@
+---
+title: Incident highlight bar widget
+merge_request: 41702
+author:
+type: added
diff --git a/changelogs/unreleased/dblessing-atlassian-integration-sign-in.yml b/changelogs/unreleased/dblessing-atlassian-integration-sign-in.yml
new file mode 100644
index 00000000000..c3f204420db
--- /dev/null
+++ b/changelogs/unreleased/dblessing-atlassian-integration-sign-in.yml
@@ -0,0 +1,5 @@
+---
+title: Add OmniAuth sign-in via Atlassian Cloud
+merge_request: 40178
+author:
+type: added
diff --git a/changelogs/unreleased/i18n-admin-dashboard.yml b/changelogs/unreleased/i18n-admin-dashboard.yml
new file mode 100644
index 00000000000..fa75f02e874
--- /dev/null
+++ b/changelogs/unreleased/i18n-admin-dashboard.yml
@@ -0,0 +1,5 @@
+---
+title: Externalize i18n strings from admin dashboard
+merge_request: 41387
+author: Takuya Noguchi
+type: other
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 7190c2c7331..b392b5a5448 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -1404,6 +1404,11 @@ test:
app_id: 'YOUR_CLIENT_ID',
app_secret: 'YOUR_CLIENT_SECRET'
}
+ - { name: 'atlassian_oauth2',
+ app_id: 'YOUR_CLIENT_ID',
+ app_secret: 'YOUR_CLIENT_SECRET',
+ args: { scope: 'offline_access read:jira-user read:jira-work', prompt: 'consent' }
+ }
ldap:
enabled: false
servers:
diff --git a/config/routes.rb b/config/routes.rb
index 481189c97c0..0512b7ca132 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -70,8 +70,8 @@ Rails.application.routes.draw do
# Use this scope for all new global routes.
scope path: '-' do
# remove in 13.5
- get '/instance_statistics', to: redirect('admin/dev_ops_score')
- get '/instance_statistics/dev_ops_score', to: redirect('admin/dev_ops_score')
+ get '/instance_statistics', to: redirect('admin/dev_ops_report')
+ get '/instance_statistics/dev_ops_score', to: redirect('admin/dev_ops_report')
get '/instance_statistics/cohorts', to: redirect('admin/cohorts')
# Autocomplete
get '/autocomplete/users' => 'autocomplete#users'
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 258a6a04221..1dd1149a9d2 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -90,7 +90,9 @@ namespace :admin do
resources :projects, only: [:index]
resources :instance_statistics, only: :index
- resource :dev_ops_score, controller: 'dev_ops_score', only: :show
+ resource :dev_ops_report, controller: 'dev_ops_report', only: :show
+ # remove in 13.5
+ get '/dev_ops_score', to: redirect('admin/dev_ops_report')
resources :cohorts, only: :index
scope(path: 'projects/*namespace_id',
diff --git a/db/fixtures/development/21_dev_ops_score_metrics.rb b/db/fixtures/development/21_dev_ops_report_metrics.rb
index afea7fb4bd0..8b4956da37e 100644
--- a/db/fixtures/development/21_dev_ops_score_metrics.rb
+++ b/db/fixtures/development/21_dev_ops_report_metrics.rb
@@ -1,5 +1,5 @@
Gitlab::Seeder.quiet do
- dev_ops_score_metric = DevOpsScore::Metric.new(
+ dev_ops_report_metric = DevOpsReport::Metric.new(
leader_issues: 10.2,
instance_issues: 3.2,
@@ -31,10 +31,10 @@ Gitlab::Seeder.quiet do
instance_service_desk_issues: 15.1
)
- if dev_ops_score_metric.save
+ if dev_ops_report_metric.save
print '.'
else
- puts dev_ops_score_metric.errors.full_messages
+ puts dev_ops_report_metric.errors.full_messages
print 'F'
end
end
diff --git a/doc/administration/auth/README.md b/doc/administration/auth/README.md
index 60e1dfb4637..926a4abab7d 100644
--- a/doc/administration/auth/README.md
+++ b/doc/administration/auth/README.md
@@ -11,6 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab integrates with the following external authentication and authorization
providers:
+- [Atlassian](atlassian.md)
- [Auth0](../../integration/auth0.md)
- [Authentiq](authentiq.md)
- [AWS Cognito](cognito.md)
diff --git a/doc/administration/auth/atlassian.md b/doc/administration/auth/atlassian.md
new file mode 100644
index 00000000000..3a1f5eeb0c2
--- /dev/null
+++ b/doc/administration/auth/atlassian.md
@@ -0,0 +1,86 @@
+---
+type: reference
+stage: Manage
+group: Access
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
+# Atlassian OmniAuth Provider
+
+To enable the Atlassian OmniAuth provider for passwordless authentication you must register an application with Atlassian.
+
+## Atlassian application registration
+
+1. Go to <https://developer.atlassian.com/apps/> and sign-in with the Atlassian
+ account that will administer the application.
+
+1. Click **Create a new app**.
+
+1. Choose an App Name, such as 'GitLab', and click **Create**.
+
+1. Note the `Client ID` and `Secret` for the [GitLab configuration](#gitlab-configuration) steps.
+
+1. In the left sidebar under **APIS AND FEATURES**, click **OAuth 2.0 (3LO)**.
+
+1. Enter the GitLab callback URL using the format `https://gitlab.example.com/users/auth/atlassian_oauth2/callback` and click **Save changes**.
+
+1. Click **+ Add** in the left sidebar under **APIS AND FEATURES**.
+
+1. Click **Add** for **Jira platform REST API** and then **Configure**.
+
+1. Click **Add** next to the following scopes:
+ - **View Jira issue data**
+ - **View user profiles**
+ - **Create and manage issues**
+
+## GitLab configuration
+
+1. On your GitLab server, open the configuration file:
+
+ For Omnibus GitLab installations:
+
+ ```shell
+ sudo editor /etc/gitlab/gitlab.rb
+ ```
+
+ For installations from source:
+
+ ```shell
+ sudo -u git -H editor /home/git/gitlab/config/gitlab.yml
+ ```
+
+1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings to enable single sign-on and add `atlassian_oauth2` as an OAuth provider.
+
+1. Add the provider configuration for Atlassian:
+
+ For Omnibus GitLab installations:
+
+ ```ruby
+ gitlab_rails['omniauth_providers'] = [
+ {
+ name: "atlassian_oauth2",
+ app_id: "YOUR_CLIENT_ID",
+ app_secret: "YOUR_CLIENT_SECRET",
+ args: { scope: 'offline_access read:jira-user read:jira-work', prompt: 'consent' }
+ }
+ ]
+ ```
+
+ For installations from source:
+
+ ```yaml
+ - name: "atlassian_oauth2",
+ app_id: "YOUR_CLIENT_ID",
+ app_secret: "YOUR_CLIENT_SECRET",
+ args: { scope: 'offline_access read:jira-user read:jira-work', prompt: 'consent' }
+ ```
+
+1. Change `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` to the Client credentials you received in [application registration](#atlassian-application-registration) steps.
+
+1. Save the configuration file.
+
+1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
+
+On the sign-in page there should now be an Atlassian icon below the regular sign in form. Click the icon to begin the authentication process.
+
+If everything goes right, the user is signed in to GitLab using their Atlassian credentials.
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 1883f6659e6..c1230a81fb8 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -750,11 +750,15 @@ U = <user_id>
# Get required details / objects
user = User.find_by_id(U)
project = Project.find_by_id(P)
-repo = ContainerRepository.find_by(project_id: P)
policy = ContainerExpirationPolicy.find_by(project_id: P)
-# Start the tag cleanup
-Projects::ContainerRepository::CleanupTagsService.new(project, user, policy.attributes.except("created_at", "updated_at")).execute(repo)
+# Loop through each container repository
+project.container_repositories.find_each do |repo|
+ puts repo.attributes
+
+ # Start the tag cleanup
+ puts Projects::ContainerRepository::CleanupTagsService.new(project, user, policy.attributes.except("created_at", "updated_at")).execute(repo)
+end
```
NOTE: **Note:**
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 210c10e0949..701b9f0b205 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -13089,6 +13089,41 @@ type Project {
): VulnerabilityConnection
"""
+ Number of vulnerabilities per day for the project
+ """
+ vulnerabilitiesCountByDay(
+ """
+ Returns the elements in the list that come after the specified cursor.
+ """
+ after: String
+
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: String
+
+ """
+ Last day for which to fetch vulnerability history
+ """
+ endDate: ISO8601Date!
+
+ """
+ Returns the first _n_ elements from the list.
+ """
+ first: Int
+
+ """
+ Returns the last _n_ elements from the list.
+ """
+ last: Int
+
+ """
+ First day for which to fetch vulnerability history
+ """
+ startDate: ISO8601Date!
+ ): VulnerabilitiesCountByDayConnection
+
+ """
Vulnerability scanners reported on the project vulnerabilties
"""
vulnerabilityScanners(
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 9729d8e799a..4ba643425ef 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -38172,6 +38172,87 @@
"deprecationReason": null
},
{
+ "name": "vulnerabilitiesCountByDay",
+ "description": "Number of vulnerabilities per day for the project",
+ "args": [
+ {
+ "name": "startDate",
+ "description": "First day for which to fetch vulnerability history",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ISO8601Date",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "endDate",
+ "description": "Last day for which to fetch vulnerability history",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ISO8601Date",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "after",
+ "description": "Returns the elements in the list that come after the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "before",
+ "description": "Returns the elements in the list that come before the specified cursor.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "first",
+ "description": "Returns the first _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "last",
+ "description": "Returns the last _n_ elements from the list.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "Int",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "VulnerabilitiesCountByDayConnection",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "vulnerabilityScanners",
"description": "Vulnerability scanners reported on the project vulnerabilties",
"args": [
diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md
index c1b3ddec1b9..f5c86d2461d 100644
--- a/doc/ci/examples/artifactory_and_gitlab/index.md
+++ b/doc/ci/examples/artifactory_and_gitlab/index.md
@@ -3,12 +3,7 @@ stage: Verify
group: Continuous Integration
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
disqus_identifier: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html'
-author: Fabio Busatto
-author_gitlab: bikebilly
-level: intermediate
-article_type: tutorial
type: tutorial
-date: 2017-08-15
---
# How to deploy Maven projects to Artifactory with GitLab CI/CD
diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
index e0d4f3f2402..b113b10f2e3 100644
--- a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
+++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
@@ -2,14 +2,7 @@
stage: Release
group: Progressive Delivery
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
-author: Dylan Griffith
-author_gitlab: DylanGriffith
-level: intermediate
-article_type: tutorial
type: tutorial
-date: 2018-06-07
-last_updated: 2019-04-08
-description: "Continuous Deployment of a Spring Boot application to Cloud Foundry with GitLab CI/CD"
---
# Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
index 35a35d97a4b..8a95c0b2258 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
@@ -2,13 +2,7 @@
stage: Verify
group: Continuous Integration
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
-author: Ryan Hall
-author_gitlab: blitzgren
-level: intermediate
-article_type: tutorial
type: tutorial
-date: 2018-03-07
-last_updated: 2019-03-11
---
# DevOps and Game Dev with GitLab CI/CD
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
index b737b0d6b14..1f6c81a68aa 100644
--- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -2,13 +2,7 @@
stage: Verify
group: Testing
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
-author: Vincent Tunru
-author_gitlab: Vinnl
-level: advanced
-article_type: user guide
type: tutorial
-date: 2019-02-18
-description: 'Confidence checking your entire app every time a new feature is added can quickly become repetitive. Learn how to automate it with GitLab CI/CD.'
---
# End-to-end testing with GitLab CI/CD and WebdriverIO
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
index 3d41929f1f2..d6a829a4a44 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
@@ -2,14 +2,7 @@
stage: Verify
group: Continuous Integration
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
-disqus_identifier: 'https://docs.gitlab.com/ee/articles/laravel_with_gitlab_and_envoy/index.html'
-author: Mehran Rasulian
-author_gitlab: mehranrasulian
-level: intermediate
-article_type: tutorial
type: tutorial
-date: 2017-08-31
-last_updated: 2019-03-06
---
# Test and deploy Laravel applications with GitLab CI/CD and Envoy
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
index f2620461c09..0aed274fc82 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
@@ -2,13 +2,7 @@
stage: Verify
group: Continuous Integration
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
-author: Alexandre S Hostert
-author_gitlab: Hostert
-level: beginner
-article_type: tutorial
type: tutorial
-date: 2018-02-20
-last_updated: 2019-03-06
---
# Testing a Phoenix application with GitLab CI/CD
diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md
index 5ae9f8bc375..d8280316f19 100644
--- a/doc/ci/ssh_keys/README.md
+++ b/doc/ci/ssh_keys/README.md
@@ -2,7 +2,6 @@
stage: Verify
group: Continuous Integration
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
-last_updated: 2017-12-13
type: tutorial
---
diff --git a/doc/install/postgresql_extensions.md b/doc/install/postgresql_extensions.md
index 4156d72097d..9e5a1e3d627 100644
--- a/doc/install/postgresql_extensions.md
+++ b/doc/install/postgresql_extensions.md
@@ -1,7 +1,3 @@
----
-last_updated: 2020-09-01
----
-
# Managing PostgreSQL extensions
This guide documents how to manage PostgreSQL extensions for installations with an external
diff --git a/doc/topics/git/lfs/migrate_to_git_lfs.md b/doc/topics/git/lfs/migrate_to_git_lfs.md
index 944f4d8f78d..f0ad7570d87 100644
--- a/doc/topics/git/lfs/migrate_to_git_lfs.md
+++ b/doc/topics/git/lfs/migrate_to_git_lfs.md
@@ -1,7 +1,5 @@
---
-type: tutorial, concepts
description: "How to migrate an existing Git repository to Git LFS with BFG."
-last_updated: 2019-07-11
---
# Migrate a Git repo into Git LFS with BFG
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 9ad77a80d50..0759d45147a 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -1,7 +1,3 @@
----
-last_updated: 2019-06-18
----
-
# Migrating from MySQL to PostgreSQL
This guide documents how to take a working GitLab instance that uses MySQL and
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index bf4438fb518..830980f0997 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -9,12 +9,13 @@ module Gitlab
module Access
AccessDeniedError = Class.new(StandardError)
- NO_ACCESS = 0
- GUEST = 10
- REPORTER = 20
- DEVELOPER = 30
- MAINTAINER = 40
- OWNER = 50
+ NO_ACCESS = 0
+ MINIMAL_ACCESS = 5
+ GUEST = 10
+ REPORTER = 20
+ DEVELOPER = 30
+ MAINTAINER = 40
+ OWNER = 50
# Branch protection settings
PROTECTION_NONE = 0
diff --git a/lib/gitlab/auth/atlassian/auth_hash.rb b/lib/gitlab/auth/atlassian/auth_hash.rb
new file mode 100644
index 00000000000..047e4eabc51
--- /dev/null
+++ b/lib/gitlab/auth/atlassian/auth_hash.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Auth
+ module Atlassian
+ class AuthHash < Gitlab::Auth::OAuth::AuthHash
+ def token
+ credentials[:token]
+ end
+
+ def refresh_token
+ credentials[:refresh_token]
+ end
+
+ def expires?
+ credentials[:expires]
+ end
+
+ def expires_at
+ credentials[:expires_at]
+ end
+
+ private
+
+ def credentials
+ auth_hash[:credentials]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/auth/atlassian/identity_linker.rb b/lib/gitlab/auth/atlassian/identity_linker.rb
new file mode 100644
index 00000000000..4dec54d44d6
--- /dev/null
+++ b/lib/gitlab/auth/atlassian/identity_linker.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Auth
+ module Atlassian
+ class IdentityLinker < OmniauthIdentityLinkerBase
+ extend ::Gitlab::Utils::Override
+ include ::Gitlab::Utils::StrongMemoize
+
+ private
+
+ override :identity
+ def identity
+ strong_memoize(:identity) do
+ current_user.atlassian_identity || build_atlassian_identity
+ end
+ end
+
+ def build_atlassian_identity
+ identity = current_user.build_atlassian_identity
+ ::Gitlab::Auth::Atlassian::User.assign_identity_from_auth_hash!(identity, auth_hash)
+ end
+
+ def auth_hash
+ ::Gitlab::Auth::Atlassian::AuthHash.new(oauth)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/auth/atlassian/user.rb b/lib/gitlab/auth/atlassian/user.rb
new file mode 100644
index 00000000000..6ab7741cc54
--- /dev/null
+++ b/lib/gitlab/auth/atlassian/user.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Auth
+ module Atlassian
+ class User < Gitlab::Auth::OAuth::User
+ def self.assign_identity_from_auth_hash!(identity, auth_hash)
+ identity.extern_uid = auth_hash.uid
+ identity.token = auth_hash.token
+ identity.refresh_token = auth_hash.refresh_token
+ identity.expires_at = Time.at(auth_hash.expires_at).utc.to_datetime if auth_hash.expires?
+
+ identity
+ end
+
+ protected
+
+ def find_by_uid_and_provider
+ ::Atlassian::Identity.find_by_extern_uid(auth_hash.uid)&.user
+ end
+
+ def add_or_update_user_identities
+ return unless gl_user
+
+ identity = gl_user.atlassian_identity || gl_user.build_atlassian_identity
+ self.class.assign_identity_from_auth_hash!(identity, auth_hash)
+ end
+
+ def auth_hash=(auth_hash)
+ @auth_hash = ::Gitlab::Auth::Atlassian::AuthHash.new(auth_hash)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb
index 1ca59aa827b..1eae7af442d 100644
--- a/lib/gitlab/auth/o_auth/provider.rb
+++ b/lib/gitlab/auth/o_auth/provider.rb
@@ -5,10 +5,11 @@ module Gitlab
module OAuth
class Provider
LABELS = {
- "github" => "GitHub",
- "gitlab" => "GitLab.com",
- "google_oauth2" => "Google",
- "azure_oauth2" => "Azure AD"
+ "github" => "GitHub",
+ "gitlab" => "GitLab.com",
+ "google_oauth2" => "Google",
+ "azure_oauth2" => "Azure AD",
+ 'atlassian_oauth2' => 'Atlassian'
}.freeze
def self.authentication(user, provider)
diff --git a/lib/gitlab/pages/settings.rb b/lib/gitlab/pages/settings.rb
index e3dbeee7b13..47f56072b37 100644
--- a/lib/gitlab/pages/settings.rb
+++ b/lib/gitlab/pages/settings.rb
@@ -7,11 +7,7 @@ module Gitlab
def path
if ::Gitlab::Runtime.web_server? && ENV['GITLAB_PAGES_DENY_DISK_ACCESS'] == '1'
- begin
- raise DiskAccessDenied
- rescue DiskAccessDenied => ex
- ::Gitlab::ErrorTracking.track_exception(ex)
- end
+ raise DiskAccessDenied
end
super
diff --git a/lib/gitlab/project_authorizations.rb b/lib/gitlab/project_authorizations.rb
index ff90a009b2e..23e380b3cf1 100644
--- a/lib/gitlab/project_authorizations.rb
+++ b/lib/gitlab/project_authorizations.rb
@@ -99,6 +99,7 @@ module Gitlab
.and(members[:source_type].eq('Namespace'))
.and(members[:requested_at].eq(nil))
.and(members[:user_id].eq(user.id))
+ .and(members[:access_level].gt(Gitlab::Access::MINIMAL_ACCESS))
Arel::Nodes::OuterJoin.new(members, Arel::Nodes::On.new(cond))
end
@@ -119,6 +120,7 @@ module Gitlab
.and(members[:source_type].eq('Namespace'))
.and(members[:requested_at].eq(nil))
.and(members[:user_id].eq(user.id))
+ .and(members[:access_level].gt(Gitlab::Access::MINIMAL_ACCESS))
Arel::Nodes::InnerJoin.new(members, Arel::Nodes::On.new(cond))
end
diff --git a/lib/gitlab/static_site_editor/config.rb b/lib/gitlab/static_site_editor/config.rb
deleted file mode 100644
index d335a434335..00000000000
--- a/lib/gitlab/static_site_editor/config.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module StaticSiteEditor
- class Config
- SUPPORTED_EXTENSIONS = %w[.md].freeze
-
- def initialize(repository, ref, file_path, return_url)
- @repository = repository
- @ref = ref
- @file_path = file_path
- @return_url = return_url
- @commit_id = repository.commit(ref)&.id if ref
- end
-
- def payload
- {
- branch: ref,
- path: file_path,
- commit_id: commit_id,
- project_id: project.id,
- project: project.path,
- namespace: project.namespace.full_path,
- return_url: sanitize_url(return_url),
- is_supported_content: supported_content?.to_s,
- base_url: Gitlab::Routing.url_helpers.project_show_sse_path(project, full_path)
- }
- end
-
- private
-
- attr_reader :repository, :ref, :file_path, :return_url, :commit_id
-
- delegate :project, to: :repository
-
- def supported_content?
- master_branch? && extension_supported? && file_exists?
- end
-
- def master_branch?
- ref == 'master'
- end
-
- def extension_supported?
- return true if file_path.end_with?('.md.erb') && Feature.enabled?(:sse_erb_support, project)
-
- SUPPORTED_EXTENSIONS.any? { |ext| file_path.end_with?(ext) }
- end
-
- def file_exists?
- commit_id.present? && !repository.blob_at(commit_id, file_path).nil?
- end
-
- def full_path
- "#{ref}/#{file_path}"
- end
-
- def sanitize_url(url)
- url if Gitlab::UrlSanitizer.valid_web?(url)
- end
- end
- end
-end
diff --git a/lib/gitlab/static_site_editor/config/combined_config.rb b/lib/gitlab/static_site_editor/config/combined_config.rb
new file mode 100644
index 00000000000..387ce08e552
--- /dev/null
+++ b/lib/gitlab/static_site_editor/config/combined_config.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module StaticSiteEditor
+ module Config
+ class CombinedConfig
+ def initialize(repository, ref, path, return_url)
+ @repository = repository
+ @ref = ref
+ @path = path
+ @return_url = return_url
+ end
+
+ def data
+ generated_data = Gitlab::StaticSiteEditor::Config::GeneratedConfig.new(
+ @repository,
+ @ref,
+ @path,
+ @return_url
+ ).data
+ file_data = Gitlab::StaticSiteEditor::Config::FileConfig.new.data
+ check_for_duplicate_keys(generated_data, file_data)
+ generated_data.merge(file_data)
+ end
+
+ private
+
+ def check_for_duplicate_keys(generated_data, file_data)
+ duplicate_keys = generated_data.keys & file_data.keys
+ raise StandardError.new("Duplicate key(s) '#{duplicate_keys}' found.") if duplicate_keys.present?
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/static_site_editor/config/file_config.rb b/lib/gitlab/static_site_editor/config/file_config.rb
new file mode 100644
index 00000000000..b43a5440e6b
--- /dev/null
+++ b/lib/gitlab/static_site_editor/config/file_config.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module StaticSiteEditor
+ module Config
+ class FileConfig
+ def data
+ merge_requests_illustration_path = ActionController::Base.helpers.image_path('illustrations/merge_requests.svg')
+ {
+ merge_requests_illustration_path: merge_requests_illustration_path
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/static_site_editor/config/generated_config.rb b/lib/gitlab/static_site_editor/config/generated_config.rb
new file mode 100644
index 00000000000..4c875b1d211
--- /dev/null
+++ b/lib/gitlab/static_site_editor/config/generated_config.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module StaticSiteEditor
+ module Config
+ class GeneratedConfig
+ SUPPORTED_EXTENSIONS = %w[.md].freeze
+
+ def initialize(repository, ref, path, return_url)
+ @repository = repository
+ @ref = ref
+ @path = path
+ @return_url = return_url
+ end
+
+ def data
+ {
+ branch: ref,
+ path: path,
+ commit_id: commit_id,
+ project_id: project.id,
+ project: project.path,
+ namespace: project.namespace.full_path,
+ return_url: sanitize_url(return_url),
+ is_supported_content: supported_content?.to_s,
+ base_url: Gitlab::Routing.url_helpers.project_show_sse_path(project, full_path)
+ }
+ end
+
+ private
+
+ attr_reader :repository, :ref, :path, :return_url
+
+ delegate :project, to: :repository
+
+ def commit_id
+ repository.commit(ref)&.id if ref
+ end
+
+ def supported_content?
+ master_branch? && extension_supported? && file_exists?
+ end
+
+ def master_branch?
+ ref == 'master'
+ end
+
+ def extension_supported?
+ return true if path.end_with?('.md.erb') && Feature.enabled?(:sse_erb_support, project)
+
+ SUPPORTED_EXTENSIONS.any? { |ext| path.end_with?(ext) }
+ end
+
+ def file_exists?
+ commit_id.present? && !repository.blob_at(commit_id, path).nil?
+ end
+
+ def full_path
+ "#{ref}/#{path}"
+ end
+
+ def sanitize_url(url)
+ url if Gitlab::UrlSanitizer.valid_web?(url)
+ end
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a43e40daecf..ce0c7e71203 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1776,21 +1776,51 @@ msgstr ""
msgid "AdminArea|Bots"
msgstr ""
+msgid "AdminArea|Components"
+msgstr ""
+
msgid "AdminArea|Developer"
msgstr ""
+msgid "AdminArea|Features"
+msgstr ""
+
+msgid "AdminArea|Groups: %{number_of_groups}"
+msgstr ""
+
msgid "AdminArea|Guest"
msgstr ""
msgid "AdminArea|Included Free in license"
msgstr ""
+msgid "AdminArea|Latest groups"
+msgstr ""
+
+msgid "AdminArea|Latest projects"
+msgstr ""
+
+msgid "AdminArea|Latest users"
+msgstr ""
+
msgid "AdminArea|Maintainer"
msgstr ""
+msgid "AdminArea|New group"
+msgstr ""
+
+msgid "AdminArea|New project"
+msgstr ""
+
+msgid "AdminArea|New user"
+msgstr ""
+
msgid "AdminArea|Owner"
msgstr ""
+msgid "AdminArea|Projects: %{number_of_projects}"
+msgstr ""
+
msgid "AdminArea|Reporter"
msgstr ""
@@ -1818,6 +1848,9 @@ msgstr ""
msgid "AdminArea|Users without a Group and Project"
msgstr ""
+msgid "AdminArea|Users: %{number_of_users}"
+msgstr ""
+
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
msgstr ""
@@ -10772,7 +10805,7 @@ msgstr ""
msgid "FeatureFlags|Include additional user IDs"
msgstr ""
-msgid "FeatureFlags|Install a %{docs_link_anchored_start}compatible client library%{docs_link_anchored_end} and specify the API URL, application name, and instance ID during the configuration setup. %{docs_link_start}More Information%{docs_link_end}"
+msgid "FeatureFlags|Install a %{docsLinkAnchoredStart}compatible client library%{docsLinkAnchoredEnd} and specify the API URL, application name, and instance ID during the configuration setup. %{docsLinkStart}More Information%{docsLinkEnd}"
msgstr ""
msgid "FeatureFlags|Instance ID"
@@ -11696,9 +11729,15 @@ msgstr ""
msgid "GitHub import"
msgstr ""
+msgid "GitLab"
+msgstr ""
+
msgid "GitLab / Unsubscribe"
msgstr ""
+msgid "GitLab API"
+msgstr ""
+
msgid "GitLab Enterprise Edition %{plan}"
msgstr ""
@@ -11711,12 +11750,18 @@ msgstr ""
msgid "GitLab Issue"
msgstr ""
+msgid "GitLab Pages"
+msgstr ""
+
msgid "GitLab Service Desk is a simple way to allow people to create issues in your GitLab instance without needing their own user account. It provides a unique email address for end users to create issues in a project, and replies can be sent either through the GitLab interface or by email. End users will only see the thread through email."
msgstr ""
msgid "GitLab Shared Runners execute code of different projects on the same Runner unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is on GitLab.com)."
msgstr ""
+msgid "GitLab Shell"
+msgstr ""
+
msgid "GitLab Support Bot"
msgstr ""
@@ -11726,6 +11771,9 @@ msgstr ""
msgid "GitLab User"
msgstr ""
+msgid "GitLab Workhorse"
+msgstr ""
+
msgid "GitLab allows you to continue using your license even if you exceed the number of seats you purchased. You will be required to pay for these seats when you renew your license."
msgstr ""
@@ -12807,6 +12855,15 @@ msgstr ""
msgid "Highest role:"
msgstr ""
+msgid "HighlightBar|Alert events:"
+msgstr ""
+
+msgid "HighlightBar|Alert start time:"
+msgstr ""
+
+msgid "HighlightBar|Original alert:"
+msgstr ""
+
msgid "History"
msgstr ""
@@ -13252,18 +13309,30 @@ msgstr ""
msgid "IncidentManagement|Create incident"
msgstr ""
+msgid "IncidentManagement|Critical - S1"
+msgstr ""
+
msgid "IncidentManagement|Date created"
msgstr ""
msgid "IncidentManagement|Display your incidents in a dedicated view"
msgstr ""
+msgid "IncidentManagement|High - S2"
+msgstr ""
+
msgid "IncidentManagement|Incident"
msgstr ""
msgid "IncidentManagement|Incidents"
msgstr ""
+msgid "IncidentManagement|Low - S4"
+msgstr ""
+
+msgid "IncidentManagement|Medium - S3"
+msgstr ""
+
msgid "IncidentManagement|No incidents to display."
msgstr ""
@@ -13285,6 +13354,9 @@ msgstr ""
msgid "IncidentManagement|Unassigned"
msgstr ""
+msgid "IncidentManagement|Unknown"
+msgstr ""
+
msgid "IncidentManagement|Unpublished"
msgstr ""
@@ -20487,6 +20559,9 @@ msgstr ""
msgid "README"
msgstr ""
+msgid "Rails"
+msgstr ""
+
msgid "Rake Tasks Help"
msgstr ""
@@ -21513,6 +21588,9 @@ msgstr ""
msgid "Rook"
msgstr ""
+msgid "Ruby"
+msgstr ""
+
msgid "Rule name is already taken."
msgstr ""
@@ -22934,6 +23012,15 @@ msgstr ""
msgid "Severity"
msgstr ""
+msgid "SeverityWidget|Severity"
+msgstr ""
+
+msgid "SeverityWidget|Severity: %{severity}"
+msgstr ""
+
+msgid "SeverityWidget|There was an error while updating severity."
+msgstr ""
+
msgid "Shards (%{shards})"
msgstr ""
diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
index f012370f8dd..90b438cacae 100644
--- a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
@@ -22,7 +22,7 @@ module QA
end
end
- it 'publishes a maven package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/943' do
+ it 'publishes a maven package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/943', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/247281', type: :investigating } do
uri = URI.parse(Runtime::Scenario.gitlab_address)
gitlab_address_with_port = "#{uri.scheme}://#{uri.host}:#{uri.port}"
pom_xml = {
diff --git a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
index 3f714b96917..e97ede35610 100644
--- a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
@@ -21,7 +21,7 @@ module QA
end
end
- it 'publishes an npm package and then deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/944' do
+ it 'publishes an npm package and then deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/944', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/247281', type: :investigating } do
uri = URI.parse(Runtime::Scenario.gitlab_address)
gitlab_host_with_port = "#{uri.host}:#{uri.port}"
gitlab_address_with_port = "#{uri.scheme}://#{uri.host}:#{uri.port}"
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/auto_devops_templates_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/auto_devops_templates_spec.rb
index d2408ab6fe2..fe2d821576b 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/auto_devops_templates_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/auto_devops_templates_spec.rb
@@ -45,7 +45,7 @@ module QA
Flow::Login.sign_in
end
- it 'works with Auto DevOps' do
+ it 'works with Auto DevOps', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/240946', type: :flaky } do
%w[build code_quality test].each do |job|
pipeline.visit!
diff --git a/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb b/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb
index a0f613cfda2..8dca0d6698e 100644
--- a/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb
+++ b/qa/qa/specs/features/browser_ui/non_devops/performance_bar_spec.rb
@@ -20,7 +20,7 @@ module QA
end
end
- it 'shows results for the original request and AJAX requests', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/478' do
+ it 'shows results for the original request and AJAX requests', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/478', quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/247467', type: :investigating } do
# Issue pages always make AJAX requests
Resource::Issue.fabricate_via_browser_ui! do |issue|
issue.title = 'Performance bar test'
diff --git a/rubocop/cop/avoid_route_redirect_leading_slash.rb b/rubocop/cop/avoid_route_redirect_leading_slash.rb
index d66e434dc9c..591c003a166 100644
--- a/rubocop/cop/avoid_route_redirect_leading_slash.rb
+++ b/rubocop/cop/avoid_route_redirect_leading_slash.rb
@@ -7,10 +7,10 @@ module RuboCop
#
# @example
# # bad
- # root to: redirect('/-/instance/statistics/dev_ops_score')
+ # root to: redirect('/-/instance_statistics/dev_ops_report')
#
# # good
- # root to: redirect('-/instance/statistics/dev_ops_score')
+ # root to: redirect('-/instance_statistics/dev_ops_report')
#
class AvoidRouteRedirectLeadingSlash < RuboCop::Cop::Cop
diff --git a/spec/controllers/admin/dev_ops_score_controller_spec.rb b/spec/controllers/admin/dev_ops_report_controller_spec.rb
index dec48340eb6..e69f8bf7462 100644
--- a/spec/controllers/admin/dev_ops_score_controller_spec.rb
+++ b/spec/controllers/admin/dev_ops_report_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Admin::DevOpsScoreController do
+RSpec.describe Admin::DevOpsReportController do
describe 'GET #show' do
context 'as admin' do
let(:user) { create(:admin) }
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index 3f7f0c55f38..de6edbe936d 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -276,6 +276,51 @@ RSpec.describe OmniauthCallbacksController, type: :controller do
end
end
+ context 'atlassian_oauth2' do
+ let(:provider) { :atlassian_oauth2 }
+ let(:extern_uid) { 'my-uid' }
+
+ context 'when the user and identity already exist' do
+ let(:user) { create(:atlassian_user, extern_uid: extern_uid) }
+
+ it 'allows sign-in' do
+ post :atlassian_oauth2
+
+ expect(request.env['warden']).to be_authenticated
+ end
+ end
+
+ context 'for a new user' do
+ before do
+ stub_omniauth_setting(enabled: true, auto_link_user: true, allow_single_sign_on: ['atlassian_oauth2'])
+
+ user.destroy
+ end
+
+ it 'denies sign-in if sign-up is enabled, but block_auto_created_users is set' do
+ post :atlassian_oauth2
+
+ expect(flash[:alert]).to start_with 'Your account has been blocked.'
+ end
+
+ it 'accepts sign-in if sign-up is enabled' do
+ stub_omniauth_setting(block_auto_created_users: false)
+
+ post :atlassian_oauth2
+
+ expect(request.env['warden']).to be_authenticated
+ end
+
+ it 'denies sign-in if sign-up is not enabled' do
+ stub_omniauth_setting(allow_single_sign_on: false, block_auto_created_users: false)
+
+ post :atlassian_oauth2
+
+ expect(flash[:alert]).to start_with 'Signing in using your Atlassian account without a pre-existing GitLab account is not allowed.'
+ end
+ end
+ end
+
context 'salesforce' do
let(:extern_uid) { 'my-uid' }
let(:provider) { :salesforce }
diff --git a/spec/controllers/profiles/accounts_controller_spec.rb b/spec/controllers/profiles/accounts_controller_spec.rb
index 52a7a1609a1..c6e7866a659 100644
--- a/spec/controllers/profiles/accounts_controller_spec.rb
+++ b/spec/controllers/profiles/accounts_controller_spec.rb
@@ -45,5 +45,18 @@ RSpec.describe Profiles::AccountsController do
end
end
end
+
+ describe 'atlassian_oauth2 provider' do
+ let(:user) { create(:atlassian_user) }
+
+ it 'allows a user to unlink a connected account' do
+ expect(user.atlassian_identity).not_to be_nil
+
+ delete :unlink, params: { provider: 'atlassian_oauth2' }
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(user.reload.atlassian_identity).to be_nil
+ end
+ end
end
end
diff --git a/spec/controllers/projects/static_site_editor_controller_spec.rb b/spec/controllers/projects/static_site_editor_controller_spec.rb
index 384218504b9..1156887b2b6 100644
--- a/spec/controllers/projects/static_site_editor_controller_spec.rb
+++ b/spec/controllers/projects/static_site_editor_controller_spec.rb
@@ -5,6 +5,13 @@ require 'spec_helper'
RSpec.describe Projects::StaticSiteEditorController do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:user) { create(:user) }
+ let(:data) { instance_double(Hash) }
+
+ before do
+ allow_next_instance_of(Gitlab::StaticSiteEditor::Config::CombinedConfig) do |config|
+ allow(config).to receive(:data) { data }
+ end
+ end
describe 'GET show' do
let(:default_params) do
@@ -55,12 +62,12 @@ RSpec.describe Projects::StaticSiteEditorController do
end
it 'assigns a required variables' do
- expect(assigns(:config)).to be_a(Gitlab::StaticSiteEditor::Config)
+ expect(assigns(:data)).to eq(data)
expect(assigns(:ref)).to eq('master')
expect(assigns(:path)).to eq('README.md')
end
- context 'when combination of ref and file path is incorrect' do
+ context 'when combination of ref and path is incorrect' do
let(:default_params) { super().merge(id: 'unknown') }
it 'responds with 404 page' do
diff --git a/spec/factories/dev_ops_score_metrics.rb b/spec/factories/dev_ops_report_metrics.rb
index 1d1f1a2c39e..808c70c2499 100644
--- a/spec/factories/dev_ops_score_metrics.rb
+++ b/spec/factories/dev_ops_report_metrics.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
FactoryBot.define do
- factory :dev_ops_score_metric, class: 'DevOpsScore::Metric' do
+ factory :dev_ops_report_metric, class: 'DevOpsReport::Metric' do
leader_issues { 9.256 }
instance_issues { 1.234 }
percentage_issues { 13.331 }
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index a6706ed4145..37ddbc09616 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -28,5 +28,11 @@ FactoryBot.define do
trait :blocked do
after(:build) { |group_member, _| group_member.user.block! }
end
+
+ trait :minimal_access do
+ to_create { |instance| instance.save!(validate: false) }
+
+ access_level { GroupMember::MINIMAL_ACCESS }
+ end
end
end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index d4b2ac5f056..1a8c5d7e40c 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -136,6 +136,16 @@ FactoryBot.define do
end
end
+ factory :atlassian_user do
+ transient do
+ extern_uid { generate(:username) }
+ end
+
+ after(:create) do |user, evaluator|
+ create(:atlassian_identity, user: user, extern_uid: evaluator.extern_uid)
+ end
+ end
+
factory :admin, traits: [:admin]
end
end
diff --git a/spec/features/admin/admin_dev_ops_score_spec.rb b/spec/features/admin/admin_dev_ops_report_spec.rb
index 1d3e7deef65..c201011cbea 100644
--- a/spec/features/admin/admin_dev_ops_score_spec.rb
+++ b/spec/features/admin/admin_dev_ops_report_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'DevOps Report page' do
end
it 'has dismissable intro callout', :js do
- visit admin_dev_ops_score_path
+ visit admin_dev_ops_report_path
expect(page).to have_content 'Introducing Your DevOps Report'
@@ -23,13 +23,13 @@ RSpec.describe 'DevOps Report page' do
end
it 'shows empty state', :js do
- visit admin_dev_ops_score_path
+ visit admin_dev_ops_report_path
expect(page).to have_selector(".js-empty-state")
end
it 'hides the intro callout' do
- visit admin_dev_ops_score_path
+ visit admin_dev_ops_report_path
expect(page).not_to have_content 'Introducing Your DevOps Report'
end
@@ -39,7 +39,7 @@ RSpec.describe 'DevOps Report page' do
it 'shows empty state' do
stub_application_setting(usage_ping_enabled: true)
- visit admin_dev_ops_score_path
+ visit admin_dev_ops_report_path
expect(page).to have_content('Data is still calculating')
end
@@ -48,9 +48,9 @@ RSpec.describe 'DevOps Report page' do
context 'when there is data to display' do
it 'shows numbers for each metric' do
stub_application_setting(usage_ping_enabled: true)
- create(:dev_ops_score_metric)
+ create(:dev_ops_report_metric)
- visit admin_dev_ops_score_path
+ visit admin_dev_ops_report_path
expect(page).to have_content(
'Issues created per active user 1.2 You 9.3 Lead 13.3%'
diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb
index 68b120db227..67e7de5921a 100644
--- a/spec/finders/group_members_finder_spec.rb
+++ b/spec/finders/group_members_finder_spec.rb
@@ -16,6 +16,7 @@ RSpec.describe GroupMembersFinder, '#execute' do
member1 = group.add_maintainer(user1)
member2 = group.add_maintainer(user2)
member3 = group.add_maintainer(user3)
+ create(:group_member, :minimal_access, user: create(:user), source: group)
result = described_class.new(group).execute
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
index 78764f79a6c..48e4c5dadc9 100644
--- a/spec/finders/groups_finder_spec.rb
+++ b/spec/finders/groups_finder_spec.rb
@@ -150,6 +150,14 @@ RSpec.describe GroupsFinder do
end
end
end
+
+ context 'being minimal access member of parent group' do
+ it 'do not return group with minimal_access access' do
+ create(:group_member, :minimal_access, user: user, source: parent_group)
+
+ is_expected.to contain_exactly(public_subgroup, internal_subgroup)
+ end
+ end
end
end
end
diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb
index 3ef8d6a01aa..3530858e2de 100644
--- a/spec/finders/members_finder_spec.rb
+++ b/spec/finders/members_finder_spec.rb
@@ -45,6 +45,18 @@ RSpec.describe MembersFinder, '#execute' do
expect(result).to contain_exactly(member1)
end
+ it 'does not return members of parent group with minimal access' do
+ nested_group.request_access(user1)
+ member1 = group.add_maintainer(user2)
+ member2 = nested_group.add_maintainer(user3)
+ member3 = project.add_maintainer(user4)
+ create(:group_member, :minimal_access, user: create(:user), source: group)
+
+ result = described_class.new(project, user2).execute
+
+ expect(result).to contain_exactly(member1, member2, member3)
+ end
+
it 'includes only non-invite members if user do not have amdin permissions on project' do
create(:project_member, :invited, project: project, invite_email: create(:user).email)
member1 = project.add_maintainer(user1)
diff --git a/spec/fixtures/api/schemas/entities/issue_sidebar.json b/spec/fixtures/api/schemas/entities/issue_sidebar.json
index b2c8244ce69..717eb4992ea 100644
--- a/spec/fixtures/api/schemas/entities/issue_sidebar.json
+++ b/spec/fixtures/api/schemas/entities/issue_sidebar.json
@@ -44,6 +44,7 @@
"move_issue_path": { "type": "string" },
"projects_autocomplete_path": { "type": "string" },
"supports_time_tracking": { "type": "boolean" },
- "supports_milestone": { "type": "boolean" }
+ "supports_milestone": { "type": "boolean" },
+ "supports_severity": { "type": "boolean" }
}
}
diff --git a/spec/frontend/issue_show/components/app_spec.js b/spec/frontend/issue_show/components/app_spec.js
index 69f1d5aeec2..c5e94aa3376 100644
--- a/spec/frontend/issue_show/components/app_spec.js
+++ b/spec/frontend/issue_show/components/app_spec.js
@@ -14,7 +14,7 @@ import {
secondRequest,
zoomMeetingUrl,
} from '../mock_data';
-import IncidentTabs from '~/issue_show/components/incident_tabs.vue';
+import IncidentTabs from '~/issue_show/components/incidents/incident_tabs.vue';
import DescriptionComponent from '~/issue_show/components/description.vue';
import PinnedLinks from '~/issue_show/components/pinned_links.vue';
@@ -39,6 +39,13 @@ describe('Issuable output', () => {
const mountComponent = (props = {}) => {
wrapper = mount(IssuableApp, {
propsData: { ...appProps, ...props },
+ provide: {
+ fullPath: 'gitlab-org/incidents',
+ iid: '19',
+ },
+ stubs: {
+ HighlightBar: true,
+ },
});
};
diff --git a/spec/frontend/issue_show/components/highlight_bar_spec.js b/spec/frontend/issue_show/components/highlight_bar_spec.js
new file mode 100644
index 00000000000..8a0b3a997e1
--- /dev/null
+++ b/spec/frontend/issue_show/components/highlight_bar_spec.js
@@ -0,0 +1,60 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlLink } from '@gitlab/ui';
+import HighlightBar from '~/issue_show/components/incidents/highlight_bar/higlight_bar.vue';
+import { formatDate } from '~/lib/utils/datetime_utility';
+
+jest.mock('~/lib/utils/datetime_utility');
+
+describe('Highlight Bar', () => {
+ let wrapper;
+
+ const alert = {
+ createdAt: '2020-05-29T10:39:22Z',
+ detailsUrl: 'http://127.0.0.1:3000/root/unique-alerts/-/alert_management/1/details',
+ eventCount: 1,
+ title: 'Alert 1',
+ };
+
+ const mountComponent = () => {
+ wrapper = shallowMount(HighlightBar, {
+ provide: {
+ fullPath: 'project/id',
+ iid: '1',
+ },
+ data() {
+ return { alert };
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ const findLink = () => wrapper.find(GlLink);
+
+ it('renders a link to the alert page', () => {
+ expect(findLink().exists()).toBe(true);
+ expect(findLink().attributes('href')).toBe(alert.detailsUrl);
+ expect(findLink().text()).toContain(alert.title);
+ });
+
+ it('renders formatted start time of the alert', () => {
+ const formattedDate = '2020-05-29 UTC';
+ formatDate.mockReturnValueOnce(formattedDate);
+ mountComponent();
+ expect(formatDate).toHaveBeenCalledWith(alert.createdAt, 'yyyy-mm-dd Z');
+ expect(wrapper.text()).toContain(formattedDate);
+ });
+
+ it('renders a number of alert events', () => {
+ expect(wrapper.text()).toContain(alert.eventCount);
+ });
+});
diff --git a/spec/frontend/issue_show/components/incident_tabs_spec.js b/spec/frontend/issue_show/components/incident_tabs_spec.js
index 19f75345450..0186b01b093 100644
--- a/spec/frontend/issue_show/components/incident_tabs_spec.js
+++ b/spec/frontend/issue_show/components/incident_tabs_spec.js
@@ -1,8 +1,9 @@
import { shallowMount } from '@vue/test-utils';
import { GlTab } from '@gitlab/ui';
-import IncidentTabs from '~/issue_show/components/incident_tabs.vue';
+import IncidentTabs from '~/issue_show/components/incidents/incident_tabs.vue';
import { descriptionProps } from '../mock_data';
import DescriptionComponent from '~/issue_show/components/description.vue';
+import HighlightBar from '~/issue_show/components/incidents/highlight_bar/higlight_bar.vue';
describe('Incident Tabs component', () => {
let wrapper;
@@ -25,6 +26,7 @@ describe('Incident Tabs component', () => {
const findTabs = () => wrapper.findAll(GlTab);
const findSummaryTab = () => findTabs().at(0);
const findDescriptionComponent = () => wrapper.find(DescriptionComponent);
+ const findHighlightBarComponent = () => wrapper.find(HighlightBar);
describe('default state', () => {
it('renders the summary tab', async () => {
@@ -33,8 +35,9 @@ describe('Incident Tabs component', () => {
expect(findSummaryTab().attributes('title')).toBe('Summary');
});
- it('renders the description component', () => {
+ it('renders the description component with highlight bar', () => {
expect(findDescriptionComponent().exists()).toBe(true);
+ expect(findHighlightBarComponent().exists()).toBe(true);
});
it('passes all props to the description component', () => {
diff --git a/spec/frontend/sidebar/components/severity/severity_spec.js b/spec/frontend/sidebar/components/severity/severity_spec.js
new file mode 100644
index 00000000000..b6690f11d6b
--- /dev/null
+++ b/spec/frontend/sidebar/components/severity/severity_spec.js
@@ -0,0 +1,57 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlIcon } from '@gitlab/ui';
+import SeverityToken from '~/sidebar/components/severity/severity.vue';
+import { INCIDENT_SEVERITY } from '~/sidebar/components/severity/constants';
+
+describe('SeverityToken', () => {
+ let wrapper;
+
+ function createComponent(props) {
+ wrapper = shallowMount(SeverityToken, {
+ propsData: {
+ ...props,
+ },
+ });
+ }
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ const findIcon = () => wrapper.find(GlIcon);
+
+ it('renders severity token for each severity type', () => {
+ Object.values(INCIDENT_SEVERITY).forEach(severity => {
+ createComponent({ severity });
+ expect(findIcon().classes()).toContain(`icon-${severity.icon}`);
+ expect(findIcon().attributes('name')).toBe(`severity-${severity.icon}`);
+ expect(wrapper.text()).toBe(severity.label);
+ });
+ });
+
+ it('renders only icon when `iconOnly` prop is set to `true`', () => {
+ const severity = INCIDENT_SEVERITY.CRITICAL;
+ createComponent({ severity, iconOnly: true });
+ expect(findIcon().classes()).toContain(`icon-${severity.icon}`);
+ expect(findIcon().attributes('name')).toBe(`severity-${severity.icon}`);
+ expect(wrapper.text()).toBe('');
+ });
+
+ describe('icon size', () => {
+ it('renders the icon in default size when other is not specified', () => {
+ const severity = INCIDENT_SEVERITY.HIGH;
+ createComponent({ severity });
+ expect(findIcon().attributes('size')).toBe('12');
+ });
+
+ it('renders the icon in provided size', () => {
+ const severity = INCIDENT_SEVERITY.HIGH;
+ const iconSize = 14;
+ createComponent({ severity, iconSize });
+ expect(findIcon().attributes('size')).toBe(`${iconSize}`);
+ });
+ });
+});
diff --git a/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js b/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js
new file mode 100644
index 00000000000..638d3706d12
--- /dev/null
+++ b/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js
@@ -0,0 +1,166 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlDropdown, GlDropdownItem, GlLoadingIcon, GlTooltip, GlSprintf } from '@gitlab/ui';
+import waitForPromises from 'helpers/wait_for_promises';
+import createFlash from '~/flash';
+import SidebarSeverity from '~/sidebar/components/severity/sidebar_severity.vue';
+import SeverityToken from '~/sidebar/components/severity/severity.vue';
+import updateIssuableSeverity from '~/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql';
+import { INCIDENT_SEVERITY, ISSUABLE_TYPES } from '~/sidebar/components/severity/constants';
+
+jest.mock('~/flash');
+
+describe('SidebarSeverity', () => {
+ let wrapper;
+ let mutate;
+ const projectPath = 'gitlab-org/gitlab-test';
+ const iid = '1';
+ const severity = 'CRITICAL';
+
+ function createComponent(props = {}) {
+ const propsData = {
+ projectPath,
+ iid,
+ issuableType: ISSUABLE_TYPES.INCIDENT,
+ initialSeverity: severity,
+ ...props,
+ };
+ mutate = jest.fn();
+ wrapper = shallowMount(SidebarSeverity, {
+ propsData,
+ mocks: {
+ $apollo: {
+ mutate,
+ },
+ },
+ stubs: {
+ GlSprintf,
+ },
+ });
+ }
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ const findSeverityToken = () => wrapper.findAll(SeverityToken);
+ const findEditBtn = () => wrapper.find('[data-testid="editButton"]');
+ const findDropdown = () => wrapper.find(GlDropdown);
+ const findCriticalSeverityDropdownItem = () => wrapper.find(GlDropdownItem);
+ const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findTooltip = () => wrapper.find(GlTooltip);
+ const findCollapsedSeverity = () => wrapper.find({ ref: 'severity' });
+
+ it('renders severity widget', () => {
+ expect(findEditBtn().exists()).toBe(true);
+ expect(findSeverityToken().exists()).toBe(true);
+ expect(findDropdown().exists()).toBe(true);
+ });
+
+ describe('Update severity', () => {
+ it('calls `$apollo.mutate` with `updateIssuableSeverity`', () => {
+ jest
+ .spyOn(wrapper.vm.$apollo, 'mutate')
+ .mockResolvedValueOnce({ data: { issueSetSeverity: { issue: { severity } } } });
+
+ findCriticalSeverityDropdownItem().vm.$emit('click');
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation: updateIssuableSeverity,
+ variables: {
+ iid,
+ projectPath,
+ severity,
+ },
+ });
+ });
+
+ it('shows error alert when severity update fails ', () => {
+ const errorMsg = 'Something went wrong';
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValueOnce(errorMsg);
+ findCriticalSeverityDropdownItem().vm.$emit('click');
+
+ setImmediate(() => {
+ expect(createFlash).toHaveBeenCalled();
+ });
+ });
+
+ it('shows loading icon while updating', async () => {
+ let resolvePromise;
+ wrapper.vm.$apollo.mutate = jest.fn(
+ () =>
+ new Promise(resolve => {
+ resolvePromise = resolve;
+ }),
+ );
+ findCriticalSeverityDropdownItem().vm.$emit('click');
+
+ await wrapper.vm.$nextTick();
+ expect(findLoadingIcon().exists()).toBe(true);
+
+ resolvePromise();
+ await waitForPromises();
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+ });
+
+ describe('Switch between collapsed/expanded view of the sidebar', () => {
+ const HIDDDEN_CLASS = 'gl-display-none';
+ const SHOWN_CLASS = 'show';
+
+ describe('collapsed', () => {
+ it('should have collapsed icon class', () => {
+ expect(findCollapsedSeverity().classes('sidebar-collapsed-icon')).toBe(true);
+ });
+
+ it('should display only icon with a tooltip', () => {
+ expect(
+ findSeverityToken()
+ .at(0)
+ .attributes('icononly'),
+ ).toBe('true');
+ expect(
+ findSeverityToken()
+ .at(0)
+ .attributes('iconsize'),
+ ).toBe('14');
+ expect(
+ findTooltip()
+ .text()
+ .replace(/\s+/g, ' '),
+ ).toContain(`Severity: ${INCIDENT_SEVERITY[severity].label}`);
+ });
+
+ it('should expand the dropdown on collapsed icon click', async () => {
+ wrapper.vm.isDropdownShowing = false;
+ await wrapper.vm.$nextTick();
+ expect(findDropdown().classes(HIDDDEN_CLASS)).toBe(true);
+
+ findCollapsedSeverity().trigger('click');
+ await wrapper.vm.$nextTick();
+ expect(findDropdown().classes(SHOWN_CLASS)).toBe(true);
+ });
+ });
+
+ describe('expanded', () => {
+ it('toggles dropdown with edit button', async () => {
+ wrapper.vm.isDropdownShowing = false;
+ await wrapper.vm.$nextTick();
+ expect(findDropdown().classes(HIDDDEN_CLASS)).toBe(true);
+
+ findEditBtn().vm.$emit('click');
+ await wrapper.vm.$nextTick();
+ expect(findDropdown().classes(SHOWN_CLASS)).toBe(true);
+
+ findEditBtn().vm.$emit('click');
+ await wrapper.vm.$nextTick();
+ expect(findDropdown().classes(HIDDDEN_CLASS)).toBe(true);
+ });
+ });
+ });
+});
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index 1e843ee221b..e0d316baa17 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -220,4 +220,44 @@ RSpec.describe AuthHelper do
it { is_expected.to be(false) }
end
end
+
+ describe '#auth_active?' do
+ let(:user) { create(:user) }
+
+ def auth_active?
+ helper.auth_active?(provider)
+ end
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ context 'for atlassian_oauth2 provider' do
+ let_it_be(:provider) { :atlassian_oauth2 }
+
+ it 'returns true when present' do
+ create(:atlassian_identity, user: user)
+
+ expect(auth_active?).to be true
+ end
+
+ it 'returns false when not present' do
+ expect(auth_active?).to be false
+ end
+ end
+
+ context 'for other omniauth providers' do
+ let_it_be(:provider) { 'google_oauth2' }
+
+ it 'returns true when present' do
+ create(:identity, provider: provider, user: user)
+
+ expect(auth_active?).to be true
+ end
+
+ it 'returns false when not present' do
+ expect(auth_active?).to be false
+ end
+ end
+ end
end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 8e83bd0ee9d..1f47ec4bf97 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -198,7 +198,7 @@ RSpec.describe IssuablesHelper do
initialDescriptionHtml: '<p dir="auto">issue text</p>',
initialDescriptionText: 'issue text',
initialTaskStatus: '0 of 0 tasks completed',
- issueType: 'issue'
+ iid: issue.iid.to_s
}
expect(helper.issuable_initial_data(issue)).to match(hash_including(expected_data))
end
diff --git a/spec/lib/gitlab/auth/atlassian/auth_hash_spec.rb b/spec/lib/gitlab/auth/atlassian/auth_hash_spec.rb
new file mode 100644
index 00000000000..c57b15361c4
--- /dev/null
+++ b/spec/lib/gitlab/auth/atlassian/auth_hash_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Auth::Atlassian::AuthHash do
+ let(:auth_hash) do
+ described_class.new(
+ OmniAuth::AuthHash.new(uid: 'john', credentials: credentials)
+ )
+ end
+
+ let(:credentials) do
+ {
+ token: 'super_secret_token',
+ refresh_token: 'super_secret_refresh_token',
+ expires_at: 2.weeks.from_now.to_i,
+ expires: true
+ }
+ end
+
+ describe '#uid' do
+ it 'returns the correct uid' do
+ expect(auth_hash.uid).to eq('john')
+ end
+ end
+
+ describe '#token' do
+ it 'returns the correct token' do
+ expect(auth_hash.token).to eq(credentials[:token])
+ end
+ end
+
+ describe '#refresh_token' do
+ it 'returns the correct refresh token' do
+ expect(auth_hash.refresh_token).to eq(credentials[:refresh_token])
+ end
+ end
+
+ describe '#token' do
+ it 'returns the correct expires boolean' do
+ expect(auth_hash.expires?).to eq(credentials[:expires])
+ end
+ end
+
+ describe '#token' do
+ it 'returns the correct expiration' do
+ expect(auth_hash.expires_at).to eq(credentials[:expires_at])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/auth/atlassian/identity_linker_spec.rb b/spec/lib/gitlab/auth/atlassian/identity_linker_spec.rb
new file mode 100644
index 00000000000..ca6b91ac6f1
--- /dev/null
+++ b/spec/lib/gitlab/auth/atlassian/identity_linker_spec.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Auth::Atlassian::IdentityLinker do
+ let(:user) { create(:user) }
+ let(:extern_uid) { generate(:username) }
+ let(:oauth) do
+ OmniAuth::AuthHash.new(
+ uid: extern_uid,
+ provider: 'atlassian_oauth2',
+ info: { name: 'John', email: 'john@mail.com' },
+ credentials: credentials
+ )
+ end
+
+ let(:credentials) do
+ {
+ token: SecureRandom.alphanumeric(1254),
+ refresh_token: SecureRandom.alphanumeric(45),
+ expires_at: 2.weeks.from_now.to_i,
+ expires: true
+ }
+ end
+
+ subject { described_class.new(user, oauth) }
+
+ context 'linked identity exists' do
+ let!(:identity) { create(:atlassian_identity, user: user, extern_uid: extern_uid) }
+
+ before do
+ subject.link
+ end
+
+ it 'sets #changed? to false' do
+ expect(subject).not_to be_changed
+ end
+
+ it 'does not mark as failed' do
+ expect(subject).not_to be_failed
+ end
+ end
+
+ context 'identity already linked to different user' do
+ let!(:identity) { create(:atlassian_identity, extern_uid: extern_uid) }
+
+ it 'sets #changed? to false' do
+ subject.link
+
+ expect(subject).not_to be_changed
+ end
+
+ it 'exposes error message' do
+ expect(subject.error_message).to eq 'Extern uid has already been taken'
+ end
+ end
+
+ context 'identity needs to be created' do
+ let(:identity) { user.atlassian_identity }
+
+ before do
+ subject.link
+ end
+
+ it_behaves_like 'an atlassian identity'
+
+ it 'sets #changed? to true' do
+ expect(subject).to be_changed
+ end
+ end
+end
diff --git a/spec/lib/gitlab/auth/atlassian/user_spec.rb b/spec/lib/gitlab/auth/atlassian/user_spec.rb
new file mode 100644
index 00000000000..1db01102bc2
--- /dev/null
+++ b/spec/lib/gitlab/auth/atlassian/user_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Auth::Atlassian::User do
+ let(:oauth_user) { described_class.new(oauth) }
+ let(:gl_user) { oauth_user.gl_user }
+ let(:extern_uid) { generate(:username) }
+ let(:oauth) do
+ OmniAuth::AuthHash.new(
+ uid: extern_uid,
+ provider: 'atlassian_oauth2',
+ info: { name: 'John', email: 'john@mail.com' },
+ credentials: credentials)
+ end
+
+ let(:credentials) do
+ {
+ token: SecureRandom.alphanumeric(1254),
+ refresh_token: SecureRandom.alphanumeric(45),
+ expires_at: 2.weeks.from_now.to_i,
+ expires: true
+ }
+ end
+
+ describe '.assign_identity_from_auth_hash!' do
+ let(:auth_hash) { ::Gitlab::Auth::Atlassian::AuthHash.new(oauth) }
+ let(:identity) { described_class.assign_identity_from_auth_hash!(Atlassian::Identity.new, auth_hash) }
+
+ it_behaves_like 'an atlassian identity'
+ end
+
+ describe '#save' do
+ context 'for an existing user' do
+ context 'with an existing Atlassian Identity' do
+ let!(:existing_user) { create(:atlassian_user, extern_uid: extern_uid) }
+ let(:identity) { gl_user.atlassian_identity }
+
+ before do
+ oauth_user.save # rubocop:disable Rails/SaveBang
+ end
+
+ it 'finds the existing user and identity' do
+ expect(gl_user.id).to eq(existing_user.id)
+ expect(identity.id).to eq(existing_user.atlassian_identity.id)
+ end
+
+ it_behaves_like 'an atlassian identity'
+ end
+
+ context 'for a new user' do
+ it 'creates the user and identity' do
+ oauth_user.save # rubocop:disable Rails/SaveBang
+
+ expect(gl_user).to be_valid
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/pages/settings_spec.rb b/spec/lib/gitlab/pages/settings_spec.rb
index 7d4db073d73..e495c004647 100644
--- a/spec/lib/gitlab/pages/settings_spec.rb
+++ b/spec/lib/gitlab/pages/settings_spec.rb
@@ -10,12 +10,6 @@ RSpec.describe Gitlab::Pages::Settings do
it { is_expected.to eq('the path') }
- it 'does not track calls' do
- expect(::Gitlab::ErrorTracking).not_to receive(:track_exception)
-
- subject
- end
-
context 'when running under a web server' do
before do
allow(::Gitlab::Runtime).to receive(:web_server?).and_return(true)
@@ -23,24 +17,13 @@ RSpec.describe Gitlab::Pages::Settings do
it { is_expected.to eq('the path') }
- it 'does not track calls' do
- expect(::Gitlab::ErrorTracking).not_to receive(:track_exception)
-
- subject
- end
-
context 'with the env var' do
before do
stub_env('GITLAB_PAGES_DENY_DISK_ACCESS', '1')
end
- it { is_expected.to eq('the path') }
-
- it 'tracks a DiskAccessDenied exception' do
- expect(::Gitlab::ErrorTracking).to receive(:track_exception)
- .with(instance_of(described_class::DiskAccessDenied)).and_call_original
-
- subject
+ it 'raises a DiskAccessDenied exception' do
+ expect { subject }.to raise_error(described_class::DiskAccessDenied)
end
end
end
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index 5ff07dcec4f..d2b41ee31d9 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -115,6 +115,66 @@ RSpec.describe Gitlab::ProjectAuthorizations do
end
end
+ context 'user with minimal access to group' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+
+ subject(:mapping) { map_access_levels(authorizations) }
+
+ context 'group membership' do
+ let!(:group_project) { create(:project, namespace: group) }
+
+ before do
+ create(:group_member, :minimal_access, user: user, source: group)
+ end
+
+ it 'does not create authorization' do
+ expect(mapping[group_project.id]).to be_nil
+ end
+ end
+
+ context 'inherited group membership' do
+ let!(:sub_group) { create(:group, parent: group) }
+ let!(:sub_group_project) { create(:project, namespace: sub_group) }
+
+ before do
+ create(:group_member, :minimal_access, user: user, source: group)
+ end
+
+ it 'does not create authorization' do
+ expect(mapping[sub_group_project.id]).to be_nil
+ end
+ end
+
+ context 'shared group' do
+ let!(:shared_group) { create(:group) }
+ let!(:shared_group_project) { create(:project, namespace: shared_group) }
+
+ before do
+ create(:group_group_link, shared_group: shared_group, shared_with_group: group)
+ create(:group_member, :minimal_access, user: user, source: group)
+ end
+
+ it 'does not create authorization' do
+ expect(mapping[shared_group_project.id]).to be_nil
+ end
+ end
+
+ context 'shared project' do
+ let!(:another_group) { create(:group) }
+ let!(:shared_project) { create(:project, namespace: another_group) }
+
+ before do
+ create(:project_group_link, group: group, project: shared_project)
+ create(:group_member, :minimal_access, user: user, source: group)
+ end
+
+ it 'does not create authorization' do
+ expect(mapping[shared_project.id]).to be_nil
+ end
+ end
+ end
+
context 'with nested groups' do
let(:group) { create(:group) }
let!(:nested_group) { create(:group, parent: group) }
diff --git a/spec/lib/gitlab/static_site_editor/config/combined_config_spec.rb b/spec/lib/gitlab/static_site_editor/config/combined_config_spec.rb
new file mode 100644
index 00000000000..9a31728648b
--- /dev/null
+++ b/spec/lib/gitlab/static_site_editor/config/combined_config_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::StaticSiteEditor::Config::CombinedConfig do
+ subject(:config) { described_class.new(repository, ref, path, return_url) }
+
+ let(:repository) { double(:repository) }
+ let(:ref) { double(:ref) }
+ let(:path) { double(:path) }
+ let(:return_url) { double(:return_url) }
+ let(:generated_data) { { generated: true } }
+ let(:file_data) { { file: true } }
+
+ describe '#data' do
+ subject { config.data }
+
+ before do
+ allow_next_instance_of(Gitlab::StaticSiteEditor::Config::GeneratedConfig) do |config|
+ allow(config).to receive(:data) { generated_data }
+ end
+ allow_next_instance_of(Gitlab::StaticSiteEditor::Config::FileConfig) do |config|
+ allow(config).to receive(:data) { file_data }
+ end
+ end
+
+ it 'returns merged generated data and config file data' do
+ is_expected.to eq({ generated: true, file: true })
+ end
+
+ it 'raises an exception if any keys would be overwritten by the merge' do
+ generated_data[:duplicate_key] = true
+ file_data[:duplicate_key] = true
+ expect { subject }.to raise_error(StandardError, /duplicate key.*duplicate_key.*found/i)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/static_site_editor/config/file_config_spec.rb b/spec/lib/gitlab/static_site_editor/config/file_config_spec.rb
new file mode 100644
index 00000000000..665e2651d65
--- /dev/null
+++ b/spec/lib/gitlab/static_site_editor/config/file_config_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig do
+ subject(:config) { described_class.new }
+
+ describe '#data' do
+ subject { config.data }
+
+ it 'returns hardcoded data for now' do
+ is_expected.to match(
+ merge_requests_illustration_path: %r{illustrations/merge_requests}
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/static_site_editor/config_spec.rb b/spec/lib/gitlab/static_site_editor/config/generated_config_spec.rb
index 56cdb573785..44e0c9249f5 100644
--- a/spec/lib/gitlab/static_site_editor/config_spec.rb
+++ b/spec/lib/gitlab/static_site_editor/config/generated_config_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe Gitlab::StaticSiteEditor::Config do
- subject(:config) { described_class.new(repository, ref, file_path, return_url) }
+RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do
+ subject(:config) { described_class.new(repository, ref, path, return_url) }
let_it_be(:namespace) { create(:namespace, name: 'namespace') }
let_it_be(:root_group) { create(:group, name: 'group') }
@@ -13,11 +13,11 @@ RSpec.describe Gitlab::StaticSiteEditor::Config do
let_it_be(:repository) { project.repository }
let(:ref) { 'master' }
- let(:file_path) { 'README.md' }
+ let(:path) { 'README.md' }
let(:return_url) { 'http://example.com' }
- describe '#payload' do
- subject { config.payload }
+ describe '#data' do
+ subject { config.data }
it 'returns data for the frontend component' do
is_expected.to eq(
@@ -49,7 +49,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config do
before do
repository.create_file(
project.creator,
- file_path,
+ path,
'',
message: 'message',
branch_name: 'master'
@@ -57,7 +57,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config do
end
context 'when feature flag is enabled' do
- let(:file_path) { 'FEATURE_ON.md.erb' }
+ let(:path) { 'FEATURE_ON.md.erb' }
before do
stub_feature_flags(sse_erb_support: project)
@@ -67,7 +67,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config do
end
context 'when feature flag is disabled' do
- let(:file_path) { 'FEATURE_OFF.md.erb' }
+ let(:path) { 'FEATURE_OFF.md.erb' }
before do
stub_feature_flags(sse_erb_support: false)
@@ -78,7 +78,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config do
end
context 'when file path is nested' do
- let(:file_path) { 'lib/README.md' }
+ let(:path) { 'lib/README.md' }
it { is_expected.to include(base_url: '/namespace/project/-/sse/master%2Flib%2FREADME.md') }
end
@@ -90,19 +90,19 @@ RSpec.describe Gitlab::StaticSiteEditor::Config do
end
context 'when file does not have a markdown extension' do
- let(:file_path) { 'README.txt' }
+ let(:path) { 'README.txt' }
it { is_expected.to include(is_supported_content: 'false') }
end
context 'when file does not have an extension' do
- let(:file_path) { 'README' }
+ let(:path) { 'README' }
it { is_expected.to include(is_supported_content: 'false') }
end
context 'when file does not exist' do
- let(:file_path) { 'UNKNOWN.md' }
+ let(:path) { 'UNKNOWN.md' }
it { is_expected.to include(is_supported_content: 'false') }
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index ff574a2e2f4..44561e2e55a 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -837,6 +837,24 @@ RSpec.describe Issuable do
end
end
+ describe '#supports_severity?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:issuable_type, :supports_severity) do
+ :issue | false
+ :incident | true
+ :merge_request | false
+ end
+
+ with_them do
+ let(:issuable) { build_stubbed(issuable_type) }
+
+ subject { issuable.supports_severity? }
+
+ it { is_expected.to eq(supports_severity) }
+ end
+ end
+
describe '#incident?' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/models/dev_ops_score/metric_spec.rb b/spec/models/dev_ops_report/metric_spec.rb
index 60001d0667d..191692f43a4 100644
--- a/spec/models/dev_ops_score/metric_spec.rb
+++ b/spec/models/dev_ops_report/metric_spec.rb
@@ -2,8 +2,8 @@
require 'spec_helper'
-RSpec.describe DevOpsScore::Metric do
- let(:conv_dev_index) { create(:dev_ops_score_metric) }
+RSpec.describe DevOpsReport::Metric do
+ let(:conv_dev_index) { create(:dev_ops_report_metric) }
describe '#percentage_score' do
it 'returns stored percentage score' do
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 3eb74da09e1..568d5480613 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -692,6 +692,7 @@ RSpec.describe Group do
before do
create(:group_member, user: user, group: group_parent, access_level: parent_group_access_level)
create(:group_member, user: user, group: group, access_level: group_access_level)
+ create(:group_member, :minimal_access, user: create(:user), source: group)
create(:group_member, user: user, group: group_child, access_level: child_group_access_level)
end
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index d8e70911a29..39807747cc0 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -16,7 +16,6 @@ RSpec.describe Member do
it { is_expected.to validate_presence_of(:user) }
it { is_expected.to validate_presence_of(:source) }
- it { is_expected.to validate_inclusion_of(:access_level).in_array(Gitlab::Access.all_values) }
it_behaves_like 'an object with email-formated attributes', :invite_email do
subject { build(:project_member) }
@@ -150,6 +149,7 @@ RSpec.describe Member do
accepted_request_user = create(:user).tap { |u| project.request_access(u) }
@accepted_request_member = project.requesters.find_by(user_id: accepted_request_user.id).tap { |m| m.accept_request }
+ @member_with_minimal_access = create(:group_member, :minimal_access, source: group)
end
describe '.access_for_user_ids' do
@@ -180,6 +180,15 @@ RSpec.describe Member do
it { expect(described_class.non_invite).to include @accepted_request_member }
end
+ describe '.non_minimal_access' do
+ it { expect(described_class.non_minimal_access).to include @maintainer }
+ it { expect(described_class.non_minimal_access).to include @invited_member }
+ it { expect(described_class.non_minimal_access).to include @accepted_invite_member }
+ it { expect(described_class.non_minimal_access).to include @requested_member }
+ it { expect(described_class.non_minimal_access).to include @accepted_request_member }
+ it { expect(described_class.non_minimal_access).not_to include @member_with_minimal_access }
+ end
+
describe '.request' do
it { expect(described_class.request).not_to include @maintainer }
it { expect(described_class.request).not_to include @invited_member }
@@ -257,6 +266,34 @@ RSpec.describe Member do
it { is_expected.not_to include @blocked_maintainer }
it { is_expected.not_to include @blocked_developer }
end
+
+ describe '.active' do
+ subject { described_class.active.to_a }
+
+ it { is_expected.to include @owner }
+ it { is_expected.to include @maintainer }
+ it { is_expected.to include @invited_member }
+ it { is_expected.to include @accepted_invite_member }
+ it { is_expected.not_to include @requested_member }
+ it { is_expected.to include @accepted_request_member }
+ it { is_expected.not_to include @blocked_maintainer }
+ it { is_expected.not_to include @blocked_developer }
+ it { is_expected.not_to include @member_with_minimal_access }
+ end
+
+ describe '.active_without_invites_and_requests' do
+ subject { described_class.active_without_invites_and_requests.to_a }
+
+ it { is_expected.to include @owner }
+ it { is_expected.to include @maintainer }
+ it { is_expected.not_to include @invited_member }
+ it { is_expected.to include @accepted_invite_member }
+ it { is_expected.not_to include @requested_member }
+ it { is_expected.to include @accepted_request_member }
+ it { is_expected.not_to include @blocked_maintainer }
+ it { is_expected.not_to include @blocked_developer }
+ it { is_expected.not_to include @member_with_minimal_access }
+ end
end
describe "Delegate methods" do
diff --git a/spec/presenters/dev_ops_score/metric_presenter_spec.rb b/spec/presenters/dev_ops_report/metric_presenter_spec.rb
index 8b7b2c88578..306b5592c33 100644
--- a/spec/presenters/dev_ops_score/metric_presenter_spec.rb
+++ b/spec/presenters/dev_ops_report/metric_presenter_spec.rb
@@ -2,10 +2,10 @@
require 'spec_helper'
-RSpec.describe DevOpsScore::MetricPresenter do
+RSpec.describe DevOpsReport::MetricPresenter do
subject { described_class.new(metric) }
- let(:metric) { build(:dev_ops_score_metric) }
+ let(:metric) { build(:dev_ops_report_metric) }
describe '#cards' do
it 'includes instance score, leader score and percentage score' do
diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb
index 13e371ad68a..396b01edbfa 100644
--- a/spec/routing/admin_routing_spec.rb
+++ b/spec/routing/admin_routing_spec.rb
@@ -134,6 +134,20 @@ RSpec.describe Admin::HealthCheckController, "routing" do
end
end
+# admin_dev_ops_report GET /admin/dev_ops_report(.:format) admin/dev_ops_report#show
+RSpec.describe Admin::DevOpsReportController, "routing" do
+ it "to #show" do
+ expect(get("/admin/dev_ops_report")).to route_to('admin/dev_ops_report#show')
+ end
+end
+
+# admin_cohorts GET /admin/cohorts(.:format) admin/cohorst#index
+RSpec.describe Admin::CohortsController, "routing" do
+ it "to #index" do
+ expect(get("/admin/cohorts")).to route_to('admin/cohorts#index')
+ end
+end
+
RSpec.describe Admin::GroupsController, "routing" do
let(:name) { 'complex.group-namegit' }
diff --git a/spec/routing/instance_statistics_routing_spec.rb b/spec/routing/instance_statistics_routing_spec.rb
index 14d2bbb78a0..7eec807fb0b 100644
--- a/spec/routing/instance_statistics_routing_spec.rb
+++ b/spec/routing/instance_statistics_routing_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'Instance Statistics', 'routing' do
include RSpec::Rails::RequestExampleGroup
- it "routes '/-/instance_statistics' to dev ops score" do
- expect(get('/-/instance_statistics')).to redirect_to('/admin/dev_ops_score')
+ it "routes '/-/instance_statistics' to dev ops report" do
+ expect(get('/-/instance_statistics')).to redirect_to('/admin/dev_ops_report')
end
end
diff --git a/spec/services/authorized_project_update/project_create_service_spec.rb b/spec/services/authorized_project_update/project_create_service_spec.rb
index 891800bfb87..a9d0b82acfb 100644
--- a/spec/services/authorized_project_update/project_create_service_spec.rb
+++ b/spec/services/authorized_project_update/project_create_service_spec.rb
@@ -81,6 +81,7 @@ RSpec.describe AuthorizedProjectUpdate::ProjectCreateService do
before do
create(:group_member, access_level: Gitlab::Access::REPORTER, group: group, user: group_user)
create(:group_member, access_level: Gitlab::Access::MAINTAINER, group: shared_with_group, user: group_user)
+ create(:group_member, :minimal_access, source: shared_with_group, user: create(:user))
create(:group_group_link, shared_group: group, shared_with_group: shared_with_group, group_access: Gitlab::Access::DEVELOPER)
@@ -97,6 +98,11 @@ RSpec.describe AuthorizedProjectUpdate::ProjectCreateService do
access_level: Gitlab::Access::DEVELOPER)
expect(project_authorization).to exist
end
+
+ it 'does not create project authorization for user with minimal access' do
+ expect { service.execute }.to(
+ change { ProjectAuthorization.count }.from(0).to(1))
+ end
end
end
@@ -118,6 +124,17 @@ RSpec.describe AuthorizedProjectUpdate::ProjectCreateService do
end
end
+ context 'member with minimal access' do
+ before do
+ create(:group_member, :minimal_access, user: group_user, source: group)
+ end
+
+ it 'does not create project authorization' do
+ expect { service.execute }.not_to(
+ change { ProjectAuthorization.count }.from(0))
+ end
+ end
+
context 'project has more user than BATCH_SIZE' do
let(:batch_size) { 2 }
let(:users) { create_list(:user, batch_size + 1 ) }
diff --git a/spec/services/authorized_project_update/project_group_link_create_service_spec.rb b/spec/services/authorized_project_update/project_group_link_create_service_spec.rb
index 961322a1a21..1fd47f78c24 100644
--- a/spec/services/authorized_project_update/project_group_link_create_service_spec.rb
+++ b/spec/services/authorized_project_update/project_group_link_create_service_spec.rb
@@ -112,6 +112,17 @@ RSpec.describe AuthorizedProjectUpdate::ProjectGroupLinkCreateService do
end
end
+ context 'minimal access member' do
+ before do
+ create(:group_member, :minimal_access, user: group_user, source: group)
+ end
+
+ it 'does not create project authorization' do
+ expect { service.execute }.not_to(
+ change { ProjectAuthorization.count }.from(0))
+ end
+ end
+
context 'project has more users than BATCH_SIZE' do
let(:batch_size) { 2 }
let(:users) { create_list(:user, batch_size + 1 ) }
diff --git a/spec/services/submit_usage_ping_service_spec.rb b/spec/services/submit_usage_ping_service_spec.rb
index 401d1ed5adc..2082a163b29 100644
--- a/spec/services/submit_usage_ping_service_spec.rb
+++ b/spec/services/submit_usage_ping_service_spec.rb
@@ -71,12 +71,12 @@ RSpec.describe SubmitUsagePingService do
shared_examples 'saves DevOps report data from the response' do
it do
expect { subject.execute }
- .to change { DevOpsScore::Metric.count }
+ .to change { DevOpsReport::Metric.count }
.by(1)
- expect(DevOpsScore::Metric.last.leader_issues).to eq 10.2
- expect(DevOpsScore::Metric.last.instance_issues).to eq 3.2
- expect(DevOpsScore::Metric.last.percentage_issues).to eq 31.37
+ expect(DevOpsReport::Metric.last.leader_issues).to eq 10.2
+ expect(DevOpsReport::Metric.last.instance_issues).to eq 3.2
+ expect(DevOpsReport::Metric.last.percentage_issues).to eq 31.37
end
end
diff --git a/spec/support/shared_examples/lib/gitlab/auth/atlassian_identity_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/auth/atlassian_identity_shared_examples.rb
new file mode 100644
index 00000000000..18a5087da3b
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/auth/atlassian_identity_shared_examples.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'an atlassian identity' do
+ it 'sets the proper values' do
+ expect(identity.extern_uid).to eq(extern_uid)
+ expect(identity.token).to eq(credentials[:token])
+ expect(identity.refresh_token).to eq(credentials[:refresh_token])
+ expect(identity.expires_at.to_i).to eq(credentials[:expires_at])
+ end
+end
diff --git a/spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb
index 285b35fefca..777dc0c8571 100644
--- a/spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe 'layouts/nav/sidebar/_admin' do
context 'on analytics' do
before do
- allow(controller).to receive(:controller_name).and_return('dev_ops_score')
+ allow(controller).to receive(:controller_name).and_return('dev_ops_report')
end
it_behaves_like 'page has active tab', 'Analytics'