Merge branch 'feature/view-related-serializers' into 'master'

Add serializer for environments

## What does this MR do?

This will make it possible to create a payload need in https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7015 for use with Vue.

## Why was this MR needed?

Closes #23886

See merge request !7174
This commit is contained in:
Douwe Maan 2016-11-04 15:53:38 +00:00
commit 5a0a4506d2
16 changed files with 343 additions and 0 deletions

View file

@ -0,0 +1,18 @@
class BaseSerializer
def initialize(parameters = {})
@request = EntityRequest.new(parameters)
end
def represent(resource, opts = {})
self.class.entity_class
.represent(resource, opts.merge(request: @request))
end
def self.entity(entity_class)
@entity_class ||= entity_class
end
def self.entity_class
@entity_class
end
end

View file

@ -0,0 +1,24 @@
class BuildEntity < Grape::Entity
include RequestAwareEntity
expose :id
expose :name
expose :build_url do |build|
url_to(:namespace_project_build, build)
end
expose :retry_url do |build|
url_to(:retry_namespace_project_build, build)
end
expose :play_url, if: ->(build, _) { build.manual? } do |build|
url_to(:play_namespace_project_build, build)
end
private
def url_to(route, build)
send("#{route}_url", build.project.namespace, build.project, build)
end
end

View file

@ -0,0 +1,12 @@
class CommitEntity < API::Entities::RepoCommit
include RequestAwareEntity
expose :author, using: UserEntity
expose :commit_url do |commit|
namespace_project_tree_url(
request.project.namespace,
request.project,
id: commit.id)
end
end

View file

@ -0,0 +1,27 @@
class DeploymentEntity < Grape::Entity
include RequestAwareEntity
expose :id
expose :iid
expose :sha
expose :ref do
expose :name do |deployment|
deployment.ref
end
expose :ref_url do |deployment|
namespace_project_tree_url(
deployment.project.namespace,
deployment.project,
id: deployment.ref)
end
end
expose :tag
expose :last?
expose :user, using: UserEntity
expose :commit, using: CommitEntity
expose :deployable, using: BuildEntity
expose :manual_actions, using: BuildEntity
end

View file

@ -0,0 +1,12 @@
class EntityRequest
# We use EntityRequest object to collect parameters and variables
# from the controller. Because options that are being passed to the entity
# do appear in each entity object in the chain, we need a way to pass data
# that is present in the controller (see #20045).
#
def initialize(parameters)
parameters.each do |key, value|
define_singleton_method(key) { value }
end
end
end

View file

@ -0,0 +1,20 @@
class EnvironmentEntity < Grape::Entity
include RequestAwareEntity
expose :id
expose :name
expose :state
expose :external_url
expose :environment_type
expose :last_deployment, using: DeploymentEntity
expose :stoppable?
expose :environment_url do |environment|
namespace_project_environment_url(
environment.project.namespace,
environment.project,
environment)
end
expose :created_at, :updated_at
end

View file

@ -0,0 +1,3 @@
class EnvironmentSerializer < BaseSerializer
entity EnvironmentEntity
end

View file

@ -0,0 +1,11 @@
module RequestAwareEntity
extend ActiveSupport::Concern
included do
include Gitlab::Routing.url_helpers
end
def request
@options.fetch(:request)
end
end

View file

@ -0,0 +1,2 @@
class UserEntity < API::Entities::UserBasic
end

View file

@ -0,0 +1,31 @@
require 'spec_helper'
describe BuildEntity do
let(:entity) do
described_class.new(build, request: double)
end
subject { entity.as_json }
context 'when build is a regular job' do
let(:build) { create(:ci_build) }
it 'contains url to build page and retry action' do
expect(subject).to include(:build_url, :retry_url)
expect(subject).not_to include(:play_url)
end
it 'does not contain sensitive information' do
expect(subject).not_to include(/token/)
expect(subject).not_to include(/variables/)
end
end
context 'when build is a manual action' do
let(:build) { create(:ci_build, :manual) }
it 'contains url to play action' do
expect(subject).to include(:play_url)
end
end
end

