From acfe394018df50bfc08aa9e41265231747675646 Mon Sep 17 00:00:00 2001
From: Grzegorz Bizon <grzesiek.bizon@gmail.com>
Date: Fri, 28 Oct 2016 14:32:07 +0200
Subject: [PATCH] Add PoC for resource serializers

---
 app/serializers/base_serializer.rb            | 24 +++++++++++++
 app/serializers/entity_request.rb             | 16 +++++++++
 app/serializers/environment_entity.rb         | 17 +++++++++
 app/serializers/environment_serializer.rb     |  3 ++
 app/serializers/project_entity.rb             |  8 +++++
 app/serializers/request_aware_entity.rb       | 11 ++++++
 spec/serializers/entity_request_spec.rb       | 26 ++++++++++++++
 .../environment_serializer_spec.rb            | 35 +++++++++++++++++++
 8 files changed, 140 insertions(+)
 create mode 100644 app/serializers/base_serializer.rb
 create mode 100644 app/serializers/entity_request.rb
 create mode 100644 app/serializers/environment_entity.rb
 create mode 100644 app/serializers/environment_serializer.rb
 create mode 100644 app/serializers/project_entity.rb
 create mode 100644 app/serializers/request_aware_entity.rb
 create mode 100644 spec/serializers/entity_request_spec.rb
 create mode 100644 spec/serializers/environment_serializer_spec.rb

diff --git a/app/serializers/base_serializer.rb b/app/serializers/base_serializer.rb
new file mode 100644
index 00000000000..f9f7135551b
--- /dev/null
+++ b/app/serializers/base_serializer.rb
@@ -0,0 +1,24 @@
+class BaseSerializer
+  def initialize(request = {})
+    @request = EntityRequest.new(request)
+    @opts = { request: @request }
+  end
+
+  def set(opts)
+    @request.merge!(opts)
+    self
+  end
+
+  def represent(resource, opts = {})
+    self.class.entity_class
+      .represent(resource, @opts.reverse_merge(opts))
+  end
+
+  def self.entity(entity_class)
+    @entity_class ||= entity_class
+  end
+
+  def self.entity_class
+    @entity_class
+  end
+end
diff --git a/app/serializers/entity_request.rb b/app/serializers/entity_request.rb
new file mode 100644
index 00000000000..12ceb38b284
--- /dev/null
+++ b/app/serializers/entity_request.rb
@@ -0,0 +1,16 @@
+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)
+    merge!(parameters)
+  end
+
+  def merge!(parameters)
+    parameters.each do |key, value|
+      define_singleton_method(key) { value }
+    end
+  end
+end
diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb
new file mode 100644
index 00000000000..9415f1dd450
--- /dev/null
+++ b/app/serializers/environment_entity.rb
@@ -0,0 +1,17 @@
+class EnvironmentEntity < Grape::Entity
+  include RequestAwareEntity
+  include Gitlab::Routing.url_helpers
+
+  expose :id
+  expose :name
+  expose :project, with: ProjectEntity
+  expose :last_deployment,
+    as: :deployment,
+    using: API::Entities::Deployment
+
+  expose :environment_path
+
+  def environment_path
+    request.path
+  end
+end
diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb
new file mode 100644
index 00000000000..91955542f25
--- /dev/null
+++ b/app/serializers/environment_serializer.rb
@@ -0,0 +1,3 @@
+class EnvironmentSerializer < BaseSerializer
+  entity EnvironmentEntity
+end
diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb
new file mode 100644
index 00000000000..b8e23db470b
--- /dev/null
+++ b/app/serializers/project_entity.rb
@@ -0,0 +1,8 @@
+class ProjectEntity < Grape::Entity
+  expose :id
+  expose :name
+
+  expose :test do |project|
+    'something'
+  end
+end
diff --git a/app/serializers/request_aware_entity.rb b/app/serializers/request_aware_entity.rb
new file mode 100644
index 00000000000..f6b6f64d0f8
--- /dev/null
+++ b/app/serializers/request_aware_entity.rb
@@ -0,0 +1,11 @@
+module RequestAwareEntity
+  # We use SerializableRequest class to collect parameters and variables
+  # from the controller. Because options that are being passed to the entity
+  # are appear in each entity in the chain, we need a way to access data
+  # that is present in the controller (see  #20045).
+  #
+  def request
+    options[:request] ||
+      raise(StandardError, 'Request not set!!')
+  end
+end
diff --git a/spec/serializers/entity_request_spec.rb b/spec/serializers/entity_request_spec.rb
new file mode 100644
index 00000000000..1c220a7b95d
--- /dev/null
+++ b/spec/serializers/entity_request_spec.rb
@@ -0,0 +1,26 @@
+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
+
+  describe '#merge!' do
+    before { subject.merge!(build: 'some build') }
+
+    it 'appends parameters' do
+      expect(subject.build).to eq 'some build'
+    end
+  end
+end
diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb
new file mode 100644
index 00000000000..3470863681c
--- /dev/null
+++ b/spec/serializers/environment_serializer_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe EnvironmentSerializer do
+  let(:serializer) do
+    described_class.new(path: 'some path').represent(resource)
+  end
+
+  context 'when there is a single object provided' do
+    let(:resource) { create(:environment) }
+
+    it 'shows json' do
+      puts serializer.to_json
+    end
+
+    it 'it generates payload for single object' do
+      expect(parsed_json).to be_an_instance_of Hash
+    end
+  end
+
+  context 'when there is a collection of objects provided' do
+    let(:resource) { create_list(:environment, 2) }
+
+    it 'shows json' do
+      puts serializer.to_json
+    end
+
+    it 'generates payload for collection' do
+      expect(parsed_json).to be_an_instance_of Array
+    end
+  end
+
+  def parsed_json
+    JSON.parse(serializer.to_json)
+  end
+end
-- 
GitLab