Skip to content

Commit

Permalink
Merge pull request #38 from acidtangodev/master
Browse files Browse the repository at this point in the history
Fix id mappings for included objects
  • Loading branch information
fran-worley committed Feb 7, 2019
2 parents 2727cee + 2389d02 commit deaf86f
Show file tree
Hide file tree
Showing 3 changed files with 276 additions and 8 deletions.
13 changes: 9 additions & 4 deletions lib/roar/json/json_api/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def call(options, mappings)

internal_options = {}
rewrite_include_option!(internal_options, include,
mappings.fetch(:id, :id))
mappings.fetch(:id, {}))
rewrite_fields!(internal_options, fields,
mappings.fetch(:relationships, {}))

Expand All @@ -30,14 +30,19 @@ def call(options, mappings)

private

def rewrite_include_option!(options, include, id_mapping)
def default_includes_for(name, id_mappings)
[id_mappings.fetch(name.to_s, :id).to_sym] + DEFAULT_INTERNAL_INCLUDES
end

def rewrite_include_option!(options, include, id_mappings)
include_paths = parse_include_option(include)
default_includes = [id_mapping.to_sym] + DEFAULT_INTERNAL_INCLUDES
default_includes = default_includes_for('_self', id_mappings)
options[:include] = default_includes + [:included]
options[:included] = { include: include_paths.map(&:first) - [:_self] }
include_paths.each do |include_path|
includes = default_includes_for(include_path.first, id_mappings)
options[:included].merge!(
explode_include_path(*include_path, default_includes)
explode_include_path(*include_path, includes)
)
end
options
Expand Down
15 changes: 12 additions & 3 deletions lib/roar/json/json_api/single_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,28 @@ def to_hash(options = {})
def mappings
@mappings ||= begin
mappings = {}
mappings[:id] = find_id_mapping
mappings[:id] = find_id_mappings
mappings[:relationships] = find_relationship_mappings
mappings[:relationships]['_self'] = self.class.type
mappings
end
end

def find_id_mapping
self.class.definitions.detect { |definition|
def find_id_mapping(klass)
self_id = klass.definitions.detect { |definition|
definition[:as] && definition[:as].(:value) == 'id'
}.name
end

def find_id_mappings
included_definitions = self.class.definitions['included'].representer_module.definitions
id_mappings = included_definitions.each_with_object({}) do |definition, hash|
hash[definition.name] = find_id_mapping(definition[:decorator])
end
id_mappings['_self'] = find_id_mapping(self.class)
id_mappings
end

def find_relationship_mappings
included_definitions = self.class.definitions['included'].representer_module.definitions
included_definitions.each_with_object({}) do |definition, hash|
Expand Down
256 changes: 255 additions & 1 deletion test/jsonapi/fieldsets_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require 'json'

class JSONAPIFieldsetsTest < Minitest::Spec
Article = Struct.new(:id, :title, :summary, :comments, :author)
Article = Struct.new(:id, :title, :summary, :comments, :author, :slug)
Comment = Struct.new(:id, :body, :good, :comment_author)
Author = Struct.new(:id, :name, :email)

Expand Down Expand Up @@ -290,4 +290,258 @@ class CollectionResourceObjectDecorator < Roar::Decorator
).must_equal_json document
end
end

describe 'Document with given id_key and nested resources with default id_key' do
class DocumentResourceWithDifferentIdAtRoot < Roar::Decorator
include Roar::JSON::JSONAPI.resource :articles, id_key: :article_id

attributes do
property :title
property :summary
end

has_many(:comments) do
attributes do
property :body
property :good
end
end
end

let(:comments) {
[
Comment.new('c:1', 'Cool!', true,
Author.new('a:2', 'Tim', 'troll@trollblazer.io')),
Comment.new('c:2', 'Nah', false)
]
}

let(:article) {
klass = Struct.new(:article_id, :title, :summary, :comments, :author)
klass.new(1, 'My Article', 'An interesting read.', comments,
Author.new('a:1', 'Celso', 'celsito@trb.to'))
}

