diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb index 2bafcee0109f7c49f72ecac2cc19b5b12e028898..943c707218d0b3c48836f0341ef2a40ab48e1a7b 100644 --- a/app/serializers/deployment_entity.rb +++ b/app/serializers/deployment_entity.rb @@ -22,8 +22,18 @@ class DeploymentEntity < Grape::Entity expose :last? expose :user, using: UserEntity + expose :deployable do |deployment, opts| + deployment.deployable.yield_self do |deployable| + if include_details? + JobEntity.represent(deployable, opts) + elsif can_read_deployables? + { name: deployable.name, + build_path: project_job_path(deployable.project, deployable) } + end + end + end + expose :commit, using: CommitEntity, if: -> (*) { include_details? } - expose :deployable, using: JobEntity, if: -> (*) { include_details? } expose :manual_actions, using: JobEntity, if: -> (*) { include_details? && can_create_deployment? } expose :scheduled_actions, using: JobEntity, if: -> (*) { include_details? && can_create_deployment? } @@ -36,4 +46,13 @@ class DeploymentEntity < Grape::Entity def can_create_deployment? can?(request.current_user, :create_deployment, request.project) end + + def can_read_deployables? + ## + # We intentionally do not check `:read_build, deployment.deployable` + # because it triggers a policy evaluation that involves multiple + # Gitaly calls that might not be cached. + # + can?(request.current_user, :read_build, request.project) + end end diff --git a/spec/fixtures/api/schemas/environment.json b/spec/fixtures/api/schemas/environment.json index 9a10ab18c30306fb5767996b840536265baf1949..5b1e3c049fa6a1d26bdcad734f95507c63d88b9b 100644 --- a/spec/fixtures/api/schemas/environment.json +++ b/spec/fixtures/api/schemas/environment.json @@ -31,7 +31,11 @@ "last_deployment": { "oneOf": [ { "type": "null" }, - { "$ref": "deployment.json" } + { "$ref": "deployment.json" }, + { + "name": { "type": "string" }, + "build_path": { "type": "string" } + } ] } }, diff --git a/spec/serializers/build_details_entity_spec.rb b/spec/serializers/build_details_entity_spec.rb index 68cf428ed38da700436e81045008a8d51183eff6..9c2e5c79a9dee82d492586d7005cd78ec87f10ef 100644 --- a/spec/serializers/build_details_entity_spec.rb +++ b/spec/serializers/build_details_entity_spec.rb @@ -142,7 +142,7 @@ describe BuildDetailsEntity do response = subject.with_indifferent_access response.dig(:deployment_status, :environment, :last_deployment).tap do |deployment| - expect(deployment).not_to include(:commit, :deployable, :manual_actions, :scheduled_actions) + expect(deployment).not_to include(:commit, :manual_actions, :scheduled_actions) end end end diff --git a/spec/serializers/deployment_entity_spec.rb b/spec/serializers/deployment_entity_spec.rb index f0bbf90c6c67e494ba3046b2802d11b327f0172e..37614cc0b4ca8d03389c266f7bcfc887d4721708 100644 --- a/spec/serializers/deployment_entity_spec.rb +++ b/spec/serializers/deployment_entity_spec.rb @@ -93,13 +93,22 @@ describe DeploymentEntity do end context 'when deployment details serialization was disabled' do + include Gitlab::Routing + let(:entity) do described_class.new(deployment, request: request, deployment_details: false) end it 'does not serialize deployment details' do expect(subject.with_indifferent_access) - .not_to include(:commit, :deployable, :manual_actions, :scheduled_actions) + .not_to include(:commit, :manual_actions, :scheduled_actions) + end + + it 'only exposes deployable name and path' do + project_job_path(project, deployment.deployable).tap do |path| + expect(subject.fetch(:deployable)) + .to eq('name' => 'test', 'build_path' => path) + end end end end