Mappers for Grape API

pull/1435/head
Marek Takac 11 years ago
parent 0a4f39be58
commit 6a40cdd6bf
  1. 1
      Gemfile
  2. 17
      Gemfile.lock
  3. 2
      app/api/api.rb
  4. 28
      app/api/projects/project_mapper.rb
  5. 52
      app/api/projects/projects.rb
  6. 6
      app/api/users/user_mapper.rb
  7. 24
      app/api/versions/version_representer.rb
  8. 59
      app/api/work_packages/work_package_mapper.rb
  9. 52
      app/api/work_packages/work_package_representer.rb
  10. 4
      app/api/work_packages/work_packages.rb
  11. 7
      app/policies/application_policy.rb
  12. 13
      app/policies/work_package_policy.rb

@ -185,6 +185,7 @@ gem 'pundit'
# API gems
gem 'grape', '~> 0.7.0'
gem 'roar', '~> 0.12.6'
gem 'yaks'
# Use the commented pure ruby gems, if you have not the needed prerequisites on
# board to compile the native ones. Note, that their use is discouraged, since

@ -61,6 +61,9 @@ GEM
multi_json (~> 1.0)
acts_as_list (0.2.0)
activerecord (>= 3.0)
adamantium (0.2.0)
ice_nine (~> 0.11.0)
memoizable (~> 0.4.0)
addressable (2.3.4)
arel (3.0.3)
awesome_nested_set (2.1.6)
@ -103,6 +106,9 @@ GEM
coffee-script-source (1.6.2)
color-tools (1.3.0)
columnize (0.3.6)
concord (0.1.5)
adamantium (~> 0.2.0)
equalizer (~> 0.0.9)
cucumber (1.3.8)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
@ -181,11 +187,13 @@ GEM
guard-test (1.0.0)
guard (>= 1.8)
test-unit (~> 2.2)
hamster (0.4.3)
hashie (2.1.1)
hike (1.2.3)
htmldiff (0.0.1)
i18n (0.6.5)
ice_nine (0.11.0)
inflection (1.0.0)
interception (0.3)
journey (1.0.4)
jquery-atwho-rails (0.4.7)
@ -212,6 +220,8 @@ GEM
mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
metaclass (0.0.1)
method_source (0.8.2)
mime-types (1.25.1)
@ -388,6 +398,7 @@ GEM
uglifier (2.1.1)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
uri_template (0.6.0)
virtus (1.0.2)
axiom-types (~> 0.1)
coercible (~> 1.0)
@ -397,6 +408,11 @@ GEM
will_paginate (3.0.5)
xpath (2.0.0)
nokogiri (~> 1.3)
yaks (0.2.0)
concord (~> 0.1.4)
hamster (~> 0.4.3)
inflection (~> 1.0.0)
uri_template (~> 0.6.0)
yard (0.8.7.2)
PLATFORMS
@ -486,3 +502,4 @@ DEPENDENCIES
timecop (~> 0.6.1)
uglifier (>= 1.0.3)
will_paginate (~> 3.0)
yaks

@ -40,6 +40,8 @@ class API < Grape::API
"search"
end
mount Projects::API
mount WorkPackages::API
mount Users::API
end

@ -0,0 +1,28 @@
module Projects
class ProjectMapper < Yaks::Mapper
link :self, '/api/v3/projects/{id}'
link :root, '/api/v3'
link :createChildren, '/api/v3/projects?parent_id={id}', method: :post
link :update, '/api/v3/projects/{id}', method: :patch
link :delete, '/api/v3/projects/{id}', method: :delete
link :version, '/api/v3/versions?filter[project_id]={id}'
link :responsible, '/api/v3/users/{responsible_id}'
link :members, '/api/v3/projects/{id}/members'
link :workPackages, '/api/v3/work_packages?filter[project_id]={id}'
link :createWorkPackages, '/api/v3/work_packages', method: :post
link :possibleResponsibles, '/api/v3/projects/{id}/possible_responsibles'
attributes :id, :name, :description, :createdAt, :updatedAt, :summary
has_one :responsible, mapper: Users::UserMapper
has_many :users, mapper: Users::UserMapper, as: :members
def createdAt
object.created_on.strftime("at %I:%M%p")
end
def updatedAt
object.updated_on.strftime("at %I:%M%p")
end
end
end

@ -0,0 +1,52 @@
module Projects
class API < Grape::API
resources :projects do
get do
projects = Project.all
projects_array = []
projects.each do |project|
resource = ProjectMapper.new(project).to_resource
projects_array << Yaks::HalSerializer.new(resource).serialize
end
json = {
_collection: projects_array,
_type: "Project",
_links: {
root: { href: '/api/v3' },
self: { href: '/api/v3/projects' },
next: { href: '/api/v3/projects/page=3' },
previous: { href: '/api/v3/projects/page=1' },
first: { href: '/api/v3/projects/page=1' },
last: { href: '/api/v3/projects/page=102' },
create: { href: '/api/v3/projects,', method: 'post' },
batchUpdate: { href: '/api/v3/projects', method: 'delete' },
batchDelete: { href: '/api/v3/projects?{ids}', method: 'delete' },
},
_count: projects_array.count,
_total: Project.count
}.to_json
end
get ':id' do
project = Project.find(params[:id])
resource = ProjectMapper.new(project).to_resource
Yaks::HalSerializer.new(resource).serialize.to_json
end
patch ':id' do
end
delete :':id' do
end
patch do
end
delete do
end
end
end
end