let(:document) {
%({
"data": {
"id": "1",
"attributes": {
"summary": "An interesting read.",
"title": "My Article"
},
"type": "articles",
"relationships": {
"comments": {
"data": [
{
"id": "c:1",
"type": "comments"
},
{
"id": "c:2",
"type": "comments"
}
]
}
}
},
"included": [
{
"type": "comments",
"id": "c:1",
"attributes": {
"body": "Cool!",
"good": true
}
},
{
"type": "comments",
"id": "c:2",
"attributes": {
"body": "Nah",
"good": false
}
}
]
})
}

it do
DocumentResourceWithDifferentIdAtRoot.new(article).to_json(include: 'comments')
.must_equal_json document
end
end

describe 'Document with default id_key and nested resources with given id_key' do
class CommentDecorator < Roar::Decorator
include Roar::JSON::JSONAPI.resource :comments, id_key: :comment_id

attributes do
property :body
property :good
end
end

class DocumentResourceWithDifferentIdAtRelation < Roar::Decorator
include Roar::JSON::JSONAPI.resource :articles

attributes do
property :title
property :summary
end

has_many :comments, decorator: CommentDecorator
end

let(:comments) {
klass = Struct.new(:comment_id, :body, :good, :comment_author)
[
klass.new('c:1', 'Cool!', true,
Author.new('a:2', 'Tim', 'troll@trollblazer.io')),
klass.new('c:2', 'Nah', false)
]
}

let(:article) {
Article.new(1, 'My Article', 'An interesting read.', comments,
Author.new('a:1', 'Celso', 'celsito@trb.to'))
}

let(:document) {
%({
"data": {
"id": "1",
"attributes": {
"summary": "An interesting read.",
"title": "My Article"
},
"type": "articles",
"relationships": {
"comments": {
"data": [
{
"id": "c:1",
"type": "comments"
},
{
"id": "c:2",
"type": "comments"
}
]
}
}
},
"included": [
{
"type": "comments",
"id": "c:1",
"attributes": {
"body": "Cool!",
"good": true
}
},
{
"type": "comments",
"id": "c:2",
"attributes": {
"body": "Nah",
"good": false
}
}
]
})
}

it do
DocumentResourceWithDifferentIdAtRelation.new(article).to_json(include: 'comments')
.must_equal_json document
end
end

describe 'Document with given id_key and nested resources with given id_key' do
class CommentDecoratorWithId < Roar::Decorator
include Roar::JSON::JSONAPI.resource :comments, id_key: :comment_id

attributes do
property :body
property :good
end
end

class DocumentAndRelationWithDifferentId < Roar::Decorator
include Roar::JSON::JSONAPI.resource :articles, id_key: :slug

attributes do
property :title
property :summary
end

has_many :comments, decorator: CommentDecoratorWithId
end

let(:comments) {
klass = Struct.new(:comment_id, :body, :good, :comment_author)
[
klass.new('c:1', 'Cool!', true,
Author.new('a:2', 'Tim', 'troll@trollblazer.io')),
klass.new('c:2', 'Nah', false)
]
}

let(:article) {
Article.new(1, 'My Article', 'An interesting read.', comments,
Author.new('a:1', 'Celso', 'celsito@trb.to'), 'my-article')
}

let(:document) {
%({
"data": {
"id": "my-article",
"attributes": {
"summary": "An interesting read.",
"title": "My Article"
},
"type": "articles",
"relationships": {
"comments": {
"data": [
{
"id": "c:1",
"type": "comments"
},
{
"id": "c:2",
"type": "comments"
}
]
}
}
},
"included": [
{
"type": "comments",
"id": "c:1",
"attributes": {
"body": "Cool!",
"good": true
}
},
{
"type": "comments",
"id": "c:2",
"attributes": {
"body": "Nah",
"good": false
}
}
]
})
}

it do
DocumentAndRelationWithDifferentId.new(article).to_json(include: 'comments')
.must_equal_json document
end
end
end

0 comments on commit deaf86f

Please sign in to comment.