View file

@ -0,0 +1,44 @@
require 'spec_helper'
describe CommitEntity do
let(:entity) do
described_class.new(commit, request: request)
end
let(:request) { double('request') }
let(:project) { create(:project) }
let(:commit) { project.commit }
subject { entity.as_json }
before do
allow(request).to receive(:project).and_return(project)
end
context 'when commit author is a user' do
before do
create(:user, email: commit.author_email)
end
it 'contains information about user' do
expect(subject.fetch(:author)).not_to be_nil
end
end
context 'when commit author is not a user' do
it 'does not contain author details' do
expect(subject.fetch(:author)).to be_nil
end
end
it 'contains commit URL' do
expect(subject).to include(:commit_url)
end
it 'needs to receive project in the request' do
expect(request).to receive(:project)
.and_return(project)
subject
end
end

View file

@ -0,0 +1,20 @@
require 'spec_helper'
describe DeploymentEntity do
let(:entity) do
described_class.new(deployment, request: double)
end
let(:deployment) { create(:deployment) }
subject { entity.as_json }
it 'exposes internal deployment id' do
expect(subject).to include(:iid)
end
it 'exposes nested information about branch' do
expect(subject[:ref][:name]).to eq 'master'
expect(subject[:ref][:ref_url]).not_to be_empty
end
end

View file

@ -0,0 +1,18 @@
require 'spec_helper'
describe EntityRequest do
subject do
described_class.new(user: 'user', project: 'some project')
end
describe 'methods created' do
it 'defines accessible attributes' do
expect(subject.user).to eq 'user'
expect(subject.project).to eq 'some project'
end
it 'raises error when attribute is not defined' do
expect { subject.some_method }.to raise_error NoMethodError
end
end
end

View file

@ -0,0 +1,18 @@
require 'spec_helper'
describe EnvironmentEntity do
let(:entity) do
described_class.new(environment, request: double)
end
let(:environment) { create(:environment) }
subject { entity.as_json }
it 'exposes latest deployment' do
expect(subject).to include(:last_deployment)
end
it 'exposes core elements of environment' do
expect(subject).to include(:id, :name, :state, :environment_url)
end
end

View file

@ -0,0 +1,60 @@
require 'spec_helper'
describe EnvironmentSerializer do
let(:serializer) do
described_class
.new(user: user, project: project)
.represent(resource)
end
let(:json) { serializer.as_json }
let(:user) { create(:user) }
let(:project) { create(:project) }
context 'when there is a single object provided' do
before do
create(:ci_build, :manual, name: 'manual1',
pipeline: deployable.pipeline)
end
let(:deployment) do
create(:deployment, deployable: deployable,
user: user,
project: project,
sha: project.commit.id)
end
let(:deployable) { create(:ci_build) }
let(:resource) { deployment.environment }
it 'it generates payload for single object' do
expect(json).to be_an_instance_of Hash
end
it 'contains important elements of environment' do
expect(json)
.to include(:name, :external_url, :environment_url, :last_deployment)
end
it 'contains relevant information about last deployment' do
last_deployment = json.fetch(:last_deployment)
expect(last_deployment)
.to include(:ref, :user, :commit, :deployable, :manual_actions)
end
end
context 'when there is a collection of objects provided' do
let(:project) { create(:empty_project) }
let(:resource) { create_list(:environment, 2) }
it 'contains important elements of environment' do
expect(json.first)
.to include(:last_deployment, :name, :external_url)
end
it 'generates payload for collection' do
expect(json).to be_an_instance_of Array
end
end
end

View file

@ -0,0 +1,23 @@
require 'spec_helper'
describe UserEntity do
let(:entity) { described_class.new(user) }
let(:user) { create(:user) }
subject { entity.as_json }
it 'exposes user name and login' do
expect(subject).to include(:username, :name)
end
it 'does not expose passwords' do
expect(subject).not_to include(/password/)
end
it 'does not expose tokens' do
expect(subject).not_to include(/token/)
end
it 'does not expose 2FA OTPs' do
expect(subject).not_to include(/otp/)
end
end