diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 2bb34b7ed4b2d95af0cf327e552d6ee983fc3587..78ddbedf25bfb2d5d4f8c77ba46199c758d40a4a 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -238,6 +238,7 @@ .group-path { color: $gl-gray-400; +<<<<<<< HEAD } } @@ -248,6 +249,8 @@ .project-path { color: $gl-gray-400; +======= +>>>>>>> upstream/master } } diff --git a/app/models/concerns/project_services_loggable.rb b/app/models/concerns/project_services_loggable.rb index 248a21f357878e0244845079e0d51c0858cff6be..fecd77cdc985a195131bb21c52ef4c673713ffb0 100644 --- a/app/models/concerns/project_services_loggable.rb +++ b/app/models/concerns/project_services_loggable.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ProjectServicesLoggable def log_info(message, params = {}) message = build_message(message, params) diff --git a/app/models/hooks/active_hook_filter.rb b/app/models/hooks/active_hook_filter.rb index ea046bea368206dc16a4668ba7d90a4da80a0a8f..283e2d680f4b2c1b03d68289a9cd4a3071a09e09 100644 --- a/app/models/hooks/active_hook_filter.rb +++ b/app/models/hooks/active_hook_filter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ActiveHookFilter def initialize(hook) @hook = hook diff --git a/app/serializers/status_entity.rb b/app/serializers/detailed_status_entity.rb similarity index 74% rename from app/serializers/status_entity.rb rename to app/serializers/detailed_status_entity.rb index 306c30f0323b8529d6603274cfa095b440271989..c772c807f76d121551972f1bc12ce34edf31a5fd 100644 --- a/app/serializers/status_entity.rb +++ b/app/serializers/detailed_status_entity.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class StatusEntity < Grape::Entity +class DetailedStatusEntity < Grape::Entity include RequestAwareEntity expose :icon, :text, :label, :group @@ -8,6 +8,14 @@ class StatusEntity < Grape::Entity expose :has_details?, as: :has_details expose :details_path + expose :illustration do |status| + begin + status.illustration + rescue NotImplementedError + # ignored + end + end + expose :favicon do |status| Gitlab::Favicon.status_overlay(status.favicon) end diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb index 7bc1d87dea5a7a3646f87e750bdf3aaf58543b34..26b29993fecee6ffc671b50604cb01f506e5b143 100644 --- a/app/serializers/job_entity.rb +++ b/app/serializers/job_entity.rb @@ -27,7 +27,7 @@ class JobEntity < Grape::Entity expose :playable?, as: :playable expose :created_at expose :updated_at - expose :detailed_status, as: :status, with: StatusEntity + expose :detailed_status, as: :status, with: DetailedStatusEntity expose :callout_message, if: -> (*) { failed? && !build.script_failure? } expose :recoverable, if: -> (*) { failed? } diff --git a/app/serializers/job_group_entity.rb b/app/serializers/job_group_entity.rb index 0941a9d36be9e43a6147435b1a90bb1f7ec5431e..0db7624b3f76adb8addef48bf9b0075739f18fa0 100644 --- a/app/serializers/job_group_entity.rb +++ b/app/serializers/job_group_entity.rb @@ -5,7 +5,7 @@ class JobGroupEntity < Grape::Entity expose :name expose :size - expose :detailed_status, as: :status, with: StatusEntity + expose :detailed_status, as: :status, with: DetailedStatusEntity expose :jobs, with: JobEntity private diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb index 6cf1925adda6c17f30bcc8c251409b3f4f0ef5a3..aef838409e0c718da3a1449f500142be6d8ef8c2 100644 --- a/app/serializers/pipeline_entity.rb +++ b/app/serializers/pipeline_entity.rb @@ -30,7 +30,7 @@ class PipelineEntity < Grape::Entity end expose :details do - expose :detailed_status, as: :status, with: StatusEntity + expose :detailed_status, as: :status, with: DetailedStatusEntity expose :duration expose :finished_at end diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb index 00e6d32ee3a9b21dd61e9e3ce04326e2aa7c2daa..ca8fa7e78772f6a8a7fb4561d6938021f26bf4c6 100644 --- a/app/serializers/stage_entity.rb +++ b/app/serializers/stage_entity.rb @@ -19,7 +19,7 @@ class StageEntity < Grape::Entity latest_statuses end - expose :detailed_status, as: :status, with: StatusEntity + expose :detailed_status, as: :status, with: DetailedStatusEntity expose :path do |stage| project_pipeline_path( diff --git a/app/services/emails/base_service.rb b/app/services/emails/base_service.rb index ba7b689a9af62a29e448879e3eaf8506dad0b98b..988215ffc78aaad3236cb2b8a3a704b308b2e03a 100644 --- a/app/services/emails/base_service.rb +++ b/app/services/emails/base_service.rb @@ -2,6 +2,8 @@ module Emails class BaseService + attr_reader :current_user + def initialize(current_user, params = {}) @current_user, @params = current_user, params.dup @user = params.delete(:user) diff --git a/app/services/emails/create_service.rb b/app/services/emails/create_service.rb index 224ee7018ad42d50f8334145bbcfbcad20aad432..fb7d234ccae0207c2a7b7bbc15dd5cc7afcc1f73 100644 --- a/app/services/emails/create_service.rb +++ b/app/services/emails/create_service.rb @@ -5,7 +5,12 @@ module Emails prepend ::EE::Emails::CreateService def execute(extra_params = {}) - @user.emails.create(@params.merge(extra_params)) + skip_confirmation = @params.delete(:skip_confirmation) + + email = @user.emails.create(@params.merge(extra_params)) + + email&.confirm if skip_confirmation && current_user.admin? + email end end end diff --git a/app/validators/branch_filter_validator.rb b/app/validators/branch_filter_validator.rb index ef482aaaa6315cf74f1a9a50daa82f75c731ac68..6a0899be850b6d75b3989a007b8143808c0de862 100644 --- a/app/validators/branch_filter_validator.rb +++ b/app/validators/branch_filter_validator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # BranchFilterValidator # # Custom validator for branch names. Squishes whitespace and ignores empty diff --git a/app/validators/js_regex_validator.rb b/app/validators/js_regex_validator.rb index a515af7b919d4793f8101493b92dad3690052f99..be715967b4a20dbfccf36d8fb97142e1fcfd0c31 100644 --- a/app/validators/js_regex_validator.rb +++ b/app/validators/js_regex_validator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class JsRegexValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) return true if value.blank? diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index 5623f0f590a50b51089e17f09fc84b8282b2cac7..78a1d1a0553c2c47b299eb419d6396062c9deeb8 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -11,5 +11,5 @@ = render "events/event/note", event: event - else = render "events/event/common", event: event -- elsif @user.include_private_contributions? +- elsif @user&.include_private_contributions? = render "events/event/private", event: event diff --git a/changelogs/unreleased/51112-add-status-illustration-in-job-api.yml b/changelogs/unreleased/51112-add-status-illustration-in-job-api.yml new file mode 100644 index 0000000000000000000000000000000000000000..fdc75e2882480da2ba171ea2d26c6dc9e7be8665 --- /dev/null +++ b/changelogs/unreleased/51112-add-status-illustration-in-job-api.yml @@ -0,0 +1,5 @@ +--- +title: Add empty state illustration information in job API +merge_request: 21532 +author: +type: other diff --git a/changelogs/unreleased/frozen-string-enable-vestigial.yml b/changelogs/unreleased/frozen-string-enable-vestigial.yml new file mode 100644 index 0000000000000000000000000000000000000000..55313ff0fccf0670a9bb0979a0272337a1aa61f3 --- /dev/null +++ b/changelogs/unreleased/frozen-string-enable-vestigial.yml @@ -0,0 +1,5 @@ +--- +title: Enable frozen string in vestigial files +merge_request: +author: gfyoung +type: performance diff --git a/changelogs/unreleased/sh-support-adding-confirmed-emails.yml b/changelogs/unreleased/sh-support-adding-confirmed-emails.yml new file mode 100644 index 0000000000000000000000000000000000000000..1b64a1c62dc6951f4a61fd757a6222be171372f3 --- /dev/null +++ b/changelogs/unreleased/sh-support-adding-confirmed-emails.yml @@ -0,0 +1,5 @@ +--- +title: Add ability to skip user email confirmation with API +merge_request: 21630 +author: +type: added diff --git a/db/importers/common_metrics_importer.rb b/db/importers/common_metrics_importer.rb index 01fbbd6866b99b910a2cac94919940a780a29ee5..6302394d7a6b173f2995ab898bb92250923799b4 100644 --- a/db/importers/common_metrics_importer.rb +++ b/db/importers/common_metrics_importer.rb @@ -35,8 +35,8 @@ module Importers attr_reader :content - def initialize(file = 'config/prometheus/common_metrics.yml') - @content = YAML.load_file(file) + def initialize(filename = 'common_metrics.yml') + @content = YAML.load_file(Rails.root.join('config', 'prometheus', filename)) end def execute diff --git a/doc/api/users.md b/doc/api/users.md index f2e6486c080a8436065f1e2dd6b1c83c1672bdb1..389f5101d63c73b70a650d2df1afd70a04390737 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -977,6 +977,7 @@ Parameters: - `id` (required) - id of specified user - `email` (required) - email address +- `skip_confirmation` (optional) - Skip confirmation and assume e-mail is verified - true or false (default) ## Delete email for current user diff --git a/lib/api/users.rb b/lib/api/users.rb index eea79cf33b65eb3e65a2c94185a57a274647f01f..661e070896b984e6e4e584f6ebfa07b5dc7bcf9a 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -367,6 +367,7 @@ module API params do requires :id, type: Integer, desc: 'The ID of the user' requires :email, type: String, desc: 'The email of the user' + optional :skip_confirmation, type: Boolean, desc: 'Skip confirmation of email and assume it is verified' end post ":id/emails" do authenticated_as_admin! diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 751919f95017f487eaabe53afc239314c0109b52..8b6903011c323b16cc6c87af4482599fe4cb51bf 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -194,6 +194,18 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do expect(json_response['terminal_path']).to match(%r{/terminal}) end end + + context 'when job passed with no trace' do + let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } + + it 'exposes empty state illustrations' do + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('job/job_details') + expect(json_response['status']['illustration']).to have_key('image') + expect(json_response['status']['illustration']).to have_key('size') + expect(json_response['status']['illustration']).to have_key('title') + end + end end def get_show(**extra_params) diff --git a/spec/db/importers/common_metrics_importer_spec.rb b/spec/db/importers/common_metrics_importer_spec.rb index 16b59e1dfe876337c63ba5d6e616e33261dbd8a0..68260820958db113c904fd2bf5f319c8a18ff8c7 100644 --- a/spec/db/importers/common_metrics_importer_spec.rb +++ b/spec/db/importers/common_metrics_importer_spec.rb @@ -47,6 +47,16 @@ describe Importers::CommonMetricsImporter do end end + context "does import common_metrics.yml" do + it "when executed from outside of the Rails.root" do + Dir.chdir(Dir.tmpdir) do + expect { subject.execute }.not_to raise_error + end + + expect(PrometheusMetric.common).not_to be_empty + end + end + context 'does import properly all fields' do let(:query_identifier) { 'response-metric' } let(:group) do diff --git a/spec/features/projects/activity/user_sees_private_activity_spec.rb b/spec/features/projects/activity/user_sees_private_activity_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..d7dc0a6712a913827cc92b6b619660ee61c8f4ae --- /dev/null +++ b/spec/features/projects/activity/user_sees_private_activity_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe 'Project > Activity > User sees private activity', :js do + let(:project) { create(:project, :public) } + let(:author) { create(:user) } + let(:user) { create(:user) } + let(:issue) { create(:issue, :confidential, project: project, author: author) } + let(:message) { "#{author.name} opened issue #{issue.to_reference}" } + + before do + project.add_developer(author) + + create(:event, :created, project: project, target: issue, author: author) + end + + it 'shows the activity to a logged-in user with permissions' do + sign_in(author) + visit activity_project_path(project) + + expect(page).to have_content(message) + end + + it 'hides the activity from a logged-in user without permissions' do + sign_in(user) + visit activity_project_path(project) + + expect(page).not_to have_content(message) + end + + it 'hides the activity from an anonymous user' do + visit activity_project_path(project) + + expect(page).not_to have_content(message) + end +end diff --git a/spec/fixtures/api/schemas/job/job.json b/spec/fixtures/api/schemas/job/job.json index c793d93c0f67f98e501d874dc353042f43bb0059..f5d58b21e3d198564bbb14d48413ac3623ee2ae0 100644 --- a/spec/fixtures/api/schemas/job/job.json +++ b/spec/fixtures/api/schemas/job/job.json @@ -25,7 +25,7 @@ "playable": { "type": "boolean" }, "created_at": { "type": "string" }, "updated_at": { "type": "string" }, - "status": { "$ref": "../ci_detailed_status.json" } + "status": { "$ref": "../status/ci_detailed_status.json" } }, "additionalProperties": true } diff --git a/spec/fixtures/api/schemas/pipeline_stage.json b/spec/fixtures/api/schemas/pipeline_stage.json index eb2667295f0d841bb23e1aa8f6492f29be2a06e4..f72988a3d3dfabba648c481845562448cb7807b7 100644 --- a/spec/fixtures/api/schemas/pipeline_stage.json +++ b/spec/fixtures/api/schemas/pipeline_stage.json @@ -16,7 +16,7 @@ "items": { "$ref": "job/job.json" }, "optional": true }, - "status": { "$ref": "ci_detailed_status.json" }, + "status": { "$ref": "status/ci_detailed_status.json" }, "path": { "type": "string" }, "dropdown_path": { "type": "string" } }, diff --git a/spec/fixtures/api/schemas/status/action.json b/spec/fixtures/api/schemas/status/action.json new file mode 100644 index 0000000000000000000000000000000000000000..99a576e6c5b569541f5777bc8fb72557e66c1775 --- /dev/null +++ b/spec/fixtures/api/schemas/status/action.json @@ -0,0 +1,22 @@ +{ + "type": "object", + "required": [ + "icon", + "title", + "path", + "method" + ], + "properties": { + "icon": { + "type": "string", + "enum": [ + "retry", + "play", + "cancel" + ] + }, + "title": { "type": "string" }, + "path": { "type": "string" }, + "method": { "$ref": "../http_method.json" } + } +} diff --git a/spec/fixtures/api/schemas/ci_detailed_status.json b/spec/fixtures/api/schemas/status/ci_detailed_status.json similarity index 51% rename from spec/fixtures/api/schemas/ci_detailed_status.json rename to spec/fixtures/api/schemas/status/ci_detailed_status.json index d74248eabef29436d16c53555b174871c66325df..8d0f1e4a6af06572a4dd02de7d09b40aee97cc14 100644 --- a/spec/fixtures/api/schemas/ci_detailed_status.json +++ b/spec/fixtures/api/schemas/status/ci_detailed_status.json @@ -1,6 +1,6 @@ { "type": "object", - "required" : [ + "required": [ "icon", "text", "label", @@ -19,28 +19,8 @@ "has_details": { "type": "boolean" }, "details_path": { "type": "string" }, "favicon": { "type": "string" }, - "action": { - "type": "object", - "required": [ - "icon", - "title", - "path", - "method" - ], - "properties": { - "icon": { - "type": "string", - "enum": [ - "retry", - "play", - "cancel" - ] - }, - "title": { "type": "string" }, - "path": { "type": "string" }, - "method": { "$ref": "http_method.json" } - } - } + "illustration": { "$ref": "illustration.json" }, + "action": { "$ref": "action.json" } }, "additionalProperties": false } diff --git a/spec/fixtures/api/schemas/status/illustration.json b/spec/fixtures/api/schemas/status/illustration.json new file mode 100644 index 0000000000000000000000000000000000000000..9a085f5f1ee64a6afcd6f41a8c7d4497db395f46 --- /dev/null +++ b/spec/fixtures/api/schemas/status/illustration.json @@ -0,0 +1,19 @@ +{ + "oneOf": [ + { "type": "null" }, + { + "type": "object", + "required": [ + "image", + "size", + "title" + ], + "properties": { + "image": { "type": "string" }, + "size": { "type": "string" }, + "title": { "type": "string" }, + "content": { "type": "string" } + } + } + ] +} diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 0d9ec21b6060d9476105aa59c5cb2330c4991566..62990ddd5d8e3759f339f78017825a6de8b58d98 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -1064,11 +1064,14 @@ describe API::Users do expect(json_response['error']).to eq('email is missing') end - it "creates email" do + it "creates unverified email" do email_attrs = attributes_for :email expect do post api("/users/#{user.id}/emails", admin), email_attrs end.to change { user.emails.count }.by(1) + + email = Email.find_by(user_id: user.id, email: email_attrs[:email]) + expect(email).not_to be_confirmed end it "returns a 400 for invalid ID" do @@ -1076,6 +1079,18 @@ describe API::Users do expect(response).to have_gitlab_http_status(400) end + + it "creates verified email" do + email_attrs = attributes_for :email + email_attrs[:skip_confirmation] = true + + post api("/users/#{user.id}/emails", admin), email_attrs + + expect(response).to have_gitlab_http_status(201) + + email = Email.find_by(user_id: user.id, email: email_attrs[:email]) + expect(email).to be_confirmed + end end describe 'GET /user/:id/emails' do diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/detailed_status_entity_spec.rb similarity index 95% rename from spec/serializers/status_entity_spec.rb rename to spec/serializers/detailed_status_entity_spec.rb index 0b010ebd5073bff71a31b80f55ba8bbe90760179..62f57ca86899e09aa62dd33c6a2064a265916192 100644 --- a/spec/serializers/status_entity_spec.rb +++ b/spec/serializers/detailed_status_entity_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe StatusEntity do +describe DetailedStatusEntity do let(:entity) { described_class.new(status) } let(:status) do