Commit 403678e0 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent f5050253
<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();
......
......@@ -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,
......
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 () => {};
// eslint-disable-next-line import/prefer-default-export
export const isContentLoaded = ({ content }) => Boolean(content);
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,
});
};
......
export const LOAD_CONTENT = 'loadContent';
export const RECEIVE_CONTENT_SUCCESS = 'receiveContentSuccess';
export const RECEIVE_CONTENT_ERROR = 'receiveContentError';
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;
},
};
const createState = (initialState = {}) => ({
projectId: null,
sourcePath: null,
isLoadingContent: false,
isContentLoaded: false,
content: '',
title: '',
...initialState,
});
......
......@@ -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
......
......@@ -20,7 +20,7 @@ module Ci
end
def groups
@groups ||= Ci::Group.fabricate(self)
@groups ||= Ci::Group.fabricate(project, self)
end
def to_param
......
......@@ -109,7 +109,7 @@ module Ci
end
def groups
@groups ||= Ci::Group.fabricate(self)
@groups ||= Ci::Group.fabricate(project, self)
end
def has_warnings?
......
......@@ -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] }
......
......@@ -3,4 +3,5 @@
class AnalyticsSummaryEntity < Grape::Entity
expose :value, safe: true
expose :title
expose :unit, if: { with_unit: true }
end
# 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
# 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
......@@ -5,4 +5,6 @@
%i.fa.fa-rss
.content_list
= spinner
.loading
.spinner.spinner-md
......@@ -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'
......
......@@ -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
......
---
title: Add read_api scope to personal access tokens for granting read only API access
merge_request: 28944
author:
type: added
---
title: Migrate .fa-spinner to .spinner for app/views/groups
merge_request: 25053
author: nuwe1
type: other
---
title: Create operations_user_lists table
merge_request: 28822
author:
type: added
---
title: Add support for dot (.) in variables masking
merge_request: 29022
author:
type: changed
......@@ -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:
......
# 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
# 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
......@@ -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
......
......@@ -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`. |
......
......@@ -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
......
......@@ -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,
......
......@@ -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]
......
......@@ -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
......
......@@ -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 ""
......
......@@ -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);
......
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.',
);
});
});
});
});
});
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);
});
});
});
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);
});
});
});
......@@ -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);
});
});
......
......@@ -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 => {
......
......@@ -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
......
......@@ -477,6 +477,7 @@ project:
- export_jobs
- daily_report_results
- jira_imports
- compliance_framework_setting
award_emoji:
- awardable
- user
......
......@@ -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) }
......
......@@ -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
......
......@@ -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
......@@ -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
......
......@@ -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
......@@ -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) }
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment