From f61bb2a16a514b71bf33aabbbb999d6732016a24 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 20 Apr 2021 14:36:54 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-11-stable-ee --- app/serializers/README.md | 2 +- app/serializers/admin/user_entity.rb | 2 + app/serializers/build_artifact_entity.rb | 10 ++-- app/serializers/build_details_entity.rb | 8 ++-- app/serializers/ci/group_variable_entity.rb | 1 + app/serializers/diff_file_entity.rb | 17 ++++--- app/serializers/discussion_entity.rb | 8 +++- app/serializers/environment_serializer.rb | 54 ++++++++++++++++++++-- app/serializers/fork_namespace_entity.rb | 2 +- app/serializers/member_entity.rb | 2 + app/serializers/member_serializer.rb | 6 +++ .../merge_request_poll_cached_widget_entity.rb | 46 ++++++++++++++++-- .../merge_request_poll_widget_entity.rb | 47 ++++--------------- app/serializers/merge_request_widget_entity.rb | 2 +- app/serializers/merge_requests/pipeline_entity.rb | 4 +- app/serializers/note_entity.rb | 3 +- app/serializers/pipeline_details_entity.rb | 2 +- app/serializers/pipeline_serializer.rb | 1 - app/serializers/runner_entity.rb | 2 +- app/serializers/service_field_entity.rb | 29 ++++++++++-- 20 files changed, 175 insertions(+), 73 deletions(-) (limited to 'app/serializers') diff --git a/app/serializers/README.md b/app/serializers/README.md index 97e9625eb6f..d83c2061e0b 100644 --- a/app/serializers/README.md +++ b/app/serializers/README.md @@ -284,7 +284,7 @@ MyObjectSerializer.new.represent(object.present) entity requires `request.user` attribute, but the second one wants `request.current_user`. When it happens that these two entities are used in the same serialization request, you might need to pass both parameters to - the serializer, which is obviously not a perfect situation. + the serializer, which is not a perfect situation. When in doubt, pass only `current_user` and `project` if these are required. diff --git a/app/serializers/admin/user_entity.rb b/app/serializers/admin/user_entity.rb index 8908d610046..a5cf40a50b9 100644 --- a/app/serializers/admin/user_entity.rb +++ b/app/serializers/admin/user_entity.rb @@ -30,3 +30,5 @@ module Admin end end end + +Admin::UserEntity.prepend_if_ee('EE::Admin::UserEntity') diff --git a/app/serializers/build_artifact_entity.rb b/app/serializers/build_artifact_entity.rb index 7a030372591..3c2c548d049 100644 --- a/app/serializers/build_artifact_entity.rb +++ b/app/serializers/build_artifact_entity.rb @@ -15,17 +15,15 @@ class BuildArtifactEntity < Grape::Entity expose :path do |artifact| fast_download_project_job_artifacts_path( - artifact.project, + project, artifact.job, file_type: artifact.file_type ) end - expose :keep_path, if: -> (*) { artifact.expiring? } do |artifact| - fast_keep_project_job_artifacts_path(artifact.project, artifact.job) - end + private - expose :browse_path do |artifact| - fast_browse_project_job_artifacts_path(artifact.project, artifact.job) + def project + options[:project] || artifact.project end end diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb index 01a8a4ebea9..0ddcad4dcb9 100644 --- a/app/serializers/build_details_entity.rb +++ b/app/serializers/build_details_entity.rb @@ -28,15 +28,15 @@ class BuildDetailsEntity < JobEntity expose :artifact, if: -> (*) { can?(current_user, :read_job_artifacts, build) } do expose :download_path, if: -> (*) { build.locked_artifacts? || build.artifacts? } do |build| - download_project_job_artifacts_path(project, build) + fast_download_project_job_artifacts_path(project, build) end expose :browse_path, if: -> (*) { build.locked_artifacts? || build.browsable_artifacts? } do |build| - browse_project_job_artifacts_path(project, build) + fast_browse_project_job_artifacts_path(project, build) end expose :keep_path, if: -> (*) { (build.has_expired_locked_archive_artifacts? || build.has_expiring_archive_artifacts?) && can?(current_user, :update_build, build) } do |build| - keep_project_job_artifacts_path(project, build) + fast_keep_project_job_artifacts_path(project, build) end expose :expire_at, if: -> (*) { build.artifacts_expire_at.present? } do |build| @@ -99,7 +99,7 @@ class BuildDetailsEntity < JobEntity end expose :available do |build| - project.any_active_runners? + build.any_runners_available? end expose :settings_path, if: -> (*) { can_admin_build? } do |build| diff --git a/app/serializers/ci/group_variable_entity.rb b/app/serializers/ci/group_variable_entity.rb index e7d0a957082..30c8239541a 100644 --- a/app/serializers/ci/group_variable_entity.rb +++ b/app/serializers/ci/group_variable_entity.rb @@ -2,5 +2,6 @@ module Ci class GroupVariableEntity < Ci::BasicVariableEntity + expose :environment_scope end end diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb index 9865af1e116..b2a544e1125 100644 --- a/app/serializers/diff_file_entity.rb +++ b/app/serializers/diff_file_entity.rb @@ -54,7 +54,7 @@ class DiffFileEntity < DiffFileBaseEntity end # Used for inline diffs - expose :highlighted_diff_lines, using: DiffLineEntity, if: -> (diff_file, options) { inline_diff_view?(options, diff_file) && diff_file.text? } do |diff_file| + expose :highlighted_diff_lines, using: DiffLineEntity, if: -> (diff_file, options) { inline_diff_view?(options) && diff_file.text? } do |diff_file| file = conflict_file(options, diff_file) || diff_file file.diff_lines_for_serializer end @@ -68,7 +68,7 @@ class DiffFileEntity < DiffFileBaseEntity end # Used for parallel diffs - expose :parallel_diff_lines, using: DiffLineParallelEntity, if: -> (diff_file, options) { parallel_diff_view?(options, diff_file) && diff_file.text? } + expose :parallel_diff_lines, using: DiffLineParallelEntity, if: -> (diff_file, options) { parallel_diff_view?(options) && diff_file.text? } expose :code_navigation_path, if: -> (diff_file) { options[:code_navigation_path] } do |diff_file| options[:code_navigation_path].full_json_path_for(diff_file.new_path) @@ -76,14 +76,17 @@ class DiffFileEntity < DiffFileBaseEntity private - def parallel_diff_view?(options, diff_file) - # If we're not rendering inline, we must be rendering parallel - !inline_diff_view?(options, diff_file) + def parallel_diff_view?(options) + diff_view(options) == :parallel end - def inline_diff_view?(options, diff_file) + def inline_diff_view?(options) + diff_view(options) == :inline + end + + def diff_view(options) # If nothing is present, inline will be the default. - options.fetch(:diff_view, :inline).to_sym == :inline + options.fetch(:diff_view, :inline).to_sym end def conflict_file(options, diff_file) diff --git a/app/serializers/discussion_entity.rb b/app/serializers/discussion_entity.rb index bcf6b331192..0dbfe0f0772 100644 --- a/app/serializers/discussion_entity.rb +++ b/app/serializers/discussion_entity.rb @@ -2,7 +2,13 @@ class DiscussionEntity < BaseDiscussionEntity expose :notes do |discussion, opts| - request.note_entity.represent(discussion.notes, opts.merge(with_base_discussion: false)) + request.note_entity.represent( + discussion.notes, + opts.merge( + with_base_discussion: false, + discussion: discussion + ) + ) end expose :positions, if: -> (d, _) { display_merge_ref_discussions?(d) } do |discussion| diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb index 598ce5f9e4f..2bb9a7e7254 100644 --- a/app/serializers/environment_serializer.rb +++ b/app/serializers/environment_serializer.rb @@ -23,7 +23,7 @@ class EnvironmentSerializer < BaseSerializer latest: super(item.latest, opts) } end else - super(resource, opts) + super(batch_load(resource), opts) end end @@ -41,11 +41,59 @@ class EnvironmentSerializer < BaseSerializer # immediately. items = @paginator.paginate(items) if paginated? - environments = resource.where(id: items.map(&:last_id)).index_by(&:id) + environments = batch_load(resource.where(id: items.map(&:last_id))) + environments_by_id = environments.index_by(&:id) items.map do |item| - Item.new(item.folder, item.size, environments[item.last_id]) + Item.new(item.folder, item.size, environments_by_id[item.last_id]) end end + + def batch_load(resource) + resource = resource.preload(environment_associations) + + resource.all.tap do |environments| + environments.each do |environment| + # Batch loading the commits of the deployments + environment.last_deployment&.commit&.try(:lazy_author) + environment.upcoming_deployment&.commit&.try(:lazy_author) + end + end + end + + def environment_associations + { + last_deployment: deployment_associations, + upcoming_deployment: deployment_associations, + project: project_associations + } + end + + def deployment_associations + { + user: [], + cluster: [], + project: [], + deployable: { + user: [], + metadata: [], + pipeline: { + manual_actions: [], + scheduled_actions: [] + }, + project: project_associations + } + } + end + + def project_associations + { + project_feature: [], + route: [], + namespace: :route + } + end # rubocop: enable CodeReuse/ActiveRecord end + +EnvironmentSerializer.prepend_if_ee('EE::EnvironmentSerializer') diff --git a/app/serializers/fork_namespace_entity.rb b/app/serializers/fork_namespace_entity.rb index abfaf4be811..fc238fa3958 100644 --- a/app/serializers/fork_namespace_entity.rb +++ b/app/serializers/fork_namespace_entity.rb @@ -23,7 +23,7 @@ class ForkNamespaceEntity < Grape::Entity end expose :relative_path do |namespace| - polymorphic_path(namespace) + group_path(namespace) end expose :markdown_description do |namespace| diff --git a/app/serializers/member_entity.rb b/app/serializers/member_entity.rb index e8f2bb28d60..6cbdaeea5ea 100644 --- a/app/serializers/member_entity.rb +++ b/app/serializers/member_entity.rb @@ -36,6 +36,8 @@ class MemberEntity < Grape::Entity GroupEntity.represent(member.source, only: [:id, :full_name, :web_url]) end + expose :type + expose :valid_level_roles, as: :valid_roles expose :user, if: -> (member) { member.user.present? }, using: MemberUserEntity diff --git a/app/serializers/member_serializer.rb b/app/serializers/member_serializer.rb index b34d7f30a58..462f6be5d04 100644 --- a/app/serializers/member_serializer.rb +++ b/app/serializers/member_serializer.rb @@ -2,4 +2,10 @@ class MemberSerializer < BaseSerializer entity MemberEntity + + def represent(members, opts = {}) + Members::LastGroupOwnerAssigner.new(opts[:group], members).execute unless opts[:source].is_a?(Project) + + super(members, opts) + end end diff --git a/app/serializers/merge_request_poll_cached_widget_entity.rb b/app/serializers/merge_request_poll_cached_widget_entity.rb index 1db4ec37d4a..52f5b975656 100644 --- a/app/serializers/merge_request_poll_cached_widget_entity.rb +++ b/app/serializers/merge_request_poll_cached_widget_entity.rb @@ -46,18 +46,28 @@ class MergeRequestPollCachedWidgetEntity < IssuableEntity end end - expose :actual_head_pipeline, as: :pipeline, if: -> (mr, _) { - Feature.enabled?(:merge_request_cached_pipeline_serializer, mr.project) && presenter(mr).can_read_pipeline? - } do |merge_request, options| + expose :actual_head_pipeline, as: :pipeline, if: -> (mr, _) { presenter(mr).can_read_pipeline? } do |merge_request, options| MergeRequests::PipelineEntity.represent(merge_request.actual_head_pipeline, options) end + expose :merge_pipeline, if: ->(mr, _) { + Feature.enabled?(:merge_request_cached_merge_pipeline_serializer, mr.project, default_enabled: :yaml) && + mr.merged? && + can?(request.current_user, :read_pipeline, mr.target_project) + } do |merge_request, options| + MergeRequests::PipelineEntity.represent(merge_request.merge_pipeline, options) + end + # Paths # expose :target_branch_commits_path do |merge_request| presenter(merge_request).target_branch_commits_path end + expose :merge_request_widget_path do |merge_request| + widget_project_json_merge_request_path(merge_request.target_project, merge_request, format: :json) + end + expose :target_branch_tree_path do |merge_request| presenter(merge_request).target_branch_tree_path end @@ -104,6 +114,36 @@ class MergeRequestPollCachedWidgetEntity < IssuableEntity presenter(merge_request).api_unapprove_path end + expose :test_reports_path do |merge_request| + if merge_request.has_test_reports? + test_reports_project_merge_request_path(merge_request.project, merge_request, format: :json) + end + end + + expose :accessibility_report_path do |merge_request| + if merge_request.has_accessibility_reports? + accessibility_reports_project_merge_request_path(merge_request.project, merge_request, format: :json) + end + end + + expose :codequality_reports_path do |merge_request| + if merge_request.has_codequality_reports? + codequality_reports_project_merge_request_path(merge_request.project, merge_request, format: :json) + end + end + + expose :terraform_reports_path do |merge_request| + if merge_request.has_terraform_reports? + terraform_reports_project_merge_request_path(merge_request.project, merge_request, format: :json) + end + end + + expose :exposed_artifacts_path do |merge_request| + if merge_request.has_exposed_artifacts? + exposed_artifacts_project_merge_request_path(merge_request.project, merge_request, format: :json) + end + end + expose :blob_path do expose :head_path, if: -> (mr, _) { mr.source_branch_sha } do |merge_request| project_blob_path(merge_request.project, merge_request.source_branch_sha) diff --git a/app/serializers/merge_request_poll_widget_entity.rb b/app/serializers/merge_request_poll_widget_entity.rb index 4c34da3fc88..97a81d8170f 100644 --- a/app/serializers/merge_request_poll_widget_entity.rb +++ b/app/serializers/merge_request_poll_widget_entity.rb @@ -19,19 +19,22 @@ class MergeRequestPollWidgetEntity < Grape::Entity # User entities expose :merge_user, using: UserEntity - expose :actual_head_pipeline, as: :pipeline, if: -> (mr, _) { - Feature.disabled?(:merge_request_cached_pipeline_serializer, mr.project) && presenter(mr).can_read_pipeline? + expose :merge_pipeline, if: ->(mr, _) { + Feature.disabled?(:merge_request_cached_merge_pipeline_serializer, mr.project, default_enabled: :yaml) && + mr.merged? && + can?(request.current_user, :read_pipeline, mr.target_project) } do |merge_request, options| - MergeRequests::PipelineEntity.represent(merge_request.actual_head_pipeline, options) - end - - expose :merge_pipeline, if: ->(mr, _) { mr.merged? && can?(request.current_user, :read_pipeline, mr.target_project)} do |merge_request, options| MergeRequests::PipelineEntity.represent(merge_request.merge_pipeline, options) end expose :default_merge_commit_message - expose :mergeable?, as: :mergeable + expose :mergeable do |merge_request, options| + next merge_request.mergeable? if Feature.disabled?(:check_mergeability_async_in_widget, merge_request.project, default_enabled: :yaml) + next false if options[:async_mergeability_check].present? && merge_request.checking? + + merge_request.mergeable? + end expose :default_merge_commit_message_with_description do |merge_request| merge_request.default_merge_commit_message(include_description: true) @@ -73,36 +76,6 @@ class MergeRequestPollWidgetEntity < Grape::Entity presenter(merge_request).cancel_auto_merge_path end - expose :test_reports_path do |merge_request| - if merge_request.has_test_reports? - test_reports_project_merge_request_path(merge_request.project, merge_request, format: :json) - end - end - - expose :accessibility_report_path do |merge_request| - if merge_request.has_accessibility_reports? - accessibility_reports_project_merge_request_path(merge_request.project, merge_request, format: :json) - end - end - - expose :codequality_reports_path do |merge_request| - if merge_request.has_codequality_reports? - codequality_reports_project_merge_request_path(merge_request.project, merge_request, format: :json) - end - end - - expose :terraform_reports_path do |merge_request| - if merge_request.has_terraform_reports? - terraform_reports_project_merge_request_path(merge_request.project, merge_request, format: :json) - end - end - - expose :exposed_artifacts_path do |merge_request| - if merge_request.has_exposed_artifacts? - exposed_artifacts_project_merge_request_path(merge_request.project, merge_request, format: :json) - end - end - expose :create_issue_to_resolve_discussions_path do |merge_request| presenter(merge_request).create_issue_to_resolve_discussions_path end diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index 3ed7d9d8914..a168c7a8490 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -36,7 +36,7 @@ class MergeRequestWidgetEntity < Grape::Entity end expose :merge_request_widget_path do |merge_request| - widget_project_json_merge_request_path(merge_request.target_project, merge_request, format: :json) + widget_project_json_merge_request_path(merge_request.target_project, merge_request, async_mergeability_check: true, format: :json) end expose :merge_request_cached_widget_path do |merge_request| diff --git a/app/serializers/merge_requests/pipeline_entity.rb b/app/serializers/merge_requests/pipeline_entity.rb index c7caad0e62b..bdf5cb160b2 100644 --- a/app/serializers/merge_requests/pipeline_entity.rb +++ b/app/serializers/merge_requests/pipeline_entity.rb @@ -28,7 +28,7 @@ class MergeRequests::PipelineEntity < Grape::Entity rel = rel.select { |artifact| can?(request.current_user, :read_job_artifacts, artifact.job) } end - BuildArtifactEntity.represent(rel, options) + BuildArtifactEntity.represent(rel, options.merge(project: pipeline.project)) end expose :detailed_status, as: :status, with: DetailedStatusEntity do |pipeline| @@ -36,6 +36,8 @@ class MergeRequests::PipelineEntity < Grape::Entity end expose :stages, using: StageEntity + + expose :finished_at end # Coverage isn't always necessary (e.g. when displaying project pipelines in diff --git a/app/serializers/note_entity.rb b/app/serializers/note_entity.rb index 9a96778786b..d44958bc0c4 100644 --- a/app/serializers/note_entity.rb +++ b/app/serializers/note_entity.rb @@ -36,7 +36,8 @@ class NoteEntity < API::Entities::Note end expose :can_resolve_discussion do |note| - note.discussion.resolvable? && note.discussion.can_resolve?(current_user) + discussion = options.fetch(:discussion, nil) || note.discussion + discussion.resolvable? && discussion.can_resolve?(current_user) end end diff --git a/app/serializers/pipeline_details_entity.rb b/app/serializers/pipeline_details_entity.rb index 4fec543eca8..bb6aa2f78ac 100644 --- a/app/serializers/pipeline_details_entity.rb +++ b/app/serializers/pipeline_details_entity.rb @@ -15,7 +15,7 @@ class PipelineDetailsEntity < Ci::PipelineEntity rel = rel.select { |artifact| can?(request.current_user, :read_job_artifacts, artifact.job) } end - BuildArtifactEntity.represent(rel, options) + BuildArtifactEntity.represent(rel, options.merge(project: pipeline.project)) end expose :manual_actions, using: BuildActionEntity expose :scheduled_actions, using: BuildActionEntity diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb index 85887e64a8b..9a2e29a6ee3 100644 --- a/app/serializers/pipeline_serializer.rb +++ b/app/serializers/pipeline_serializer.rb @@ -41,7 +41,6 @@ class PipelineSerializer < BaseSerializer def preloaded_relations [ :cancelable_statuses, - :latest_statuses_ordered_by_stage, :retryable_builds, :stages, :latest_statuses, diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb index 97e5b336a35..6d6ba920a3b 100644 --- a/app/serializers/runner_entity.rb +++ b/app/serializers/runner_entity.rb @@ -3,7 +3,7 @@ class RunnerEntity < Grape::Entity include RequestAwareEntity - expose :id, :description + expose :id, :description, :short_sha expose :edit_path, if: -> (*) { can_edit_runner? } do |runner| edit_project_runner_path(project, runner) diff --git a/app/serializers/service_field_entity.rb b/app/serializers/service_field_entity.rb index 08e08ae187f..960e216906e 100644 --- a/app/serializers/service_field_entity.rb +++ b/app/serializers/service_field_entity.rb @@ -2,14 +2,22 @@ class ServiceFieldEntity < Grape::Entity include RequestAwareEntity + include Gitlab::Utils::StrongMemoize - expose :type, :name, :title, :placeholder, :required, :choices, :help + expose :type, :name, :placeholder, :required, :choices + + expose :title do |field| + non_empty_password?(field) ? field[:non_empty_password_title] : field[:title] + end + + expose :help do |field| + non_empty_password?(field) ? field[:non_empty_password_help] : field[:help] + end expose :value do |field| - # field[:name] is not user input and so can assume is safe - value = service.public_send(field[:name]) # rubocop:disable GitlabSecurity/PublicSend + value = value_for(field) - if field[:type] == 'password' && value.present? + if non_empty_password?(field) 'true' elsif field[:type] == 'checkbox' ActiveRecord::Type::Boolean.new.deserialize(value).to_s @@ -23,4 +31,17 @@ class ServiceFieldEntity < Grape::Entity def service request.service end + + def value_for(field) + strong_memoize(:value_for) do + # field[:name] is not user input and so can assume is safe + service.public_send(field[:name]) # rubocop:disable GitlabSecurity/PublicSend + end + end + + def non_empty_password?(field) + strong_memoize(:non_empty_password) do + field[:type] == 'password' && value_for(field).present? + end + end end -- cgit v1.2.3