From 1844c162f14e64adb20bda20371efc7ff844c9ef Mon Sep 17 00:00:00 2001 From: Leandro Cesquini Pereira Date: Fri, 24 Jul 2015 23:05:52 -0300 Subject: [PATCH] Adds support for top-level links to JsonApi adapter http://jsonapi.org/format/#document-top-level fix failing tests support for top-level links limited to jsonapi adapter Move docs from README to docs/ dir move links to json-api adapter & create Links class to hold links data --- .gitignore | 1 + CHANGELOG.md | 2 + docs/README.md | 1 + docs/howto/add_top_level_links.md | 31 ++++++ .../serializer/adapter/json_api/links.rb | 25 +++++ test/action_controller/serialization_test.rb | 34 ++++++ test/adapter/json_api/top_level_links_test.rb | 101 ++++++++++++++++++ 7 files changed, 195 insertions(+) create mode 100644 docs/howto/add_top_level_links.md create mode 100644 lib/active_model/serializer/adapter/json_api/links.rb create mode 100644 test/adapter/json_api/top_level_links_test.rb diff --git a/.gitignore b/.gitignore index cd2acb28..2bc7e6c8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ tmp .ruby-version .ruby-gemset vendor/bundle +tags diff --git a/CHANGELOG.md b/CHANGELOG.md index 721f42ac..fb9ae71b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,8 @@ Features: CollectionSerializer for clarity, add ActiveModelSerializers.config.collection_serializer (@bf4) - [#1295](https://github.com/rails-api/active_model_serializers/pull/1295) Add config `serializer_lookup_enabled` that, when disabled, requires serializers to explicitly specified. (@trek) +- [#1247](https://github.com/rails-api/active_model_serializers/pull/1247) Add top-level links (@beauby) + * Add more tests and docs for top-level links (@leandrocp) Fixes: diff --git a/docs/README.md b/docs/README.md index 7f0a8ac0..cf658fb6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,6 +23,7 @@ This is the documentation of ActiveModelSerializers, it's focused on the **0.10. - [How to add pagination links](howto/add_pagination_links.md) - [Using ActiveModelSerializers Outside Of Controllers](howto/outside_controller_use.md) - [Testing ActiveModelSerializers](howto/test.md) +- [How to add top-level links](howto/add_top_level_links.md) (```JSON-API``` only) ## Integrations diff --git a/docs/howto/add_top_level_links.md b/docs/howto/add_top_level_links.md new file mode 100644 index 00000000..a61775d2 --- /dev/null +++ b/docs/howto/add_top_level_links.md @@ -0,0 +1,31 @@ +# How to add top-level links + +JsonApi supports a [links object](http://jsonapi.org/format/#document-links) to be specified at top-level, that you can specify in the `render`: + +```ruby + render json: @posts, links: { "self": "http://example.com/api/posts" } +``` + +That's the result: + +```json +{ + "data": [ + { + "type": "posts", + "id": "1", + "attributes": { + "title": "JSON API is awesome!", + "body": "You should be using JSON API", + "created": "2015-05-22T14:56:29.000Z", + "updated": "2015-05-22T14:56:28.000Z" + } + } + ], + "links": { + "self": "http://example.com/api/posts" + } +} +``` + +This feature is specific to JsonApi, so you have to use the use the [JsonApi Adapter](https://github.com/rails-api/active_model_serializers/blob/master/docs/general/adapters.md#jsonapi) diff --git a/lib/active_model/serializer/adapter/json_api/links.rb b/lib/active_model/serializer/adapter/json_api/links.rb new file mode 100644 index 00000000..4e4c6127 --- /dev/null +++ b/lib/active_model/serializer/adapter/json_api/links.rb @@ -0,0 +1,25 @@ +module ActiveModel + class Serializer + class Adapter + class JsonApi < Adapter + class Links + def initialize(links = {}) + @links = links + end + + def serializable_hash + @links + end + + def update(links = {}) + @links.update(links) + end + + def present? + !@links.empty? + end + end + end + end + end +end diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb index d2fe3959..c6489b29 100644 --- a/test/action_controller/serialization_test.rb +++ b/test/action_controller/serialization_test.rb @@ -45,6 +45,17 @@ module ActionController render json: @profiles, meta: { total: 10 } end + def render_array_using_implicit_serializer_and_links + with_adapter ActiveModel::Serializer::Adapter::JsonApi do + + @profiles = [ + Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + ] + + render json: @profiles, links: { self: "http://example.com/api/profiles/1" } + end + end + def render_object_with_cache_enabled @comment = Comment.new(id: 1, body: 'ZOMG A COMMENT') @author = Author.new(id: 1, name: 'Joao Moura.') @@ -254,6 +265,29 @@ module ActionController assert_equal expected.to_json, @response.body end + def test_render_array_using_implicit_serializer_and_links + get :render_array_using_implicit_serializer_and_links + + expected = { + data: [ + { + id: assigns(:profiles).first.id.to_s, + type: "profiles", + attributes: { + name: "Name 1", + description: "Description 1" + } + } + ], + links: { + self: "http://example.com/api/profiles/1" + } + } + + assert_equal 'application/json', @response.content_type + assert_equal expected.to_json, @response.body + end + def test_render_with_cache_enable expected = { id: 1, diff --git a/test/adapter/json_api/top_level_links_test.rb b/test/adapter/json_api/top_level_links_test.rb new file mode 100644 index 00000000..3b849d5b --- /dev/null +++ b/test/adapter/json_api/top_level_links_test.rb @@ -0,0 +1,101 @@ +require 'test_helper' + +module ActiveModel + class Serializer + class Adapter + class JsonApi + class TopLevelLinksTest < Minitest::Test + URI = 'http://example.com' + + def setup + ActionController::Base.cache_store.clear + @blog = Blog.new(id: 1, + name: 'AMS Hints', + writer: Author.new(id: 2, name: "Steve"), + articles: [Post.new(id: 3, title: "AMS")]) + end + + def load_adapter(paginated_collection, options = {}) + options = options.merge(adapter: :json_api) + ActiveModel::SerializableResource.new(paginated_collection, options) + end + + def test_links_is_not_present_when_not_defined + adapter = load_adapter(@blog) + + expected = { + :data => { + :id => "1", + :type => "blogs", + :attributes => { + :name => "AMS Hints" + }, + :relationships => { + :writer=> {:data => {:type => "authors", :id => "2"}}, + :articles => {:data => [{:type => "posts", :id => "3"}]} + } + }} + + assert_equal expected, adapter.serializable_hash(@options) + end + + def test_links_is_present_when_defined + adapter = load_adapter(@blog, {links: links}) + + expected = { + :data => { + :id => "1", + :type => "blogs", + :attributes => { + :name => "AMS Hints" + }, + :relationships => { + :writer=> {:data => {:type => "authors", :id => "2"}}, + :articles => {:data => [{:type => "posts", :id => "3"}]} + } + }, + :links => {:self => "http://example.com/blogs/1"} + } + + assert_equal expected, adapter.serializable_hash(@options) + end + + def links + { + self: "#{URI}/blogs/1" + } + end + + # def test_links_is_not_present_when_not_declared + # serializer = AlternateBlogSerializer.new(@blog) + # adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer) + # expected = { + # data: { + # id: "1", + # type: "blogs", + # attributes: { + # title: "AMS Hints" + # } + # } + # } + # assert_equal expected, adapter.as_json + # end + + # def test_links_is_not_present_on_flattenjson_adapter + # serializer = AlternateBlogSerializer.new(@blog, :links => {:self => "/blogs/1"}) + # adapter = ActiveModel::Serializer::Adapter::FlattenJson.new(serializer) + # expected = {:id=>1, :title=>"AMS Hints"} + # assert_equal expected, adapter.as_json + # end + + # def test_links_is_not_present_on_json_adapter + # serializer = AlternateBlogSerializer.new(@blog, :links => {:self => "/blogs/1"}) + # adapter = ActiveModel::Serializer::Adapter::Json.new(serializer) + # expected = {:blog=>{:id=>1, :title=>"AMS Hints"}} + # assert_equal expected, adapter.as_json + # end + end + end + end + end +end