summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRémy Coutable <remy@rymai.me>2017-02-08 12:43:58 +0000
committerRémy Coutable <remy@rymai.me>2017-02-08 12:43:58 +0000
commit0fc474291eb84a8a128baea743c54ec661c2d44b (patch)
treeb2ddf56e23ecdeb8ab3018272869961731ac9803
parent40704daad839b7a9744b8d9c9ebc82e56ac452a1 (diff)
parente217bb21a6c87b8599cb0ba1636928c2bd0ca814 (diff)
downloadgitlab-ce-0fc474291eb84a8a128baea743c54ec661c2d44b.tar.gz
Merge branch 'feature/gb/paginated-environments-api' into 'master'
Expose paginated environments list API endpoint See merge request !8928
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/serializers/environment_serializer.rb47
-rw-r--r--app/serializers/pipeline_serializer.rb40
-rw-r--r--lib/gitlab/serializer/ci/variables.rb (renamed from lib/gitlab/serialize/ci/variables.rb)2
-rw-r--r--lib/gitlab/serializer/pagination.rb36
-rw-r--r--spec/lib/gitlab/serializer/ci/variables_spec.rb (renamed from spec/lib/gitlab/serialize/ci/variables_spec.rb)2
-rw-r--r--spec/lib/gitlab/serializer/pagination_spec.rb49
-rw-r--r--spec/serializers/environment_serializer_spec.rb132
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb6
9 files changed, 282 insertions, 34 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5213ea9d02b..8c1b076c2d7 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -20,7 +20,7 @@ module Ci
end
serialize :options
- serialize :yaml_variables, Gitlab::Serialize::Ci::Variables
+ serialize :yaml_variables, Gitlab::Serializer::Ci::Variables
validates :coverage, numericality: true, allow_blank: true
validates_presence_of :ref
diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb
index 91955542f25..fe16a3784c4 100644
--- a/app/serializers/environment_serializer.rb
+++ b/app/serializers/environment_serializer.rb
@@ -1,3 +1,50 @@
class EnvironmentSerializer < BaseSerializer
+ Item = Struct.new(:name, :size, :latest)
+
entity EnvironmentEntity
+
+ def within_folders
+ tap { @itemize = true }
+ end
+
+ def with_pagination(request, response)
+ tap { @paginator = Gitlab::Serializer::Pagination.new(request, response) }
+ end
+
+ def itemized?
+ @itemize
+ end
+
+ def paginated?
+ @paginator.present?
+ end
+
+ def represent(resource, opts = {})
+ resource = @paginator.paginate(resource) if paginated?
+
+ if itemized?
+ itemize(resource).map do |item|
+ { name: item.name,
+ size: item.size,
+ latest: super(item.latest, opts) }
+ end
+ else
+ super(resource, opts)
+ end
+ end
+
+ private
+
+ def itemize(resource)
+ items = resource.group(:item_name).order('item_name ASC')
+ .pluck('COALESCE(environment_type, name) AS item_name',
+ 'COUNT(*) AS environments_count',
+ 'MAX(id) AS last_environment_id')
+
+ environments = resource.where(id: items.map(&:last)).index_by(&:id)
+
+ items.map do |name, size, id|
+ Item.new(name, size, environments[id])
+ end
+ end
end
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index b2de6c5832e..2bc6cf3266e 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -1,41 +1,25 @@
class PipelineSerializer < BaseSerializer
class InvalidResourceError < StandardError; end
- include API::Helpers::Pagination
- Struct.new('Pagination', :request, :response)
entity PipelineEntity
- def represent(resource, opts = {})
- if paginated?
- raise InvalidResourceError unless resource.respond_to?(:page)
-
- super(paginate(resource.includes(project: :namespace)), opts)
- else
- super(resource, opts)
- end
- end
-
- def paginated?
- defined?(@pagination)
- end
-
def with_pagination(request, response)
- tap { @pagination = Struct::Pagination.new(request, response) }
+ tap { @paginator = Gitlab::Serializer::Pagination.new(request, response) }
end
- private
-
- # Methods needed by `API::Helpers::Pagination`
- #
- def params
- @pagination.request.query_parameters
+ def paginated?
+ @paginator.present?
end
- def request
- @pagination.request
- end
+ def represent(resource, opts = {})
+ if resource.is_a?(ActiveRecord::Relation)
+ resource = resource.includes(project: :namespace)
+ end
- def header(header, value)
- @pagination.response.headers[header] = value
+ if paginated?
+ super(@paginator.paginate(resource), opts)
+ else
+ super(resource, opts)
+ end
end
end
diff --git a/lib/gitlab/serialize/ci/variables.rb b/lib/gitlab/serializer/ci/variables.rb
index 3a9443bfcd9..c059c454eac 100644
--- a/lib/gitlab/serialize/ci/variables.rb
+++ b/lib/gitlab/serializer/ci/variables.rb
@@ -1,5 +1,5 @@
module Gitlab
- module Serialize
+ module Serializer
module Ci
# This serializer could make sure our YAML variables' keys and values
# are always strings. This is more for legacy build data because
diff --git a/lib/gitlab/serializer/pagination.rb b/lib/gitlab/serializer/pagination.rb
new file mode 100644
index 00000000000..bf2c0acc729
--- /dev/null
+++ b/lib/gitlab/serializer/pagination.rb
@@ -0,0 +1,36 @@
+module Gitlab
+ module Serializer
+ class Pagination
+ class InvalidResourceError < StandardError; end
+ include ::API::Helpers::Pagination
+
+ def initialize(request, response)
+ @request = request
+ @response = response
+ end
+
+ def paginate(resource)
+ if resource.respond_to?(:page)
+ super(resource)
+ else
+ raise InvalidResourceError
+ end
+ end
+
+ private
+
+ # Methods needed by `API::Helpers::Pagination`
+ #
+
+ attr_reader :request
+
+ def params
+ @request.query_parameters
+ end
+
+ def header(header, value)
+ @response.headers[header] = value
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/serialize/ci/variables_spec.rb b/spec/lib/gitlab/serializer/ci/variables_spec.rb
index 7ea74da5252..b810c68ea03 100644
--- a/spec/lib/gitlab/serialize/ci/variables_spec.rb
+++ b/spec/lib/gitlab/serializer/ci/variables_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Serialize::Ci::Variables do
+describe Gitlab::Serializer::Ci::Variables do
subject do
described_class.load(described_class.dump(object))
end
diff --git a/spec/lib/gitlab/serializer/pagination_spec.rb b/spec/lib/gitlab/serializer/pagination_spec.rb
new file mode 100644
index 00000000000..519eb1b274f
--- /dev/null
+++ b/spec/lib/gitlab/serializer/pagination_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+describe Gitlab::Serializer::Pagination do
+ let(:request) { spy('request') }
+ let(:response) { spy('response') }
+ let(:headers) { spy('headers') }
+
+ before do
+ allow(request).to receive(:query_parameters)
+ .and_return(params)
+
+ allow(response).to receive(:headers)
+ .and_return(headers)
+ end
+
+ let(:pagination) { described_class.new(request, response) }
+
+ describe '#paginate' do
+ subject { pagination.paginate(resource) }
+
+ let(:resource) { User.all }
+ let(:params) { { page: 1, per_page: 2 } }
+
+ context 'when a multiple resources are present in relation' do
+ before { create_list(:user, 3) }
+
+ it 'correctly paginates the resource' do
+ expect(subject.count).to be 2
+ end
+
+ it 'appends relevant headers' do
+ expect(headers).to receive(:[]=).with('X-Total', '3')
+ expect(headers).to receive(:[]=).with('X-Total-Pages', '2')
+ expect(headers).to receive(:[]=).with('X-Per-Page', '2')
+
+ subject
+ end
+ end
+
+ context 'when an invalid resource is about to be paginated' do
+ let(:resource) { create(:user) }
+
+ it 'raises error' do
+ expect { subject }.to raise_error(
+ described_class::InvalidResourceError)
+ end
+ end
+ end
+end
diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb
index 3c37660885d..1b95f1ff198 100644
--- a/spec/serializers/environment_serializer_spec.rb
+++ b/spec/serializers/environment_serializer_spec.rb
@@ -52,4 +52,136 @@ describe EnvironmentSerializer do
expect(json).to be_an_instance_of Array
end
end
+
+ context 'when representing environments within folders' do
+ let(:serializer) do
+ described_class.new(project: project).within_folders
+ end
+
+ let(:resource) { Environment.all }
+
+ subject { serializer.represent(resource) }
+
+ context 'when there is a single environment' do
+ before { create(:environment, name: 'staging') }
+
+ it 'represents one standalone environment' do
+ expect(subject.count).to eq 1
+ expect(subject.first[:name]).to eq 'staging'
+ expect(subject.first[:size]).to eq 1
+ expect(subject.first[:latest][:name]).to eq 'staging'
+ end
+ end
+
+ context 'when there are multiple environments in folder' do
+ before do
+ create(:environment, name: 'staging/my-review-1')
+ create(:environment, name: 'staging/my-review-2')
+ end
+
+ it 'represents one item that is a folder' do
+ expect(subject.count).to eq 1
+ expect(subject.first[:name]).to eq 'staging'
+ expect(subject.first[:size]).to eq 2
+ expect(subject.first[:latest][:name]).to eq 'staging/my-review-2'
+ expect(subject.first[:latest][:environment_type]).to eq 'staging'
+ end
+ end
+
+ context 'when there are multiple folders and standalone environments' do
+ before do
+ create(:environment, name: 'staging/my-review-1')
+ create(:environment, name: 'staging/my-review-2')
+ create(:environment, name: 'production/my-review-3')
+ create(:environment, name: 'testing')
+ end
+
+ it 'represents multiple items grouped within folders' do
+ expect(subject.count).to eq 3
+
+ expect(subject.first[:name]).to eq 'production'
+ expect(subject.first[:size]).to eq 1
+ expect(subject.first[:latest][:name]).to eq 'production/my-review-3'
+ expect(subject.first[:latest][:environment_type]).to eq 'production'
+ expect(subject.second[:name]).to eq 'staging'
+ expect(subject.second[:size]).to eq 2
+ expect(subject.second[:latest][:name]).to eq 'staging/my-review-2'
+ expect(subject.second[:latest][:environment_type]).to eq 'staging'
+ expect(subject.third[:name]).to eq 'testing'
+ expect(subject.third[:size]).to eq 1
+ expect(subject.third[:latest][:name]).to eq 'testing'
+ expect(subject.third[:latest][:environment_type]).to be_nil
+ end
+ end
+ end
+
+ context 'when used with pagination' do
+ let(:request) { spy('request') }
+ let(:response) { spy('response') }
+ let(:resource) { Environment.all }
+ let(:pagination) { { page: 1, per_page: 2 } }
+
+ let(:serializer) do
+ described_class.new(project: project)
+ .with_pagination(request, response)
+ end
+
+ before do
+ allow(request).to receive(:query_parameters)
+ .and_return(pagination)
+ end
+
+ subject { serializer.represent(resource) }
+
+ it 'creates a paginated serializer' do
+ expect(serializer).to be_paginated
+ end
+
+ context 'when resource is paginatable relation' do
+ context 'when there is a single environment object in relation' do
+ before { create(:environment) }
+
+ it 'serializes environments' do
+ expect(subject.first).to have_key :id
+ end
+ end
+
+ context 'when multiple environment objects are serialized' do
+ before { create_list(:environment, 3) }
+
+ it 'serializes appropriate number of objects' do
+ expect(subject.count).to be 2
+ end
+
+ it 'appends relevant headers' do
+ expect(response).to receive(:[]=).with('X-Total', '3')
+ expect(response).to receive(:[]=).with('X-Total-Pages', '2')
+ expect(response).to receive(:[]=).with('X-Per-Page', '2')
+
+ subject
+ end
+ end
+
+ context 'when grouping environments within folders' do
+ let(:serializer) do
+ described_class.new(project: project)
+ .with_pagination(request, response)
+ .within_folders
+ end
+
+ before do
+ create(:environment, name: 'staging/review-1')
+ create(:environment, name: 'staging/review-2')
+ create(:environment, name: 'production/deploy-3')
+ create(:environment, name: 'testing')
+ end
+
+ it 'paginates grouped items including ordering' do
+ expect(subject.count).to eq 2
+ expect(subject.first[:name]).to eq 'production'
+ expect(subject.second[:name]).to eq 'staging'
+ end
+ end
+ end
+ end
end
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index 7cbf131e41e..2aaef03cb93 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -52,14 +52,14 @@ describe PipelineSerializer do
expect(serializer).to be_paginated
end
- context 'when resource does is not paginatable' do
+ context 'when resource is not paginatable' do
context 'when a single pipeline object is being serialized' do
let(:resource) { create(:ci_empty_pipeline) }
let(:pagination) { { page: 1, per_page: 1 } }
it 'raises error' do
- expect { subject }
- .to raise_error(PipelineSerializer::InvalidResourceError)
+ expect { subject }.to raise_error(
+ Gitlab::Serializer::Pagination::InvalidResourceError)
end
end
end