@ -0,0 +1,6 @@
module Users
class UserMapper < Yaks::Mapper
attributes :id, :login, :firstname, :lastname, :mail
end
end

@ -0,0 +1,24 @@
require 'roar/decorator'
require 'roar/representer/json'
require 'roar/representer/json/hal'
module Versions
class VersionRepresenter < Roar::Decorator
include Roar::Representer::JSON
include Roar::Representer::Feature::Hypermedia
include Roar::Representer::JSON::HAL
property :id
property :name
property :description
property :effective_date, getter: lambda { |args| effective_date.to_time.to_i }
property :created_on, getter: lambda { |args| created_on.to_time.to_i }
property :updated_on, getter: lambda { |args| updated_on.to_time.to_i }
property :woki_page_title
property :status
link :self do
"/versions/#{represented.id}"
end
end
end

@ -0,0 +1,59 @@
module WorkPackages
class WorkPackageMapper < Yaks::Mapper
link :self, '/api/v3/work_packages/{id}'
attributes :id, :subject, :description, :type, :dueDate, :status, :priority, :percentageDone,
:estimatedTime, :startDate, :createdAt, :updatedAt, :customFields, :_type
# has_one :responsible, mapper: Users::UserMapper
# has_many :users, mapper: Users::UserMapper, as: :members
def type
object.type.name
end
def dueDate
object.due_date.to_s
end
def status
object.status.name
end
def priority
object.priority.name
end
def percentageDone
object.done_ratio
end
def estimatedTime
{ unit: 'hours', value: object.estimated_hours }
end
def startDate
object.start_date.to_s
end
def createdAt
object.created_at.to_s
end
def updatedAt
object.updated_at.to_s
end
def customFields
fields = [ ]
object.custom_field_values.each do |custom_value|
fields << { name: custom_value.custom_field.name, format: custom_value.custom_field.field_format, value: custom_value.value }
end
fields
end
def _type
"WorkPackage"
end
end
end

@ -1,52 +0,0 @@
require 'roar/decorator'
require 'roar/representer/json'
require 'roar/representer/json/hal'
module WorkPackages
class WorkPackageRepresenter < Roar::Decorator
include Roar::Representer::JSON
include Roar::Representer::Feature::Hypermedia
include Roar::Representer::JSON::HAL
property :id
property :subject
property :description
property :type, getter: lambda { |arg| type.try(:name) }
property :due_date
property :category, getter: lambda { |arg| category.try(:name) }
property :status, getter: lambda { |arg| status.try(:name) }
property :priority, getter: lambda { |arg| priority.try(:name) }
property :fixed_version, getter: lambda { |arg| fixed_version.try(:name) }
property :lock_version
property :done_ratio
property :estimated_hours
property :start_date
property :created_at
property :updated_at
property :_links, exec_context: :decorator
def _links
{
root: { href: "/", title: 'OpenProject API entry point' },
self: { href: "/work_packages/#{represented.id}", title: "represented.subject" },
work_packages: { href: "/work_packages", title: "Work packages" },
project_work_packages: { href: "/projects/#{represented.project.identifier}/work_packages", title: "#{represented.project.name} - Work packages" },
descendants: { href: "/projects/#{represented.project.identifier}/work_packages?filter[ancestor_id]=#{represented.id}", title: "#{represented.subject} - Descendant work packages" },
children: { href: "/projects/#{represented.project.identifier}/work_packages?filter[parent_id]=#{represented.id}", title: "#{represented.subject} - Children work packages" },
create: { href: "/projects/#{represented.project.identifier}/work_packages", method: :post, title: "#{represented.project.name} - Create new work package" },
update: { href: "/work_packages/#{represented.id}", method: :patch, title: "Update #{represented.subject}" },
delete: { href: "/work_packages/#{represented.id}", method: :delete, title: "Delete #{represented.subject}" },
project: { href: "/projects/#{represented.project.identifier}", title: "#{represented.project}" },
author: { href: "/users/#{represented.author_id}", title: "?" },
assignee: { href: "/users/#{represented.assigned_to_id}", title: "?" },
responsible: { href: "/users/#{represented.responsible_id}", title: "?" }
}
end
property :project, class: Project, decorator: Projects::ProjectRepresenter, embedded: true
property :author, class: User, decorator: Users::UserRepresenter, embedded: true
property :assigned_to, as: :assignee, class: User, decorator: Users::UserRepresenter, embedded: true
property :responsible, class: User, decorator: Users::UserRepresenter, embedded: true
end
end

@ -10,14 +10,14 @@ module WorkPackages
optional :extend, type: String
end
get do
binding.pry
work_packages = WorkPackage.all
end
get ':id' do
work_package = WorkPackage.find(params[:id])
authorize work_package, :show?
WorkPackageRepresenter.new(work_package).to_json
resource = WorkPackageMapper.new(work_package).to_resource
Yaks::HalSerializer.new(resource).serialize.to_json
end
patch ':id' do

@ -0,0 +1,7 @@
class ApplicationPolicy
def initialize(user, record)
raise Pundit::NotAuthorizedError, "must be logged in" unless user
@user = user
@record = record
end
end

@ -1,19 +1,12 @@
class WorkPackagePolicy
attr_reader :user, :work_package
def initialize(user, work_package)
@user = user
@work_package = work_package
end
class WorkPackagePolicy < ApplicationPolicy
def index?
end
# if project is public
# ...
def show?
user.admin? ||
work_package.project.is_public?
@user.admin? ||
@record.project.is_public?
end
def create?

Loading…
Cancel
Save