diff --git a/Gemfile b/Gemfile index e1a769d1a2..3adcc443ca 100644 --- a/Gemfile +++ b/Gemfile @@ -186,7 +186,7 @@ gem 'pundit' # API gems gem 'grape', '~> 0.7.0' gem 'roar', '~> 0.12.6' -gem 'yaks' +gem 'reform' # 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 diff --git a/Gemfile.lock b/Gemfile.lock index e1b8adb509..54a01200ae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,9 +61,6 @@ 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) @@ -106,9 +103,6 @@ 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) @@ -142,6 +136,9 @@ GEM descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) diff-lcs (1.2.5) + disposable (0.0.3) + representable (~> 1.8.1) + uber equalizer (0.0.9) erubis (2.7.0) eventmachine (1.0.3) @@ -187,13 +184,11 @@ 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) @@ -220,8 +215,6 @@ 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) @@ -309,6 +302,11 @@ GEM rdoc (3.12.2) json (~> 1.4) ref (1.0.5) + reform (1.0.0) + activemodel + disposable (~> 0.0.3) + representable (~> 1.8.1) + uber (~> 0.0.4) representable (1.8.1) multi_json nokogiri @@ -398,7 +396,6 @@ 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) @@ -408,11 +405,6 @@ 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 @@ -479,6 +471,7 @@ DEPENDENCIES rb-fsevent rb-readline rdoc (>= 2.4.2) + reform request_store roar (~> 0.12.6) rspec (~> 2.14) @@ -503,4 +496,3 @@ DEPENDENCIES timecop (~> 0.6.1) uglifier (>= 1.0.3) will_paginate (~> 3.0) - yaks diff --git a/app/api/api.rb b/app/api/api.rb index ee2aff7e09..5223975274 100644 --- a/app/api/api.rb +++ b/app/api/api.rb @@ -52,5 +52,4 @@ class API < Grape::API mount Projects::API mount WorkPackages::API mount Users::API - end diff --git a/app/api/work_packages/relation_mapper.rb b/app/api/work_packages/relation_mapper.rb deleted file mode 100644 index b911348baa..0000000000 --- a/app/api/work_packages/relation_mapper.rb +++ /dev/null @@ -1,11 +0,0 @@ -module WorkPackages - class RelationMapper < Yaks::Mapper - link :self, '/api/v3/work_packages/{work_package_id}/relations' - - attributes :id - - def work_package_id - 1 - end - end -end diff --git a/app/api/work_packages/user_representer.rb b/app/api/work_packages/user_representer.rb new file mode 100644 index 0000000000..5addddebbc --- /dev/null +++ b/app/api/work_packages/user_representer.rb @@ -0,0 +1,26 @@ +require 'roar/representer/json' +require 'roar/decorator' +require 'roar/representer/json/hal' + +module WorkPackages + class UserRepresenter < Roar::Decorator + include Roar::Representer::JSON + include Roar::Representer::JSON::HAL + include Roar::Representer::Feature::Hypermedia + include Rails.application.routes.url_helpers + + property :login + property :firstname + property :lastname + property :mail + property :_type, exec_context: :decorator + + link :self do + { href: "http://localhost:3000/api/v3/users/#{represented.id}", title: "User." } + end + + def _type + "User" + end + end +end diff --git a/app/api/work_packages/work_package_mapper.rb b/app/api/work_packages/work_package_mapper.rb deleted file mode 100644 index ed5f151c65..0000000000 --- a/app/api/work_packages/work_package_mapper.rb +++ /dev/null @@ -1,90 +0,0 @@ -module WorkPackages - class WorkPackageMapper < Yaks::Mapper - def initialize(object, embedded) - super(object) - end - - link :self, '/api/v3/work_packages/{id}' - link :createChildren, '/api/v3/work_packages?parent_id={id}', method: :post - link :update, '/api/v3/work_packages/{id}', method: :patch - link :delete, '/api/v3/work_packages/{id}', method: :delete - link :project, '/api/v3/projects/{project_id}' - link :author, '/api/v3/users/{author_id}' - link :assignee, '/api/v3/users/{assigned_to_id}' - link :responsible, '/api/v3/users/{responsible_id}' - link :targetVersion, '/api/v3/versions/{fixed_version_id}' - link :projectWorkPackages, '/api/v3/work_packages?filter=project_ideql{project_id}' - link :descendants, '/api/v3/work_packages?filter=ancestors_idscontain{id}' - link :children, '/api/v3/work_packages?filter=parent_ideql{id}' - link :parent, '/api/v3/work_packages?filter=children_idscontain{id}' - link :ancestors, '/api/v3/work_packages?filter=descendants_idscontain{id}' - link :relations, '/api/v3/work_packages/{id}/relations' - - attributes :id, :subject, :description, :type, :dueDate, :status, :priority, :percentageDone, - :estimatedTime, :startDate, :createdAt, :updatedAt, :customFields, :_type - - has_one :project, mapper: Projects::ProjectMapper - has_one :author, mapper: Users::UserMapper - has_one :assigned_to, mapper: Users::UserMapper, as: :assignee - has_one :responsible, mapper: Users::UserMapper - has_one :fixed_version, mapper: Versions::VersionMapper, as: :targetVersion - has_one :parent, mapper: WorkPackages::WorkPackageMapper - - has_many :descendants, mapper: WorkPackages::WorkPackageMapper - has_many :ancestors, mapper: WorkPackages::WorkPackageMapper - has_many :children, mapper: WorkPackages::WorkPackageMapper - has_many :relations, mapper: WorkPackages::RelationMapper - - 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 - - def relations - [] - end - end -end diff --git a/app/api/work_packages/work_package_model.rb b/app/api/work_packages/work_package_model.rb new file mode 100644 index 0000000000..ffe217a966 --- /dev/null +++ b/app/api/work_packages/work_package_model.rb @@ -0,0 +1,80 @@ +module WorkPackages + class WorkPackageModel < Reform::Form + include Composition + + model :work_package + + property :subject, on: :work_package + property :description, on: :work_package + property :due_date, on: :work_package + property :percentage_done, as: :done_ratio, on: :work_package + property :start_date, on: :work_package + property :created_at, on: :work_package + property :updated_at, on: :work_package + property :author, on: :work_package + + def type + work_package.type.name + end + + def type=(value) + type = Type.find(:first, conditions: ['name ilike ?', value]) + work_package.type = type + end + + def status + work_package.status.name + end + + def status=(value) + status = Status.find(:first, conditions: ['name ilike ?', value]) + work_package.status = status + end + + def priority + work_package.priority.name + end + + def priority=(value) + priority = IssuePriority.find(:first, conditions: ['name ilike ?', value]) + work_package.priority = priority + end + + def responsible_login + work_package.responsible.try(:login) + end + + def responsible_login=(value) + responsible_user = User.find(:first, conditions: ['login ilike ?', value]) + work_package.responsible = responsible_user + end + + def responsible + work_package.responsible + end + + def assignee_login + work_package.assigned_to.try(:login) + end + + def assignee_login=(value) + assignee_user = User.find(:first, conditions: ['login ilike ?', value]) + work_package.assigned_to = assignee_user + end + + def assignee + work_package.assigned_to + end + + def estimated_time + { units: 'hours', value: work_package.estimated_hours } + end + + def estimated_time=(value) + hours = ActiveSupport::JSON.decode(value)['value'] + work_package.estimated_hours = hours + end + + validates :subject, presence: true + end +end diff --git a/app/api/work_packages/work_package_representer.rb b/app/api/work_packages/work_package_representer.rb index f702c2c766..b56f25be7f 100644 --- a/app/api/work_packages/work_package_representer.rb +++ b/app/api/work_packages/work_package_representer.rb @@ -2,48 +2,47 @@ require 'roar/representer/json' require 'roar/decorator' require 'roar/representer/json/hal' -class WorkPackageRepresenter < Roar::Decorator - include Roar::Representer::JSON - include Roar::Representer::JSON::HAL - include Roar::Representer::Feature::Hypermedia - include Rails.application.routes.url_helpers +module WorkPackages + class WorkPackageRepresenter < Roar::Decorator + include Roar::Representer::JSON + include Roar::Representer::JSON::HAL + include Roar::Representer::Feature::Hypermedia + include Rails.application.routes.url_helpers - property :id - property :subject - property :description - property :type, getter: lambda { |*| self.type.try(:name) } - property :due_date, as: :dueDate, getter: lambda { |*| self.due_date.try(:to_s) } - property :status, getter: lambda { |*| self.status.try(:name) } - property :priority, getter: lambda { |*| self.priority.try(:name) } - property :done_ratio, as: :percentageDone - property :estimated_time, as: :estimatedTime, exec_context: :decorator - property :start_date, as: :startDate, getter: lambda { |*| self.start_date.try(:to_s) } - property :created_at, as: :createdAt, getter: lambda { |*| self.created_at.try(:to_s) } - property :updated_at, as: :updatedAt, getter: lambda { |*| self.updated_at.try(:to_s) } - property :custom_fields, as: :customFields, exec_context: :decorator - property :_type, exec_context: :decorator - property :_links, exec_context: :decorator + property :_type, exec_context: :decorator - def estimated_time - { units: :hours, value: represented.estimated_hours} - end - - def custom_fields - fields = [] - represented.custom_field_values.each do |value| - fields << { name: value.custom_field.name, format: value.custom_field.field_format, value: value.value } + link :self do + { href: "http://localhost:3000/api/v3/work_packages/#{represented.work_package.id}", title: "Work package" } end - fields - end - def _type - "WorkPackage" - end + property :id, getter: lambda { |*| work_package.id } + property :subject + property :type + property :description + property :status + property :priority + property :start_date + property :due_date + property :estimated_time + property :percentage_done + property :project_id, getter: lambda { |*| work_package.project.id } + property :project_name, getter: lambda { |*| work_package.project.name } + property :responsible_id, getter: lambda { |*| work_package.responsible.try(:id) }, render_nil: true + property :responsible_name, getter: lambda { |*| work_package.responsible.try(:name) }, render_nil: true + property :responsible_login, getter: lambda { |*| work_package.responsible.try(:login) }, render_nil: true + property :responsible_mail, getter: lambda { |*| work_package.responsible.try(:mail) }, render_nil: true + property :assignee_id, getter: lambda { |*| work_package.assigned_to.try(:id) }, render_nil: true + property :assignee_name, getter: lambda { |*| work_package.assigned_to.try(:name) }, render_nil: true + property :assignee_login, getter: lambda { |*| work_package.assigned_to.try(:login) }, render_nil: true + property :assignee_mail, getter: lambda { |*| work_package.assigned_to.try(:mail) }, render_nil: true + property :author_name, getter: lambda { |*| work_package.author.name } + property :author_login, getter: lambda { |*| work_package.author.login } + property :author_mail, getter: lambda { |*| work_package.author.mail } + property :created_at + property :updated_at - def _links - { - self: { href: api_v3_work_package_path(represented.id) }, - update: { href: api_v3_work_package_path(represented.id), method: :put } - } + def _type + "WorkPackage" + end end end diff --git a/app/api/work_packages/work_packages.rb b/app/api/work_packages/work_packages.rb index 98909374c4..1b8442e507 100644 --- a/app/api/work_packages/work_packages.rb +++ b/app/api/work_packages/work_packages.rb @@ -29,7 +29,9 @@ module WorkPackages end get do - 'show a work package' + work_package_model = WorkPackageModel.new(work_package: @work_package) + work_package_representer = WorkPackageRepresenter.new(work_package_model) + work_package_representer.to_json end put do @@ -37,7 +39,16 @@ module WorkPackages end patch do - @work_package + work_package_model = WorkPackageModel.new(work_package: @work_package) + work_package_representer = WorkPackageRepresenter.new(work_package_model) + work_package_representer.from_json(request.POST.to_json) + + if work_package_representer.represented.valid? + work_package_representer.represented.save! + work_package_representer.to_json + else + work_package_representer.represented.errors.to_json + end end delete do