diff --git a/jsonapi-rails.gemspec b/jsonapi-rails.gemspec index 656a00b..3a38e72 100644 --- a/jsonapi-rails.gemspec +++ b/jsonapi-rails.gemspec @@ -20,5 +20,4 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'sqlite3' spec.add_development_dependency 'rake', '~> 11.3' spec.add_development_dependency 'rspec-rails', '~> 3.5' - spec.add_development_dependency 'dry-validation', '~> 0.10' end diff --git a/lib/jsonapi/rails/action_controller.rb b/lib/jsonapi/rails/action_controller.rb index d3b1aab..856e2de 100644 --- a/lib/jsonapi/rails/action_controller.rb +++ b/lib/jsonapi/rails/action_controller.rb @@ -33,8 +33,6 @@ def _deserializable(key, options, fallback, &block) end end - private - def jsonapi_pointers request.env[JSONAPI_POINTERS_KEY] end diff --git a/spec/action_controller_spec.rb b/spec/action_controller_spec.rb index bd608cd..7c3842f 100644 --- a/spec/action_controller_spec.rb +++ b/spec/action_controller_spec.rb @@ -1,17 +1,72 @@ require 'rails_helper' -RSpec.describe ActionController::Base do - it 'exposes the deserialization mapping via the jsonapi_pointers method' do - pointers = { id: '/data/id', type: '/data/type' } - - allow(subject).to receive(:request) do - OpenStruct.new( - env: { - JSONAPI::Rails::ActionController::JSONAPI_POINTERS_KEY => pointers +describe ActionController::Base, type: :controller do + describe '.deserializable_resource' do + controller do + deserializable_resource :user + + def create + render plain: 'ok' + end + end + + let(:payload) do + { + _jsonapi: { + 'data' => { + 'type' => 'users', + 'attributes' => { 'foo' => 'bar', 'bar' => 'baz' } + } + } + } + end + + it 'makes the deserialized resource available in params' do + post :create, params: payload + + expected = { 'type' => 'users', 'foo' => 'bar', 'bar' => 'baz' } + expect(controller.params[:user]).to eq(expected) + end + + it 'makes the deserialization mapping available via #jsonapi_pointers' do + post :create, params: payload + + expected = { foo: '/data/attributes/foo', + bar: '/data/attributes/bar', + type: '/data/type' } + expect(controller.jsonapi_pointers).to eq(expected) + end + end + + describe '#render jsonapi:' do + controller do + def index + serializer = Class.new(JSONAPI::Serializable::Resource) do + type :users + attribute :name + end + user = OpenStruct.new(id: 1, name: 'Lucas') + + render jsonapi: user, class: serializer + end + end + + subject { JSON.parse(response.body) } + let(:serialized_user) do + { + 'data' => { + 'id' => '1', + 'type' => 'users', + 'attributes' => { 'name' => 'Lucas' } } - ) + } end - expect(subject.send(:jsonapi_pointers)).to equal pointers + it 'renders a JSON API document' do + get :index + + expect(response.content_type).to eq('application/vnd.api+json') + is_expected.to eq(serialized_user) + end end end diff --git a/spec/dummy/app/controllers/tweets_controller.rb b/spec/dummy/app/controllers/tweets_controller.rb deleted file mode 100644 index 4b1c381..0000000 --- a/spec/dummy/app/controllers/tweets_controller.rb +++ /dev/null @@ -1,49 +0,0 @@ -class TweetsController < ActionController::Base - deserializable_resource :tweet, only: [:create, :update] - - def index - tweets = Tweet.all - - render jsonapi: tweets, - jsonapi_object: { version: '1.0' }, - meta: { foo: 'bar' }, - links: { - self: 'http://foo.bar' - } - end - - def show - tweet = Tweet.find(params[:id]) - - render jsonapi: tweet - end - - def create - tweet = Tweet.new(create_params.merge(author: current_user)) - - p request.env['jsonapi_deserializable.reverse_mapping'] - - unless tweet.save - render jsonapi_errors: tweet.errors - return - end - - render jsonapi: tweet, status: :created - end - - def destroy - Tweet.delete(params[:id]) - - head :no_content - end - - private - - def current_user - User.first || User.create(name: 'Lucas', email: 'lucas@jsonapi-rb.org') - end - - def create_params - params.require(:tweet).permit(:content, :parent_id) - end -end diff --git a/spec/dummy/app/deserializable/deserializable_tweet.rb b/spec/dummy/app/deserializable/deserializable_tweet.rb deleted file mode 100644 index 99d5972..0000000 --- a/spec/dummy/app/deserializable/deserializable_tweet.rb +++ /dev/null @@ -1,13 +0,0 @@ -class DeserializableTweet < JSONAPI::Deserializable::Resource - id - - attribute :content - - has_one :parent do |rel, id, type| - field parent_id: id - end - - has_one :author do |rel, id, type| - field author_id: id - end -end diff --git a/spec/dummy/app/models/tweet.rb b/spec/dummy/app/models/tweet.rb deleted file mode 100644 index 90a76c2..0000000 --- a/spec/dummy/app/models/tweet.rb +++ /dev/null @@ -1,4 +0,0 @@ -class Tweet < ApplicationRecord - belongs_to :parent, optional: true - belongs_to :author, class_name: 'User' -end diff --git a/spec/dummy/app/models/user.rb b/spec/dummy/app/models/user.rb deleted file mode 100644 index fef2809..0000000 --- a/spec/dummy/app/models/user.rb +++ /dev/null @@ -1,3 +0,0 @@ -class User < ApplicationRecord - has_many :tweets -end diff --git a/spec/dummy/app/serializable/serializable_tweet.rb b/spec/dummy/app/serializable/serializable_tweet.rb deleted file mode 100644 index e32465d..0000000 --- a/spec/dummy/app/serializable/serializable_tweet.rb +++ /dev/null @@ -1,9 +0,0 @@ -class SerializableTweet < JSONAPI::Serializable::Resource - type 'tweets' - - attribute :content - attribute(:date) { @object.created_at } - - has_one :parent - has_one :author -end diff --git a/spec/dummy/app/serializable/serializable_user.rb b/spec/dummy/app/serializable/serializable_user.rb deleted file mode 100644 index 2a84a46..0000000 --- a/spec/dummy/app/serializable/serializable_user.rb +++ /dev/null @@ -1,10 +0,0 @@ -class SerializableUser < JSONAPI::Serializable::Resource - type 'users' - - attribute :name - attribute :email - attribute :created_at - attribute :updated_at - - has_many :tweets -end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 736d6e8..17e25b6 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -6,7 +6,6 @@ require 'spec_helper' require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! -require 'dry-validation' # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are @@ -56,40 +55,3 @@ # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") end - -# Custom RSpec matchers -module JSONAPI - module RSpec - class BeValidJsonapiMatcher - def matches?(body) - JSONAPI.parse_response!(JSON.parse(body)) - true - end - end - - def be_valid_jsonapi - BeValidJsonapiMatcher.new - end - - class MatchSchemaMatcher - def matches?(body, &block) - schema = Dry::Validation.Schema(&block) - @body = JSON.parse(body) unless body.is_a?(Hash) - @result = schema.call(@body.with_indifferent_access) - @result.success? - end - - def failure_message - "#{@body} #{@result.errors}" - end - end - - def match_schema(&block) - MatchSchemaMatcher.new(&block) - end - end -end - -RSpec.configure do |config| - config.include JSONAPI::RSpec -end diff --git a/spec/railtie_spec.rb b/spec/railtie_spec.rb new file mode 100644 index 0000000..1b1879d --- /dev/null +++ b/spec/railtie_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper' + +describe JSONAPI::Rails::Railtie do + it 'registers the JSON API MIME type' do + expect(Mime[:jsonapi]).to eq('application/vnd.api+json') + end + + it 'registers the params parser for the JSON API MIME type' do + expect(::ActionDispatch::Request.parameter_parsers[:jsonapi]).not_to be_nil + end +end diff --git a/spec/requests/crud_spec.rb b/spec/requests/crud_spec.rb deleted file mode 100644 index fb9a58b..0000000 --- a/spec/requests/crud_spec.rb +++ /dev/null @@ -1,99 +0,0 @@ -require 'rails_helper' - -RSpec.describe 'Basic CRUD', type: :request do - let(:headers) do - { 'ACCEPT' => 'application/vnd.api+json' } - end - - def body - JSON.parse(response.body) - end - - it 'works' do - # Get empty tweets list - get '/tweets', headers: headers - expect(response).to be_success - expect(response).to have_http_status(200) - expect(response.body).to be_valid_jsonapi - expect(response.body).to match_schema do - required(:data).value(eql?: []) - required(:meta).value(eql?: { 'foo' => 'bar' }) - required(:jsonapi).value(eql?: { 'version' => '1.0' }) - required(:links).value(eql?: { 'self' => 'http://foo.bar' }) - end - - # Post first tweet - params = { - data: { - type: 'tweets', - attributes: { - content: 'foo' - }, - relationships: { - author: { - data: { - id: 'foo', - type: 'bar' - } - } - } - } - } - post '/tweets', - params: params, - as: :json, - headers: headers.merge('CONTENT_TYPE' => 'application/vnd.api+json') - expect(response).to be_success - expect(response).to have_http_status(201) - expect(response.body).to be_valid_jsonapi - expect(response.body).to match_schema do - required(:data).schema do - required(:type).value(eql?: 'tweets') - required(:attributes).schema do - required(:content).value(eql?: 'foo') - end - required(:relationships).schema do - required(:author) - end - end - end - first_tweet = body - - # Post second tweet - params = { - data: { - type: 'tweets', - attributes: { - content: 'foo' - } - } - } - post '/tweets', - params: params, - as: :json, - headers: headers.merge('CONTENT_TYPE' => 'application/vnd.api+json') - expect(response).to be_success - expect(response).to have_http_status(201) - expect { JSONAPI.parse_response!(body) }.not_to raise_error - second_tweet = body - - # Get first tweet - get "/tweets/#{first_tweet['data']['id']}", headers: headers - expect(response).to be_success - expect(response).to have_http_status(200) - expect(response.body).to be_valid_jsonapi - expect(body).to eq(first_tweet) - - # Delete first tweet - delete "/tweets/#{first_tweet['data']['id']}" - expect(response).to be_success - expect(response).to have_http_status(204) - - # List remaining tweets - get '/tweets' - expect(response).to be_success - expect(response).to have_http_status(200) - expect(response.body).to be_valid_jsonapi - expect(body['data'].first).to eq(second_tweet['data']) - end -end