summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/static_site_editor/components/static_site_editor.vue5
-rw-r--r--app/assets/javascripts/static_site_editor/index.js6
-rw-r--r--app/assets/javascripts/static_site_editor/store/actions.js18
-rw-r--r--app/assets/javascripts/static_site_editor/store/getters.js2
-rw-r--r--app/assets/javascripts/static_site_editor/store/index.js6
-rw-r--r--app/assets/javascripts/static_site_editor/store/mutation_types.js3
-rw-r--r--app/assets/javascripts/static_site_editor/store/mutations.js15
-rw-r--r--app/assets/javascripts/static_site_editor/store/state.js6
-rw-r--r--app/models/ci/group.rb11
-rw-r--r--app/models/ci/legacy_stage.rb2
-rw-r--r--app/models/ci/stage.rb2
-rw-r--r--app/models/concerns/ci/maskable.rb4
-rw-r--r--app/serializers/analytics_summary_entity.rb1
-rw-r--r--app/services/ci/external_pull_requests/create_pipeline_service.rb31
-rw-r--r--app/services/external_pull_requests/create_pipeline_service.rb29
-rw-r--r--app/views/groups/_activities.html.haml4
-rw-r--r--app/views/projects/settings/_general.html.haml2
-rw-r--r--app/workers/update_external_pull_requests_worker.rb2
-rw-r--r--changelogs/unreleased/20440-limit-the-api-scope-of-personal-access-tokens.yml5
-rw-r--r--changelogs/unreleased/Resolve-Migrate--fa-spinner-app-views-groups.yml5
-rw-r--r--changelogs/unreleased/add-operations-ff-lists-table.yml5
-rw-r--r--changelogs/unreleased/support-dot-in-variables-masking.yml5
-rw-r--r--config/locales/doorkeeper.en.yml2
-rw-r--r--db/migrate/20200331132103_add_project_compliance_framework_settings_table.rb22
-rw-r--r--db/migrate/20200401211005_create_operations_user_lists.rb18
-rw-r--r--db/structure.sql57
-rw-r--r--doc/user/profile/personal_access_tokens.md1
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/helpers/projects_helpers.rb1
-rw-r--r--lib/gitlab/auth.rb3
-rw-r--r--lib/gitlab/import_export/project/import_export.yml1
-rw-r--r--locale/gitlab.pot27
-rw-r--r--spec/frontend/static_site_editor/components/static_site_editor_spec.js10
-rw-r--r--spec/frontend/static_site_editor/store/actions_spec.js76
-rw-r--r--spec/frontend/static_site_editor/store/getters_spec.js15
-rw-r--r--spec/frontend/static_site_editor/store/mutations_spec.js52
-rw-r--r--spec/javascripts/ci_variable_list/ajax_variable_list_spec.js2
-rw-r--r--spec/javascripts/ci_variable_list/ci_variable_list_spec.js2
-rw-r--r--spec/lib/gitlab/auth_spec.rb10
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/models/ci/group_spec.rb8
-rw-r--r--spec/models/concerns/ci/maskable_spec.rb6
-rw-r--r--spec/requests/api/api_spec.rb49
-rw-r--r--spec/requests/openid_connect_spec.rb2
-rw-r--r--spec/serializers/analytics_summary_serializer_spec.rb14
-rw-r--r--spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb (renamed from spec/services/external_pull_requests/create_pipeline_service_spec.rb)2
-rw-r--r--spec/workers/update_external_pull_requests_worker_spec.rb6
47 files changed, 491 insertions, 66 deletions
diff --git a/app/assets/javascripts/static_site_editor/components/static_site_editor.vue b/app/assets/javascripts/static_site_editor/components/static_site_editor.vue
index 4a0e153eb33..f06d48ee4f5 100644
--- a/app/assets/javascripts/static_site_editor/components/static_site_editor.vue
+++ b/app/assets/javascripts/static_site_editor/components/static_site_editor.vue
@@ -1,5 +1,5 @@
<script>
-import { mapState, mapActions } from 'vuex';
+import { mapState, mapGetters, mapActions } from 'vuex';
import { GlSkeletonLoader } from '@gitlab/ui';
import EditArea from './edit_area.vue';
@@ -10,7 +10,8 @@ export default {
GlSkeletonLoader,
},
computed: {
- ...mapState(['content', 'isContentLoaded', 'isLoadingContent']),
+ ...mapState(['content', 'isLoadingContent']),
+ ...mapGetters(['isContentLoaded']),
},
mounted() {
this.loadContent();
diff --git a/app/assets/javascripts/static_site_editor/index.js b/app/assets/javascripts/static_site_editor/index.js
index 4290cb9a9ba..22f96a60df0 100644
--- a/app/assets/javascripts/static_site_editor/index.js
+++ b/app/assets/javascripts/static_site_editor/index.js
@@ -3,7 +3,11 @@ import StaticSiteEditor from './components/static_site_editor.vue';
import createStore from './store';
const initStaticSiteEditor = el => {
- const store = createStore();
+ const { projectId, path: sourcePath } = el.dataset;
+
+ const store = createStore({
+ initialState: { projectId, sourcePath },
+ });
return new Vue({
el,
diff --git a/app/assets/javascripts/static_site_editor/store/actions.js b/app/assets/javascripts/static_site_editor/store/actions.js
new file mode 100644
index 00000000000..192345f3749
--- /dev/null
+++ b/app/assets/javascripts/static_site_editor/store/actions.js
@@ -0,0 +1,18 @@
+import createFlash from '~/flash';
+import { __ } from '~/locale';
+
+import * as mutationTypes from './mutation_types';
+import loadSourceContent from '~/static_site_editor/services/load_source_content';
+
+export const loadContent = ({ commit, state: { sourcePath, projectId } }) => {
+ commit(mutationTypes.LOAD_CONTENT);
+
+ return loadSourceContent({ sourcePath, projectId })
+ .then(data => commit(mutationTypes.RECEIVE_CONTENT_SUCCESS, data))
+ .catch(() => {
+ commit(mutationTypes.RECEIVE_CONTENT_ERROR);
+ createFlash(__('An error ocurred while loading your content. Please try again.'));
+ });
+};
+
+export default () => {};
diff --git a/app/assets/javascripts/static_site_editor/store/getters.js b/app/assets/javascripts/static_site_editor/store/getters.js
new file mode 100644
index 00000000000..8baa2941594
--- /dev/null
+++ b/app/assets/javascripts/static_site_editor/store/getters.js
@@ -0,0 +1,2 @@
+// eslint-disable-next-line import/prefer-default-export
+export const isContentLoaded = ({ content }) => Boolean(content);
diff --git a/app/assets/javascripts/static_site_editor/store/index.js b/app/assets/javascripts/static_site_editor/store/index.js
index 653c2532ee6..43256979ddd 100644
--- a/app/assets/javascripts/static_site_editor/store/index.js
+++ b/app/assets/javascripts/static_site_editor/store/index.js
@@ -1,12 +1,18 @@
import Vuex from 'vuex';
import Vue from 'vue';
import createState from './state';
+import * as getters from './getters';
+import * as actions from './actions';
+import mutations from './mutations';
Vue.use(Vuex);
const createStore = ({ initialState } = {}) => {
return new Vuex.Store({
state: createState(initialState),
+ getters,
+ actions,
+ mutations,
});
};
diff --git a/app/assets/javascripts/static_site_editor/store/mutation_types.js b/app/assets/javascripts/static_site_editor/store/mutation_types.js
new file mode 100644
index 00000000000..cbe51180541
--- /dev/null
+++ b/app/assets/javascripts/static_site_editor/store/mutation_types.js
@@ -0,0 +1,3 @@
+export const LOAD_CONTENT = 'loadContent';
+export const RECEIVE_CONTENT_SUCCESS = 'receiveContentSuccess';
+export const RECEIVE_CONTENT_ERROR = 'receiveContentError';
diff --git a/app/assets/javascripts/static_site_editor/store/mutations.js b/app/assets/javascripts/static_site_editor/store/mutations.js
new file mode 100644
index 00000000000..88cb74d2b11
--- /dev/null
+++ b/app/assets/javascripts/static_site_editor/store/mutations.js
@@ -0,0 +1,15 @@
+import * as types from './mutation_types';
+
+export default {
+ [types.LOAD_CONTENT](state) {
+ state.isLoadingContent = true;
+ },
+ [types.RECEIVE_CONTENT_SUCCESS](state, { title, content }) {
+ state.isLoadingContent = false;
+ state.title = title;
+ state.content = content;
+ },
+ [types.RECEIVE_CONTENT_ERROR](state) {
+ state.isLoadingContent = false;
+ },
+};
diff --git a/app/assets/javascripts/static_site_editor/store/state.js b/app/assets/javascripts/static_site_editor/store/state.js
index 68a7f95760c..b68e73f06f5 100644
--- a/app/assets/javascripts/static_site_editor/store/state.js
+++ b/app/assets/javascripts/static_site_editor/store/state.js
@@ -1,8 +1,12 @@
const createState = (initialState = {}) => ({
+ projectId: null,
+ sourcePath: null,
+
isLoadingContent: false,
- isContentLoaded: false,
content: '',
+ title: '',
+
...initialState,
});
diff --git a/app/models/ci/group.rb b/app/models/ci/group.rb
index 0e05318b253..15dc1ca8954 100644
--- a/app/models/ci/group.rb
+++ b/app/models/ci/group.rb
@@ -11,11 +11,12 @@ module Ci
include StaticModel
include Gitlab::Utils::StrongMemoize
- attr_reader :stage, :name, :jobs
+ attr_reader :project, :stage, :name, :jobs
delegate :size, to: :jobs
- def initialize(stage, name:, jobs:)
+ def initialize(project, stage, name:, jobs:)
+ @project = project
@stage = stage
@name = name
@jobs = jobs
@@ -23,7 +24,7 @@ module Ci
def status
strong_memoize(:status) do
- if Feature.enabled?(:ci_composite_status, default_enabled: false)
+ if Feature.enabled?(:ci_composite_status, project, default_enabled: false)
Gitlab::Ci::Status::Composite
.new(@jobs)
.status
@@ -44,11 +45,11 @@ module Ci
end
end
- def self.fabricate(stage)
+ def self.fabricate(project, stage)
stage.statuses.ordered.latest
.sort_by(&:sortable_name).group_by(&:group_name)
.map do |group_name, grouped_statuses|
- self.new(stage, name: group_name, jobs: grouped_statuses)
+ self.new(project, stage, name: group_name, jobs: grouped_statuses)
end
end
end
diff --git a/app/models/ci/legacy_stage.rb b/app/models/ci/legacy_stage.rb
index 9ca5cf13907..f156219ea81 100644
--- a/app/models/ci/legacy_stage.rb
+++ b/app/models/ci/legacy_stage.rb
@@ -20,7 +20,7 @@ module Ci
end
def groups
- @groups ||= Ci::Group.fabricate(self)
+ @groups ||= Ci::Group.fabricate(project, self)
end
def to_param
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index e6c34f3df03..93bd42f8734 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -109,7 +109,7 @@ module Ci
end
def groups
- @groups ||= Ci::Group.fabricate(self)
+ @groups ||= Ci::Group.fabricate(project, self)
end
def has_warnings?
diff --git a/app/models/concerns/ci/maskable.rb b/app/models/concerns/ci/maskable.rb
index 15bc48bf964..4e0ee72f18f 100644
--- a/app/models/concerns/ci/maskable.rb
+++ b/app/models/concerns/ci/maskable.rb
@@ -9,9 +9,9 @@ module Ci
# * No variables
# * No spaces
# * Minimal length of 8 characters
- # * Characters must be from the Base64 alphabet (RFC4648) with the addition of @ and :
+ # * Characters must be from the Base64 alphabet (RFC4648) with the addition of '@', ':' and '.'
# * Absolutely no fun is allowed
- REGEX = /\A[a-zA-Z0-9_+=\/@:-]{8,}\z/.freeze
+ REGEX = /\A[a-zA-Z0-9_+=\/@:.-]{8,}\z/.freeze
included do
validates :masked, inclusion: { in: [true, false] }
diff --git a/app/serializers/analytics_summary_entity.rb b/app/serializers/analytics_summary_entity.rb
index 39c6b4b06b2..b9797bfb021 100644
--- a/app/serializers/analytics_summary_entity.rb
+++ b/app/serializers/analytics_summary_entity.rb
@@ -3,4 +3,5 @@
class AnalyticsSummaryEntity < Grape::Entity
expose :value, safe: true
expose :title
+ expose :unit, if: { with_unit: true }
end
diff --git a/app/services/ci/external_pull_requests/create_pipeline_service.rb b/app/services/ci/external_pull_requests/create_pipeline_service.rb
new file mode 100644
index 00000000000..78be94bfb41
--- /dev/null
+++ b/app/services/ci/external_pull_requests/create_pipeline_service.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+# This service is responsible for creating a pipeline for a given
+# ExternalPullRequest coming from other providers such as GitHub.
+
+module Ci
+ module ExternalPullRequests
+ class CreatePipelineService < BaseService
+ def execute(pull_request)
+ return unless pull_request.open? && pull_request.actual_branch_head?
+
+ create_pipeline_for(pull_request)
+ end
+
+ private
+
+ def create_pipeline_for(pull_request)
+ Ci::CreatePipelineService.new(project, current_user, create_params(pull_request))
+ .execute(:external_pull_request_event, external_pull_request: pull_request)
+ end
+
+ def create_params(pull_request)
+ {
+ ref: pull_request.source_ref,
+ source_sha: pull_request.source_sha,
+ target_sha: pull_request.target_sha
+ }
+ end
+ end
+ end
+end
diff --git a/app/services/external_pull_requests/create_pipeline_service.rb b/app/services/external_pull_requests/create_pipeline_service.rb
deleted file mode 100644
index 36411465ff1..00000000000
--- a/app/services/external_pull_requests/create_pipeline_service.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# frozen_string_literal: true
-
-# This service is responsible for creating a pipeline for a given
-# ExternalPullRequest coming from other providers such as GitHub.
-
-module ExternalPullRequests
- class CreatePipelineService < BaseService
- def execute(pull_request)
- return unless pull_request.open? && pull_request.actual_branch_head?
-
- create_pipeline_for(pull_request)
- end
-
- private
-
- def create_pipeline_for(pull_request)
- Ci::CreatePipelineService.new(project, current_user, create_params(pull_request))
- .execute(:external_pull_request_event, external_pull_request: pull_request)
- end
-
- def create_params(pull_request)
- {
- ref: pull_request.source_ref,
- source_sha: pull_request.source_sha,
- target_sha: pull_request.target_sha
- }
- end
- end
-end
diff --git a/app/views/groups/_activities.html.haml b/app/views/groups/_activities.html.haml
index 44554cab1e9..47e7e27de48 100644
--- a/app/views/groups/_activities.html.haml
+++ b/app/views/groups/_activities.html.haml
@@ -5,4 +5,6 @@
%i.fa.fa-rss
.content_list
-= spinner
+.loading
+ .spinner.spinner-md
+
diff --git a/app/views/projects/settings/_general.html.haml b/app/views/projects/settings/_general.html.haml
index 520f342f567..0f60fc18026 100644
--- a/app/views/projects/settings/_general.html.haml
+++ b/app/views/projects/settings/_general.html.haml
@@ -20,6 +20,8 @@
= f.text_field :tag_list, value: @project.tag_list.join(', '), maxlength: 2000, class: "form-control"
%p.form-text.text-muted= _('Separate topics with commas.')
+ = render_if_exists 'compliance_management/compliance_framework/project_settings', f: f
+
.row
.form-group.col-md-9
= f.label :description, _('Project description (optional)'), class: 'label-bold'
diff --git a/app/workers/update_external_pull_requests_worker.rb b/app/workers/update_external_pull_requests_worker.rb
index b459d26e487..0d48877e1b0 100644
--- a/app/workers/update_external_pull_requests_worker.rb
+++ b/app/workers/update_external_pull_requests_worker.rb
@@ -21,7 +21,7 @@ class UpdateExternalPullRequestsWorker # rubocop:disable Scalability/IdempotentW
.by_source_branch(branch)
external_pull_requests.find_each do |pull_request|
- ExternalPullRequests::CreatePipelineService.new(project, user)
+ Ci::ExternalPullRequests::CreatePipelineService.new(project, user)
.execute(pull_request)
end
end
diff --git a/changelogs/unreleased/20440-limit-the-api-scope-of-personal-access-tokens.yml b/changelogs/unreleased/20440-limit-the-api-scope-of-personal-access-tokens.yml
new file mode 100644
index 00000000000..8756237cc40
--- /dev/null
+++ b/changelogs/unreleased/20440-limit-the-api-scope-of-personal-access-tokens.yml
@@ -0,0 +1,5 @@
+---
+title: Add read_api scope to personal access tokens for granting read only API access
+merge_request: 28944
+author:
+type: added
diff --git a/changelogs/unreleased/Resolve-Migrate--fa-spinner-app-views-groups.yml b/changelogs/unreleased/Resolve-Migrate--fa-spinner-app-views-groups.yml
new file mode 100644
index 00000000000..555e02d880c
--- /dev/null
+++ b/changelogs/unreleased/Resolve-Migrate--fa-spinner-app-views-groups.yml
@@ -0,0 +1,5 @@
+---
+title: Migrate .fa-spinner to .spinner for app/views/groups
+merge_request: 25053
+author: nuwe1
+type: other
diff --git a/changelogs/unreleased/add-operations-ff-lists-table.yml b/changelogs/unreleased/add-operations-ff-lists-table.yml
new file mode 100644
index 00000000000..7696797c9f5
--- /dev/null
+++ b/changelogs/unreleased/add-operations-ff-lists-table.yml
@@ -0,0 +1,5 @@
+---
+title: Create operations_user_lists table
+merge_request: 28822
+author:
+type: added
diff --git a/changelogs/unreleased/support-dot-in-variables-masking.yml b/changelogs/unreleased/support-dot-in-variables-masking.yml
new file mode 100644
index 00000000000..24970e5c671
--- /dev/null
+++ b/changelogs/unreleased/support-dot-in-variables-masking.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for dot (.) in variables masking
+merge_request: 29022
+author:
+type: changed
diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml
index 258d8a99986..c9dbde23d35 100644
--- a/config/locales/doorkeeper.en.yml
+++ b/config/locales/doorkeeper.en.yml
@@ -70,6 +70,8 @@ en:
scope_desc:
api:
Grants complete read/write access to the API, including all groups and projects, the container registry, and the package registry.
+ read_api:
+ Grants read access to the API, including all groups and projects, the container registry, and the package registry.
read_user:
Grants read-only access to the authenticated user's profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users.
read_repository:
diff --git a/db/migrate/20200331132103_add_project_compliance_framework_settings_table.rb b/db/migrate/20200331132103_add_project_compliance_framework_settings_table.rb
new file mode 100644
index 00000000000..6af8c6db939
--- /dev/null
+++ b/db/migrate/20200331132103_add_project_compliance_framework_settings_table.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class AddProjectComplianceFrameworkSettingsTable < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ with_lock_retries do
+ create_table :project_compliance_framework_settings, id: false do |t|
+ t.references :project, primary_key: true, null: false, index: true, foreign_key: { on_delete: :cascade }
+ t.integer :framework, null: false, limit: 2
+ end
+ end
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :project_compliance_framework_settings
+ end
+ end
+end
diff --git a/db/migrate/20200401211005_create_operations_user_lists.rb b/db/migrate/20200401211005_create_operations_user_lists.rb
new file mode 100644
index 00000000000..c5b928b8350
--- /dev/null
+++ b/db/migrate/20200401211005_create_operations_user_lists.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class CreateOperationsUserLists < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ create_table :operations_user_lists do |t|
+ t.references :project, index: false, foreign_key: { on_delete: :cascade }, null: false
+ t.timestamps_with_timezone
+ t.integer :iid, null: false
+ t.string :name, null: false, limit: 255
+ t.text :user_xids, null: false, default: ''
+
+ t.index [:project_id, :iid], unique: true
+ t.index [:project_id, :name], unique: true
+ end
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index a284d001ee5..c2a2e4b361c 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -4323,6 +4323,25 @@ CREATE SEQUENCE public.operations_strategies_id_seq
ALTER SEQUENCE public.operations_strategies_id_seq OWNED BY public.operations_strategies.id;
+CREATE TABLE public.operations_user_lists (
+ id bigint NOT NULL,
+ project_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ iid integer NOT NULL,
+ name character varying(255) NOT NULL,
+ user_xids text DEFAULT ''::text NOT NULL
+);
+
+CREATE SEQUENCE public.operations_user_lists_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE public.operations_user_lists_id_seq OWNED BY public.operations_user_lists.id;
+
CREATE TABLE public.packages_build_infos (
id bigint NOT NULL,
package_id integer NOT NULL,
@@ -4722,6 +4741,20 @@ CREATE SEQUENCE public.project_ci_cd_settings_id_seq
ALTER SEQUENCE public.project_ci_cd_settings_id_seq OWNED BY public.project_ci_cd_settings.id;
+CREATE TABLE public.project_compliance_framework_settings (
+ project_id bigint NOT NULL,
+ framework smallint NOT NULL
+);
+
+CREATE SEQUENCE public.project_compliance_framework_settings_project_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE public.project_compliance_framework_settings_project_id_seq OWNED BY public.project_compliance_framework_settings.project_id;
+
CREATE TABLE public.project_custom_attributes (
id integer NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -7275,6 +7308,8 @@ ALTER TABLE ONLY public.operations_scopes ALTER COLUMN id SET DEFAULT nextval('p
ALTER TABLE ONLY public.operations_strategies ALTER COLUMN id SET DEFAULT nextval('public.operations_strategies_id_seq'::regclass);
+ALTER TABLE ONLY public.operations_user_lists ALTER COLUMN id SET DEFAULT nextval('public.operations_user_lists_id_seq'::regclass);
+
ALTER TABLE ONLY public.packages_build_infos ALTER COLUMN id SET DEFAULT nextval('public.packages_build_infos_id_seq'::regclass);
ALTER TABLE ONLY public.packages_conan_file_metadata ALTER COLUMN id SET DEFAULT nextval('public.packages_conan_file_metadata_id_seq'::regclass);
@@ -7315,6 +7350,8 @@ ALTER TABLE ONLY public.project_auto_devops ALTER COLUMN id SET DEFAULT nextval(
ALTER TABLE ONLY public.project_ci_cd_settings ALTER COLUMN id SET DEFAULT nextval('public.project_ci_cd_settings_id_seq'::regclass);
+ALTER TABLE ONLY public.project_compliance_framework_settings ALTER COLUMN project_id SET DEFAULT nextval('public.project_compliance_framework_settings_project_id_seq'::regclass);
+
ALTER TABLE ONLY public.project_custom_attributes ALTER COLUMN id SET DEFAULT nextval('public.project_custom_attributes_id_seq'::regclass);
ALTER TABLE ONLY public.project_daily_statistics ALTER COLUMN id SET DEFAULT nextval('public.project_daily_statistics_id_seq'::regclass);
@@ -8081,6 +8118,9 @@ ALTER TABLE ONLY public.operations_scopes
ALTER TABLE ONLY public.operations_strategies
ADD CONSTRAINT operations_strategies_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY public.operations_user_lists
+ ADD CONSTRAINT operations_user_lists_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY public.packages_build_infos
ADD CONSTRAINT packages_build_infos_pkey PRIMARY KEY (id);
@@ -8144,6 +8184,9 @@ ALTER TABLE ONLY public.project_auto_devops
ALTER TABLE ONLY public.project_ci_cd_settings
ADD CONSTRAINT project_ci_cd_settings_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY public.project_compliance_framework_settings
+ ADD CONSTRAINT project_compliance_framework_settings_pkey PRIMARY KEY (project_id);
+
ALTER TABLE ONLY public.project_custom_attributes
ADD CONSTRAINT project_custom_attributes_pkey PRIMARY KEY (id);
@@ -9644,6 +9687,10 @@ CREATE UNIQUE INDEX index_operations_scopes_on_strategy_id_and_environment_scope
CREATE INDEX index_operations_strategies_on_feature_flag_id ON public.operations_strategies USING btree (feature_flag_id);
+CREATE UNIQUE INDEX index_operations_user_lists_on_project_id_and_iid ON public.operations_user_lists USING btree (project_id, iid);
+
+CREATE UNIQUE INDEX index_operations_user_lists_on_project_id_and_name ON public.operations_user_lists USING btree (project_id, name);
+
CREATE UNIQUE INDEX index_packages_build_infos_on_package_id ON public.packages_build_infos USING btree (package_id);
CREATE INDEX index_packages_build_infos_on_pipeline_id ON public.packages_build_infos USING btree (pipeline_id);
@@ -9736,6 +9783,8 @@ CREATE UNIQUE INDEX index_project_auto_devops_on_project_id ON public.project_au
CREATE UNIQUE INDEX index_project_ci_cd_settings_on_project_id ON public.project_ci_cd_settings USING btree (project_id);
+CREATE INDEX index_project_compliance_framework_settings_on_project_id ON public.project_compliance_framework_settings USING btree (project_id);
+
CREATE INDEX index_project_custom_attributes_on_key_and_value ON public.project_custom_attributes USING btree (key, value);
CREATE UNIQUE INDEX index_project_custom_attributes_on_project_id_and_key ON public.project_custom_attributes USING btree (project_id, key);
@@ -10956,6 +11005,9 @@ ALTER TABLE ONLY public.project_deploy_tokens
ALTER TABLE ONLY public.packages_conan_file_metadata
ADD CONSTRAINT fk_rails_0afabd9328 FOREIGN KEY (package_file_id) REFERENCES public.packages_package_files(id) ON DELETE CASCADE;
+ALTER TABLE ONLY public.operations_user_lists
+ ADD CONSTRAINT fk_rails_0c716e079b FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY public.geo_node_statuses
ADD CONSTRAINT fk_rails_0ecc699c2a FOREIGN KEY (geo_node_id) REFERENCES public.geo_nodes(id) ON DELETE CASCADE;
@@ -11391,6 +11443,9 @@ ALTER TABLE ONLY public.prometheus_alerts
ALTER TABLE ONLY public.term_agreements
ADD CONSTRAINT fk_rails_6ea6520e4a FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+ALTER TABLE ONLY public.project_compliance_framework_settings
+ ADD CONSTRAINT fk_rails_6f5294f16c FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY public.users_security_dashboard_projects
ADD CONSTRAINT fk_rails_6f6cf8e66e FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
@@ -13002,8 +13057,10 @@ COPY "schema_migrations" (version) FROM STDIN;
20200330121000
20200330123739
20200330132913
+20200331132103
20200331195952
20200331220930
+20200401211005
20200402123926
20200402135250
20200402185044
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
index b667df24d32..204230c4ca3 100644
--- a/doc/user/profile/personal_access_tokens.md
+++ b/doc/user/profile/personal_access_tokens.md
@@ -43,6 +43,7 @@ the following table.
| ------------------ | ------------- | ----------- |
| `read_user` | [GitLab 8.15](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5951) | Allows access to the read-only endpoints under `/users`. Essentially, any of the `GET` requests in the [Users API][users] are allowed. |
| `api` | [GitLab 8.15](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5951) | Grants complete read/write access to the API, including all groups and projects, the container registry, and the package registry. |
+| `read_api` | [GitLab 12.10](https://https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28944) | Grants read access to the API, including all groups and projects, the container registry, and the package registry. |
| `read_registry` | [GitLab 9.3](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11845) | Allows to read (pull) [container registry] images if a project is private and authorization is required. |
| `sudo` | [GitLab 10.2](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14838) | Allows performing API actions as any user in the system (if the authenticated user is an admin). |
| `read_repository` | [GitLab 10.7](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17894) | Allows read-only access (pull) to the repository through `git clone`. |
diff --git a/lib/api/api.rb b/lib/api/api.rb
index cf6e7f18a5f..eb7f47de9e2 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -28,6 +28,7 @@ module API
]
allow_access_with_scope :api
+ allow_access_with_scope :read_api, if: -> (request) { request.get? }
prefix :api
version 'v3', using: :path do
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index cedf50aaff6..8ad682fc961 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -129,6 +129,7 @@ module API
:avatar,
:suggestion_commit_message,
:repository_storage,
+ :compliance_framework_setting,
# TODO: remove in API v5, replaced by *_access_level
:issues_enabled,
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 8a68808d9fd..c489c835d9d 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -6,7 +6,7 @@ module Gitlab
IpBlacklisted = Class.new(StandardError)
# Scopes used for GitLab API access
- API_SCOPES = [:api, :read_user].freeze
+ API_SCOPES = [:api, :read_user, :read_api].freeze
# Scopes used for GitLab Repository access
REPOSITORY_SCOPES = [:read_repository, :write_repository].freeze
@@ -198,6 +198,7 @@ module Gitlab
def abilities_for_scopes(scopes)
abilities_by_scope = {
api: full_authentication_abilities,
+ read_api: read_only_authentication_abilities,
read_registry: [:read_container_image],
read_repository: [:download_code],
write_repository: [:download_code, :push_code]
diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
index 06d7dd6f897..3cbd0d144e6 100644
--- a/lib/gitlab/import_export/project/import_export.yml
+++ b/lib/gitlab/import_export/project/import_export.yml
@@ -159,6 +159,7 @@ excluded_attributes:
- :max_artifacts_size
- :marked_for_deletion_at
- :marked_for_deletion_by_user_id
+ - :compliance_framework_setting
namespaces:
- :runners_token
- :runners_token_encrypted
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 71b228c276a..971a77e55be 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2090,6 +2090,9 @@ msgstr ""
msgid "An error occurred. Please try again."
msgstr ""
+msgid "An error ocurred while loading your content. Please try again."
+msgstr ""
+
msgid "An instance-level serverless domain already exists."
msgstr ""
@@ -3830,6 +3833,9 @@ msgstr ""
msgid "Choose which status most accurately reflects the current state of this issue:"
msgstr ""
+msgid "Choose your framework"
+msgstr ""
+
msgid "CiStatusLabel|canceled"
msgstr ""
@@ -5236,6 +5242,24 @@ msgstr ""
msgid "Compliance Dashboard"
msgstr ""
+msgid "Compliance framework (optional)"
+msgstr ""
+
+msgid "ComplianceFramework|GDPR - General Data Protection Regulation"
+msgstr ""
+
+msgid "ComplianceFramework|HIPAA - Health Insurance Portability and Accountability Act"
+msgstr ""
+
+msgid "ComplianceFramework|PCI-DSS - Payment Card Industry-Data Security Standard"
+msgstr ""
+
+msgid "ComplianceFramework|SOC 2 - Service Organization Control 2"
+msgstr ""
+
+msgid "ComplianceFramework|SOX - Sarbanes-Oxley"
+msgstr ""
+
msgid "Confidence: %{confidence}"
msgstr ""
@@ -17967,6 +17991,9 @@ msgstr ""
msgid "Select projects you want to import."
msgstr ""
+msgid "Select required regulatory standard"
+msgstr ""
+
msgid "Select shards to replicate"
msgstr ""
diff --git a/spec/frontend/static_site_editor/components/static_site_editor_spec.js b/spec/frontend/static_site_editor/components/static_site_editor_spec.js
index 919763ce9fe..cde95d0a21e 100644
--- a/spec/frontend/static_site_editor/components/static_site_editor_spec.js
+++ b/spec/frontend/static_site_editor/components/static_site_editor_spec.js
@@ -17,11 +17,15 @@ describe('StaticSiteEditor', () => {
let store;
let loadContentActionMock;
- const buildStore = (initialState = {}) => {
+ const buildStore = ({ initialState, getters } = {}) => {
loadContentActionMock = jest.fn();
store = new Vuex.Store({
state: createState(initialState),
+ getters: {
+ isContentLoaded: () => false,
+ ...getters,
+ },
actions: {
loadContent: loadContentActionMock,
},
@@ -56,7 +60,7 @@ describe('StaticSiteEditor', () => {
const content = 'edit area content';
beforeEach(() => {
- buildStore({ content, isContentLoaded: true });
+ buildStore({ initialState: { content }, getters: { isContentLoaded: () => true } });
buildWrapper();
});
@@ -70,7 +74,7 @@ describe('StaticSiteEditor', () => {
});
it('displays skeleton loader while loading content', () => {
- buildStore({ isLoadingContent: true });
+ buildStore({ initialState: { isLoadingContent: true } });
buildWrapper();
expect(wrapper.find(GlSkeletonLoader).exists()).toBe(true);
diff --git a/spec/frontend/static_site_editor/store/actions_spec.js b/spec/frontend/static_site_editor/store/actions_spec.js
new file mode 100644
index 00000000000..98d7d0d2c2d
--- /dev/null
+++ b/spec/frontend/static_site_editor/store/actions_spec.js
@@ -0,0 +1,76 @@
+import testAction from 'helpers/vuex_action_helper';
+import createState from '~/static_site_editor/store/state';
+import * as actions from '~/static_site_editor/store/actions';
+import * as mutationTypes from '~/static_site_editor/store/mutation_types';
+import loadSourceContent from '~/static_site_editor/services/load_source_content';
+
+import createFlash from '~/flash';
+
+import {
+ projectId,
+ sourcePath,
+ sourceContentTitle as title,
+ sourceContent as content,
+} from '../mock_data';
+
+jest.mock('~/flash');
+jest.mock('~/static_site_editor/services/load_source_content', () => jest.fn());
+
+describe('Static Site Editor Store actions', () => {
+ let state;
+
+ beforeEach(() => {
+ state = createState({
+ projectId,
+ sourcePath,
+ });
+ });
+
+ describe('loadContent', () => {
+ describe('on success', () => {
+ const payload = { title, content };
+
+ beforeEach(() => {
+ loadSourceContent.mockResolvedValueOnce(payload);
+ });
+
+ it('commits receiveContentSuccess', () => {
+ testAction(
+ actions.loadContent,
+ null,
+ state,
+ [
+ { type: mutationTypes.LOAD_CONTENT },
+ { type: mutationTypes.RECEIVE_CONTENT_SUCCESS, payload },
+ ],
+ [],
+ );
+
+ expect(loadSourceContent).toHaveBeenCalledWith({ projectId, sourcePath });
+ });
+ });
+
+ describe('on error', () => {
+ const expectedMutations = [
+ { type: mutationTypes.LOAD_CONTENT },
+ { type: mutationTypes.RECEIVE_CONTENT_ERROR },
+ ];
+
+ beforeEach(() => {
+ loadSourceContent.mockRejectedValueOnce();
+ });
+
+ it('commits receiveContentError', () => {
+ testAction(actions.loadContent, null, state, expectedMutations);
+ });
+
+ it('displays flash communicating error', () => {
+ return testAction(actions.loadContent, null, state, expectedMutations).then(() => {
+ expect(createFlash).toHaveBeenCalledWith(
+ 'An error ocurred while loading your content. Please try again.',
+ );
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/static_site_editor/store/getters_spec.js b/spec/frontend/static_site_editor/store/getters_spec.js
new file mode 100644
index 00000000000..8800216f3b0
--- /dev/null
+++ b/spec/frontend/static_site_editor/store/getters_spec.js
@@ -0,0 +1,15 @@
+import createState from '~/static_site_editor/store/state';
+import { isContentLoaded } from '~/static_site_editor/store/getters';
+import { sourceContent as content } from '../mock_data';
+
+describe('Static Site Editor Store getters', () => {
+ describe('isContentLoaded', () => {
+ it('returns true when content is not empty', () => {
+ expect(isContentLoaded(createState({ content }))).toBe(true);
+ });
+
+ it('returns false when content is empty', () => {
+ expect(isContentLoaded(createState({ content: '' }))).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/static_site_editor/store/mutations_spec.js b/spec/frontend/static_site_editor/store/mutations_spec.js
new file mode 100644
index 00000000000..c7055fbb2f0
--- /dev/null
+++ b/spec/frontend/static_site_editor/store/mutations_spec.js
@@ -0,0 +1,52 @@
+import createState from '~/static_site_editor/store/state';
+import mutations from '~/static_site_editor/store/mutations';
+import * as types from '~/static_site_editor/store/mutation_types';
+import { sourceContentTitle as title, sourceContent as content } from '../mock_data';
+
+describe('Static Site Editor Store mutations', () => {
+ let state;
+
+ beforeEach(() => {
+ state = createState();
+ });
+
+ describe('loadContent', () => {
+ beforeEach(() => {
+ mutations[types.LOAD_CONTENT](state);
+ });
+
+ it('sets isLoadingContent to true', () => {
+ expect(state.isLoadingContent).toBe(true);
+ });
+ });
+
+ describe('receiveContentSuccess', () => {
+ const payload = { title, content };
+
+ beforeEach(() => {
+ mutations[types.RECEIVE_CONTENT_SUCCESS](state, payload);
+ });
+
+ it('sets current state to LOADING', () => {
+ expect(state.isLoadingContent).toBe(false);
+ });
+
+ it('sets title', () => {
+ expect(state.title).toBe(payload.title);
+ });
+
+ it('sets content', () => {
+ expect(state.content).toBe(payload.content);
+ });
+ });
+
+ describe('receiveContentError', () => {
+ beforeEach(() => {
+ mutations[types.RECEIVE_CONTENT_ERROR](state);
+ });
+
+ it('sets current state to LOADING_ERROR', () => {
+ expect(state.isLoadingContent).toBe(false);
+ });
+ });
+});
diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
index b53e30b6896..a1377564073 100644
--- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
@@ -224,7 +224,7 @@ describe('AjaxFormVariableList', () => {
describe('maskableRegex', () => {
it('takes in the regex provided by the data attribute', () => {
- expect(container.dataset.maskableRegex).toBe('^[a-zA-Z0-9_+=/@:-]{8,}$');
+ expect(container.dataset.maskableRegex).toBe('^[a-zA-Z0-9_+=/@:.-]{8,}$');
expect(ajaxVariableList.maskableRegex).toBe(container.dataset.maskableRegex);
});
});
diff --git a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
index 180ba979325..c0c3a83a44b 100644
--- a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
@@ -162,7 +162,7 @@ describe('VariableList', () => {
});
it('has a regex provided via a data attribute', () => {
- expect($wrapper.attr('data-maskable-regex')).toBe('^[a-zA-Z0-9_+=/@:-]{8,}$');
+ expect($wrapper.attr('data-maskable-regex')).toBe('^[a-zA-Z0-9_+=/@:.-]{8,}$');
});
it('allows values that are 8 characters long', done => {
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index dcc4d277f5c..ce60a19a7b3 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
describe 'constants' do
it 'API_SCOPES contains all scopes for API access' do
- expect(subject::API_SCOPES).to eq %i[api read_user]
+ expect(subject::API_SCOPES).to eq %i[api read_user read_api]
end
it 'ADMIN_SCOPES contains all scopes for ADMIN access' do
@@ -30,7 +30,7 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'optional_scopes contains all non-default scopes' do
stub_container_registry_config(enabled: true)
- expect(subject.optional_scopes).to eq %i[read_user read_repository write_repository read_registry sudo openid profile email]
+ expect(subject.optional_scopes).to eq %i[read_user read_api read_repository write_repository read_registry sudo openid profile email]
end
end
@@ -38,21 +38,21 @@ describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'contains all non-default scopes' do
stub_container_registry_config(enabled: true)
- expect(subject.all_available_scopes).to eq %i[api read_user read_repository write_repository read_registry sudo]
+ expect(subject.all_available_scopes).to eq %i[api read_user read_api read_repository write_repository read_registry sudo]
end
it 'contains for non-admin user all non-default scopes without ADMIN access' do
stub_container_registry_config(enabled: true)
user = create(:user, admin: false)
- expect(subject.available_scopes_for(user)).to eq %i[api read_user read_repository write_repository read_registry]
+ expect(subject.available_scopes_for(user)).to eq %i[api read_user read_api read_repository write_repository read_registry]
end
it 'contains for admin user all non-default scopes with ADMIN access' do
stub_container_registry_config(enabled: true)
user = create(:user, admin: true)
- expect(subject.available_scopes_for(user)).to eq %i[api read_user read_repository write_repository read_registry sudo]
+ expect(subject.available_scopes_for(user)).to eq %i[api read_user read_api read_repository write_repository read_registry sudo]
end
context 'registry_scopes' do
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index aaaf6458ee7..515d72add92 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -477,6 +477,7 @@ project:
- export_jobs
- daily_report_results
- jira_imports
+- compliance_framework_setting
award_emoji:
- awardable
- user
diff --git a/spec/models/ci/group_spec.rb b/spec/models/ci/group_spec.rb
index 5516a1a9c61..868382e3756 100644
--- a/spec/models/ci/group_spec.rb
+++ b/spec/models/ci/group_spec.rb
@@ -3,12 +3,14 @@
require 'spec_helper'
describe Ci::Group do
+ let_it_be(:project) { create(:project) }
+
+ let!(:jobs) { build_list(:ci_build, 1, :success, project: project) }
+
subject do
- described_class.new('test', name: 'rspec', jobs: jobs)
+ described_class.new(project, 'test', name: 'rspec', jobs: jobs)
end
- let!(:jobs) { build_list(:ci_build, 1, :success) }
-
it { is_expected.to include_module(StaticModel) }
it { is_expected.to respond_to(:stage) }
diff --git a/spec/models/concerns/ci/maskable_spec.rb b/spec/models/concerns/ci/maskable_spec.rb
index 22ffb294819..01861b39165 100644
--- a/spec/models/concerns/ci/maskable_spec.rb
+++ b/spec/models/concerns/ci/maskable_spec.rb
@@ -61,8 +61,12 @@ describe Ci::Maskable do
expect(subject.match?(string)).to eq(false)
end
+ it 'does not match strings using unsupported characters' do
+ expect(subject.match?('HelloWorld%#^')).to eq(false)
+ end
+
it 'matches valid strings' do
- expect(subject.match?('helloworld')).to eq(true)
+ expect(subject.match?('Hello+World_123/@:-.')).to eq(true)
end
end
diff --git a/spec/requests/api/api_spec.rb b/spec/requests/api/api_spec.rb
index baebbbce631..201c0d1796c 100644
--- a/spec/requests/api/api_spec.rb
+++ b/spec/requests/api/api_spec.rb
@@ -3,15 +3,60 @@
require 'spec_helper'
describe API::API do
- let(:user) { create(:user, last_activity_on: Date.yesterday) }
+ include GroupAPIHelpers
describe 'Record user last activity in after hook' do
# It does not matter which endpoint is used because last_activity_on should
# be updated on every request. `/groups` is used as an example
# to represent any API endpoint
+ let(:user) { create(:user, last_activity_on: Date.yesterday) }
- it 'updates the users last_activity_on date' do
+ it 'updates the users last_activity_on to the current date' do
expect { get api('/groups', user) }.to change { user.reload.last_activity_on }.to(Date.today)
end
end
+
+ describe 'User with only read_api scope personal access token' do
+ # It does not matter which endpoint is used because this should behave
+ # in the same way for every request. `/groups` is used as an example
+ # to represent any API endpoint
+
+ context 'when personal access token has only read_api scope' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:token) { create(:personal_access_token, user: user, scopes: [:read_api]) }
+
+ before_all do
+ group.add_owner(user)
+ end
+
+ it 'does authorize user for get request' do
+ get api('/groups', personal_access_token: token)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'does not authorize user for post request' do
+ params = attributes_for_group_api
+
+ post api("/groups", personal_access_token: token), params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'does not authorize user for put request' do
+ group_param = { name: 'Test' }
+
+ put api("/groups/#{group.id}", personal_access_token: token), params: group_param
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'does not authorize user for delete request' do
+ delete api("/groups/#{group.id}", personal_access_token: token)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
end
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index d7c08484dc4..bd270679acd 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -180,7 +180,7 @@ describe 'OpenID Connect requests' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['issuer']).to eq('http://localhost')
expect(json_response['jwks_uri']).to eq('http://www.example.com/oauth/discovery/keys')
- expect(json_response['scopes_supported']).to eq(%w[api read_user read_repository write_repository sudo openid profile email])
+ expect(json_response['scopes_supported']).to eq(%w[api read_user read_api read_repository write_repository sudo openid profile email])
end
end
diff --git a/spec/serializers/analytics_summary_serializer_spec.rb b/spec/serializers/analytics_summary_serializer_spec.rb
index 06f2c0ca68b..7950f89bcc7 100644
--- a/spec/serializers/analytics_summary_serializer_spec.rb
+++ b/spec/serializers/analytics_summary_serializer_spec.rb
@@ -28,4 +28,18 @@ describe AnalyticsSummarySerializer do
it 'contains important elements of AnalyticsStage' do
expect(subject).to include(:title, :value)
end
+
+ it 'does not include unit' do
+ expect(subject).not_to include(:unit)
+ end
+
+ context 'when representing with unit' do
+ let(:resource) { { title: 'frequency', value: 1.12, unit: 'per day' } }
+
+ subject { described_class.new.represent(resource, with_unit: true) }
+
+ it 'contains unit' do
+ expect(subject).to include(:unit)
+ end
+ end
end
diff --git a/spec/services/external_pull_requests/create_pipeline_service_spec.rb b/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb
index d1893960960..5048f2b71b3 100644
--- a/spec/services/external_pull_requests/create_pipeline_service_spec.rb
+++ b/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe ExternalPullRequests::CreatePipelineService do
+describe Ci::ExternalPullRequests::CreatePipelineService do
describe '#execute' do
let_it_be(:project) { create(:project, :auto_devops, :repository) }
let_it_be(:user) { create(:user) }
diff --git a/spec/workers/update_external_pull_requests_worker_spec.rb b/spec/workers/update_external_pull_requests_worker_spec.rb
index 8930a36ceb8..afac0357b2d 100644
--- a/spec/workers/update_external_pull_requests_worker_spec.rb
+++ b/spec/workers/update_external_pull_requests_worker_spec.rb
@@ -28,10 +28,10 @@ describe UpdateExternalPullRequestsWorker do
context 'when ref is a branch' do
let(:ref) { 'refs/heads/feature-1' }
- let(:create_pipeline_service) { instance_double(ExternalPullRequests::CreatePipelineService) }
+ let(:create_pipeline_service) { instance_double(Ci::ExternalPullRequests::CreatePipelineService) }
it 'runs CreatePipelineService for each pull request matching the source branch and repository' do
- expect(ExternalPullRequests::CreatePipelineService)
+ expect(Ci::ExternalPullRequests::CreatePipelineService)
.to receive(:new)
.and_return(create_pipeline_service)
.twice
@@ -45,7 +45,7 @@ describe UpdateExternalPullRequestsWorker do
let(:ref) { 'refs/tags/v1.2.3' }
it 'does nothing' do
- expect(ExternalPullRequests::CreatePipelineService).not_to receive(:new)
+ expect(Ci::ExternalPullRequests::CreatePipelineService).not_to receive(:new)
subject
end