Giter VIP home page Giter VIP logo

graphiti's People

Contributors

armellajuan avatar beauby avatar caseyprovost avatar chiliburger avatar danleyden avatar dishwasha avatar factyy avatar gfmurphy avatar jasonkarns avatar jkeen avatar kirchokirev avatar marcelobarreto avatar mattfenelon avatar michal-kazmierczak avatar mihaimuntenas avatar mrhardikjoshi avatar pchambino avatar piotrekbator avatar richmolj avatar semantic-release-bot avatar sideshowbandana avatar stevenharman avatar superslau avatar theonlyriddle avatar wadetandy avatar wagenet avatar xhpj avatar zailleh avatar zeisler avatar zpencerq avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphiti's Issues

no pagination links

since the version 0.2.0 pagination links are not rendered anymore. Is this intended?

Assign current_user.id to a resource on create?

So how in the world do you assign the current user from the session when you're creating one of his resources? I've spent a couple hours trying to make it work and am striking out. This should be a wiki page.

Resources do not inherit relationships

I have found that if a belongs_to relationship is defined on a Parent resource, this relationship will not exist on a Child of the Parent.

Is it something to be fixed?

When defining attributes and relationships on serializers they get correctly inherited by children classes, and this is also the behaviour for Rails models.

For the time being, I will try to put the relationships of the parent in a module and include it on each children (or just use concerns).

STI inheritance doubts

I have not been able to find much documentation on how jsonapi_suite behaves when using Single Table Inheritance on a model. Would anybody be so kind to clarify some points? I'd be grateful.

Example scenario: a model Pet (parent class) can be of the kind Cat or Dog (children classes).

I have some doubts on what would be the recommended approach regarding controllers, resources, serializers and JSORM models. Is there a default way to do that?

Should I use a single PetController to manage all models? It seems like I can only define one resource kind (jsonapi resource: DubbingResource).

If I fetch index from PetController will JSORM instantiate the children models for Dog and Cat, or otherwise a generic Pet model?

What I have been doing for the moment is to define:

  • PetController
  • CatController and DogController which do NOT inherit from PetController
  • PetResource
  • CatResource and DogResource that inherit from PetResource
  • SerializablePet
  • SerializableCat and SerializableDog that inherit from SerializablePet
  • JSORM model for Pet
  • JSORM model for Cat and Dog that inherit from Pet

Specs: `attributes_for(...).except("created_at", "updated_at")` not necessary?

Hey guys! Great gem! Thank you so much for building this!

I'm just diving into the gem, following the Quickstart guide.

I've been playing with the generated specs and found out that it's unnecessary to have the .except("created_at", "updated_at") part.

More specifically, it led me to write this:
.except("created_at", "updated_at", "title") which failed, because the only available keys in the post factory (from Quickstart) are %i[title upvotes active]:

FactoryBot.define do
  factory :post do
    title { "MyString" }
    upvotes { 1 }
    active { false }
  end
end
pry> attributes_for(:post)
=> {:title=>"MyString", :upvotes=>1, :active=>false}
pry> attributes_for(:post).except("title")
=> {:title=>"MyString", :upvotes=>1, :active=>false}
pry> attributes_for(:post).except(:title)
=> {:upvotes=>1, :active=>false}

NoMethodError: undefined method `[]' for nil:NilClass in RSpec #create and #update tests (and real-world actions)

I'm using the suggested templates for a basic RSpec tests, and I've got #show, #index, and #destroy working. But for #create, I'm getting NoMethodError: undefined method '[]' for nil:NilClass. My test looks like this:

require 'rails_helper'

RSpec.describe "courses#create", type: :request do
  subject(:make_request) { jsonapi_post "/api/v2/courses", payload }

  describe 'basic create' do
    let(:payload) do
      {
        data: {
          type: 'courses',
          attributes: {
            name: 'Test Course',
            description: 'A test course',
            nextStartTime: '2018-07-01 06:00:00'
          }
        }
      }
    end

    before { login_as_admin }

    it 'creates the resource' do
      expect { make_request }.to change { Course.count }.by(1)
      course = Course.last

      assert_payload(:course, course, json_item)
    end
  end
end

The test fails with:

NoMethodError: undefined method `[]' for nil:NilClass
./config/initializers/wrap_parameters.rb:17:in `process_action'
./spec/controllers/api/v2/courses/create_spec.rb:4:in `block (2 levels) in <main>'
./spec/controllers/api/v2/courses/create_spec.rb:23:in `block (4 levels) in <main>'
./spec/controllers/api/v2/courses/create_spec.rb:23:in `block (3 levels) in <main>'
-e:1:in `<main>'

The RSpec log indicates:

Processing by Api::V2::CoursesController#create as HTML
  Parameters: {"_jsonapi"=>{"data"=>{"type"=>"courses", "attributes"=>{"name"=>"Test Course", "description"=>"A test course", "next_start_time"=>"2018-07-01 06:00:00"}}}}
Completed 401 Unauthorized in 1ms (ActiveRecord: 0.0ms)

I'm not sure why it returns 401, as it's throwing the NoMethodError before any response is generated, but you can see that the params seem to be correct.

I have the same issue with my #update test.

This is not the same issue as described in #16, as my jsonapi_compliable gem is up to date (0.11.1).
Using Rails 5.1.5 and RSpec 3.7.

undefined method `build_scope' for User

I am trying to build sample application using devise_token_auth and pundit (later)

class ApplicationController < ActionController::Base
  include DeviseTokenAuth::Concerns::SetUserByToken
  include JsonapiSuite::ControllerMixin

  protect_from_forgery with: :null_session

  rescue_from Exception do |e|
    handle_exception(e)
  end
end

class ProjectResource < ApplicationResource
  type :projects

  allow_filter :id
  allow_filter :name
end

class SerializableProject < JSONAPI::Serializable::Resource
  type :projects

  attribute :name
end

class ProjectsController < ApplicationController
  before_action :authenticate_user!

  jsonapi resource: ProjectResource

  def index
    projects = current_user.projects
    render_jsonapi(projects) # error here
  end
end

backtrace

I am trying render_jsonapi(projects, scope: false) and it raise #<NoMethodError: undefined method type' for #User:0x00000006711910>`, backtrace

test with error here

How to create a resource that has_many: through:

Apologies if this has already been answered. I am struggling to create a resource that has_many of something that is defined as through.

An example for two models:

class Workout < ApplicationRecord
  has_many :workout_exercises, dependent: :destroy
  has_many :exercises, through: :workout_exercises, dependent: :destroy
  has_many :equipment, through: :workout_exercises, dependent: :destroy
end

class WorkoutExercise < ApplicationRecord
  belongs_to :exercise
  belongs_to :workout

  has_many :equipment, through: :exercise, dependent: :destroy
end

How would I write the resource for accessing equipment on the workout resource?

I have tried solutions with foreign_key but the workout_id doesnt exist on the Equipment model. Also tried with a separate scope block but could not get this to work properly.

 has_many :equipment,
            foreign_key: :workout_id,
            scope: -> { Equipment.all },
            resource: EquipmentResource

I am sure I can simplify my solution a bit, but either way I would like to learn how to do a simpler has_many through relation.

deserialize_jsonapi not found on controller

I'm having trouble during a patch with strong_resources. The guides say I should add before_action :deserialize_jsonapi!, only: [:create, :update] in order for puts/creates to work properly (makes sense). However this method doesn't seem to exist, and I didn't find it in the code.

ERROR: NoMethodError: undefined method `deserialize_jsonapi!' for #<UsersController:0x007fb548ebd4f8>
Did you mean?  deserialized_params

Is this due to out of date docs? Thanks.

no implicit conversion of nil into Hash

I have the following self association:

class OrganizationResource < JsonapiCompliable::Resource
  type :organizations
  ...
  ...
  ...

  belongs_to :parent,
    scope: -> { Organization.all },
    foreign_key: :parent_id,
    resource: OrganizationResource
    
  has_many :children,
    foreign_key: :parent_id,
    scope: -> { Organization.all },
    resource: OrganizationResource

end

My controller has the following;

class Api::V1::OrganizationsController < ApiController
  jsonapi resource: OrganizationResource
  ...
  ...
  ...
  def index
    organizations = Organization.accessible_by(current_ability)
    render_jsonapi organizations
  end

While this is working for older versions of suite and combliable (0.5.4, 0.5.7), I get the following error in the latest ones (0.6.1, 0.6.2);

 TypeError - no implicit conversion of nil into Hash:
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:107:in `block in nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:105:in `nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:96:in `block (2 levels) in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:88:in `block in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:87:in `to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:107:in `block in nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:105:in `nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:96:in `block (2 levels) in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:88:in `block in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:87:in `to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:107:in `block in nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:105:in `nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:96:in `block (2 levels) in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:88:in `block in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:87:in `to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:107:in `block in nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:105:in `nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:96:in `block (2 levels) in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:88:in `block in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:87:in `to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:107:in `block in nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:105:in `nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:96:in `block (2 levels) in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:88:in `block in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:87:in `to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:107:in `block in nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:105:in `nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:96:in `block (2 levels) in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:88:in `block in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:87:in `to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:107:in `block in nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:105:in `nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:96:in `block (2 levels) in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:88:in `block in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:87:in `to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:107:in `block in nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:105:in `nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:96:in `block (2 levels) in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:88:in `block in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:87:in `to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:107:in `block in nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:105:in `nested_sideload_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:96:in `block (2 levels) in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:88:in `block in to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/sideload.rb:87:in `to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/query.rb:31:in `include_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/query.rb:40:in `association_names'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/query.rb:46:in `to_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/scope.rb:32:in `query_hash'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/scope.rb:49:in `apply_scoping'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/scope.rb:14:in `initialize'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/resource.rb:120:in `build_scope'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/base.rb:43:in `jsonapi_scope'
  jsonapi_compliable (0.6.2) lib/jsonapi_compliable/base.rb:75:in `render_jsonapi'
  app/controllers/api/v1/organizations_controller.rb:41:in `index'

Im on ruby v 2.3.0 and rails 4.1.7

Anu suggestions?

Add before_validation hook

Original issue: #93

This is helpful to manually add validations that are dependent on the graph, e.g. "the employee must have at least two positions".

Use fast_jsonapi instead of jsonapi-rb

Hi
I want to know is there any reason why you don't use fast_jsonapi instead of jsonapi-rb?
It seems fast_jsonapi has better performance in lots of benchmarks than jsonapi-rb.

JSONAPI Suite in an engine

Hi guys,

Has anybody tested this gem as part of a Rails engine? The generators at least seem to not respect the engine's namespace, which is an unfortunate mishap.

Thanks, let me know please!

Documentation or Examples for Authorization

Hello!

I found an example of authentication in the user-account-app-cookbook, but I'm looking for some guidance on best practices to achieve authorization with resources.

I could achieve simple authorization in the controller, but I'm mostly worried about sideloading relationships. Accessing the current_user in the context for resources is a start.

Example, say resource A has_many of resource B. Resource A is publically accessible, but B is not. I can easily do this in separate controllers for A and B, but only logged in users should be able to sideload B through A. I hope that makes sense.

Thank you!

Assumed `payload` presence breaks #destroy

If the underlying model cannot be destroyed because of validations (eg: ActiveRecord), but rather errors are set, the rendering of :jsonapi_errors fails with:
ERROR: NoMethodError: undefined method 'relationships' for nil:NilClass.

This happens because presence of payload on the proxy is assumed (which is true for update scenarios), but not for #destroy scenarios.
https://github.com/graphiti-api/graphiti/blob/master/lib/graphiti/railtie.rb#L77

A #destroy action requires no additional payload, but rather only the :id of the object to be deleted. Thus, the payload is set to nil, by the Base#deserialized_params
https://github.com/graphiti-api/graphiti/blob/master/lib/graphiti/base.rb#L42

@richmolj Any idea which way to fix would be safer/better?

  • change deserialized_params to return an empty object
  • update renderer registration code to permit nil payload

`require': cannot load such file -- jsonapi/rails

I'm trying to start a new rails project with jsonapi suite on macOS 10.12.2.

ruby 2.4.0
Rails 5.0.1

I've followed the doc instructions by adding gem 'jsonapi_suite' to my Gemfile. After running bundle install I get the following error:

/usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/xml_mini.rb:51: warning: constant ::Fixnum is deprecated
/usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/xml_mini.rb:52: warning: constant ::Bignum is deprecated
/usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `require': cannot load such file -- jsonapi/rails (LoadError)
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `block in require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:259:in `load_dependency'
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/jsonapi_compliable-0.5.1/lib/jsonapi_compliable/rails.rb:1:in `<top (required)>'
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `block in require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:259:in `load_dependency'
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/jsonapi_compliable-0.5.1/lib/jsonapi_compliable.rb:24:in `<top (required)>'
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `block in require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:259:in `load_dependency'
        from /usr/local/lib/ruby/gems/2.4.0/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/jsonapi_suite-0.5.1/lib/jsonapi_suite.rb:5:in `<top (required)>'
        from /usr/local/lib/ruby/gems/2.4.0/gems/bundler-1.13.7/lib/bundler/runtime.rb:91:in `require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/bundler-1.13.7/lib/bundler/runtime.rb:91:in `block (2 levels) in require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/bundler-1.13.7/lib/bundler/runtime.rb:86:in `each'
        from /usr/local/lib/ruby/gems/2.4.0/gems/bundler-1.13.7/lib/bundler/runtime.rb:86:in `block in require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/bundler-1.13.7/lib/bundler/runtime.rb:75:in `each'
        from /usr/local/lib/ruby/gems/2.4.0/gems/bundler-1.13.7/lib/bundler/runtime.rb:75:in `require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/bundler-1.13.7/lib/bundler.rb:106:in `require'
        from /Users/lennartbastian/web/test-json/config/application.rb:17:in `<top (required)>'
        from /usr/local/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/application.rb:82:in `require'
        from /usr/local/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/application.rb:82:in `preload'
        from /usr/local/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/application.rb:143:in `serve'
        from /usr/local/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/application.rb:131:in `block in run'
        from /usr/local/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/application.rb:125:in `loop'
        from /usr/local/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/application.rb:125:in `run'
        from /usr/local/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/application/boot.rb:19:in `<top (required)>'
        from /usr/local/Cellar/ruby/2.4.0/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:in `require'
        from /usr/local/Cellar/ruby/2.4.0/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:in `require'
        from -e:1:in `<main>'```

gh-pages: Certain outbound links do not work

(In latest Safari on latest macOS... no idea about others right now)
First of all, just loading the main landing page produces some errors:

screen shot 2018-01-28 at 5 30 06 pm

But if I click the link at the center of the page (jsonapi_spec_helpers) in the screenshot below, I get the bottom-most error in the console (also pictured in the screenshot), and nothing happens.

screen shot 2018-01-28 at 5 31 59 pm

Same behavior for many—but not all—links throughout the site.

This is an FYI, only. You can right click and open any link in a new tab, no problem.

Any examples of using this with devise_token_auth?

I'm getting an error of:

Processing by DeviseTokenAuth::RegistrationsController#create as */*
  Parameters: {"email"=>"[email protected]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "confirm_success_url"=>"http://localhost:3000"}
ERROR: NoMethodError: undefined method `new' for nil:NilClass

But only when I use this gem...?

Any tips?

Custom Persistence with Sideposting?

I've read over the examples for both custom persistence and sideposting. It seems like to add a service call in the mix, you would define a create(attributes) method in the resource. If you're sideposting, I suppose you'd define multiple create(attributes) on the various resources.

Assuming that's all correct, what if your service object coordinates multiple models being posted together, none of which can be saved until the service object touches all of them?

A more concrete example, let's say you have an Order and an Address and need to geocode to find the timezone, then apply that timezone to a time on the order:

# app/services/some_service.rb
class SomeService
  def initialize(order, address)
    # assigning variables, etc...
  end

  def create
    address.geocode
    order.some_time = time_in_new_timezone(order.some_time_without_zone, address.timezone)
    address.save && order.save
  end

  private

  def time_in_new_timezone(time, zone)
    # some logic
  end
end

# app/resources/order_resource.rb
class OrderResource < ApplicationResource
  type :orders
  model Order

  def create(attributes)
    order = Order.new(attributes)
    # address = ??? (no access to the params for address here)
    SomeService.new(order, address).create
    order
  end
end

I know there are other ways to approach this particular example, but it's just a simple example representing a much more complex service.

Simplification suggestions for easier understanding

I'm building an API-only application in Rails with an Ember frontend and was surprised at how difficult it was to find something that let rails speak and understand json-api responses and requests. It felt like something I should be able to add a gem for and have it do Do The Right Thing™ without having to set up anything, but I was wrong.

I tried out several different options before coming across jsonapi-suite, and this is the best I've found and I'm super impressed how comprehensive it is!

But I've had some real trouble wrapping my head around everything that's going on and it got me digging into the source of the collection of gems and wishing it all felt more self-explanatory. So I thought I'd share the points I got hung up on, and throw in my two cents on possible improvements (and a future PR possibly) so I don't go off and try to build yet another json-api gem solution (relevant xkcd).

Don't get me wrong, the documentation and code comments are plentiful, but one of the reasons I like ruby/rails so much is because of the whole philosophy of having the code explain itself. And I'm a user-experience focused idealist which explains how I've derailed my productivity entirely this week wondering things like "Is resource the best name?" instead of just moving along with my project and accepting things the way they are. But I think a few key naming changes might make the whole suite so much easier to grok and allow people like me (who tend to avoid documentation and lengthy code comments) an easier time.

If we were just speaking normal json, some typical rails controller actions might look like this:

# GET
def show
  #1. Find Post
  @post = Post.find(params[:id])

  #2. Render post as json
  render json: @post # transformed into json
end

# POST
def create
  #1. Whitelist params
  post_params = params.require(:title, :author, :body)

  #2. Create post with params
  @post = Post.new(post_params)

  #3. Return status of operation
  if @post.save
    render json: @post, status: :created, location: @post
  else
    render json: @post.errors, status: :unprocessable_entity
  end
end

This all feels pretty normal, and going into this endeavor all I wanted a json-api gem to do was to convert the incoming json-api payload into something rails understood automatically and then convert the outgoing payload into json-api format leaving the controller looking almost the same as the default above. I understand that the suite does so more than just that, and in order to handle the nested creates and other features it's undoubtedly going to be a little more involved.

But the things that tripped me up going into the default controller setup for this were as follows:

class PostsController < ApplicationController
  jsonapi resource: PostResource

  strong_resource :post do
    has_many :comments, disassociate: true, destroy: true do
      belongs_to :post, disassociate: true, destroy: true
    end
  end

  before_action :apply_strong_params, only: [:create, :update]
  1. What's the difference between the strong resource and the other resource? (answered it myself eventually, but doubling up on terms there tripped me up).
  2. I defined strong_resources in an initializer, why do I need to define the has_many, belongs_to relationships with disassociate, destroy, etc in the controller? (still don't quite understand this, really)
  3. Is strong params something different? What's this doing (answered it myself eventually)
  def index
    posts = Post.all
    render_jsonapi(posts)
  end

  def show
    scope = jsonapi_scope(Post.where(id: params[:id]))
    instance = scope.resolve.first
    raise JsonapiCompliable::Errors::RecordNotFound unless instance
    render_jsonapi(instance, scope: false)
  end
  1. What is scope: false doing? I see that quite a bit.
  2. I defined a PostResource, assigned it to the controller, and it knows we're dealing with Post models, how is the controller using that if I'm still having to call into the Post model directly?
  def create
    post, success = jsonapi_create.to_a

    if success
      render_jsonapi(post, scope: false)
    else
      render_errors_for(post)
    end
  end

  def update
    post, success = jsonapi_update.to_a

    if success
      render_jsonapi(post, scope: false)
    else
      render_errors_for(post)
    end
  end

  def destroy
    post, success = jsonapi_destroy.to_a

    if success
      render json: { meta: {} }
    else
      render_errors_for(post)
    end
  end
  1. Why do each of those calls have an explicit .to_a call? If that’s the expected return, why not just have the internals do that?

As I dug through the source code I understood better what was going on, but sketched out a possibility that might make things feel less opaque and also help reduce the need for explanatory comments.

class PostsController < ApplicationController
  jsonapi resource: PostResource, allow_sideload: [:comments]

  before_action :validate_request_params, only: [:create, :update]

  def index
    resources = find_resources(deserialized_params)
    render json: resources.response
  end

  def show
    resource = find_resource(params[:id])

    if resource.found?
      render json: resource.response
    else
      render json: resource.errors, status: :not_found
    end
  end

  def create
    resource = build_resource(deserialized_params)

    if resource.save # persist with relationships, returns boolean
      render json: resource.response, status: :created, location: resource.location
    else
      render json: resource.errors, status: resource.status
    end
  end

  def update
    resource = find_resource(params[:id])

    if resource.found? && resource.update(deserialized_params) # persist with relationships, returns boolean
      render json: resource.response, status: :ok, location: resource.location
    else
      render json: resource.errors, status: resource.status
    end
  end

  def destroy
    resource = find_resource(params[:id])

    if resource.found? && resource.destroy
      render json: { meta: {} }
    else
      render json: resource.errors, status: resource.status
    end
  end
end

The main change here is having a new object that manipulates resources behind the scenes that the consumer of this gem interacts directly with, making it clearer on what's going on and much closer to the rails default pattern that everyone's familiar with. I implemented a hacky/incomplete version of it here

I think this pattern might have some potential.

Second piece of setup simplification that I haven't dug into as much yet is the serializer, resource, and strong_resource piece (some of the points of confusion I covered above), but is there a reason those three concepts couldn't be combined? Currently the resource object's type needs to match the serializer object's type but they're in separate files and some of the interaction/terminology was tripping me up.

What if it were something like this, and you only had to define one file/class?

class PostResource
  type :post
  model Post

  request do
    validate_request_params do
      allow :title, :string
      allow :author, :string
      allow :published_date, :date_time
    end

    allow_sideload :comments
  end

  response do
    attribute :title
    attribute :author
    attribute :published_date
    attribute :created_at
    attribute :updated_at
    has_many :comments
  end
end

Anyway, that was much longer than I intended it to be and it's all I have for now! Great work on this thing overall, and I hope I can help out somehow!

belongs_to relationship without parent should not return link.

A belongs_to relationship with a nil parent should not return a link. Currently it returns a link to the index route and that causes a potential loading of all associated resources to be associated with the belongs_to (that should be only one).

Correct behavior should be to return no link and data: null.

Camelcasing and snakecasing params?

Is this possible to do. So lets say, coming into the app I want the attributes names to go from camel to snake and then on serialize to go back to camel. Is this doable?

How can I use some number type?

Hi guys,

I want to use some number type (double, float, decimal) but I always get:

ERROR: NoMethodError: undefined method `[]' for nil:NilClass

I just follow tutorial:

$ rails new blog --api -m https://raw.githubusercontent.com/jsonapi-suite/rails_template/master/all.rb
$ bundle exec rails generate model Invoice user:string price:float
$ bundle exec rake db:migrate
$ bundle exec rails g jsonapi:resource Invoice user:string price:float

boom! I get the error message on post.

Some idea?

Thanks a lot!

How to serialize STI ActiveRecord models

Hey guys,

I have an ActiveRecord::Base Order model and two more inheriting from it using rails STI feature: Complete < Order and Abandoned < Order.

I have configured the Order model with the Suite and when I request the OrdersController#index method an error is raised:

ERROR: NameError: Undefined serializable class SerializableComplete

Does this mean that I need to create Serializers for each of the Order#descendants

Thanks!

De-serialization issue when following Quickstart steps

I'm trying to follow the steps outlined here
Tried to create a simple resource using postman with the following request parameters
Content-Type:application/vnd.api+json

{
	"data": {
		"type": "stocks",
		"attributes": {
			"symbol": "AAPL"
		}
	}
}

The Rails server for some reason is adding a root element which is breaking the deserialization

Processing by StocksController#create as */*
  Parameters: {"_jsonapi"=>{"data"=>{"type"=>"stocks", "attributes"=>{"symbol"=>"AAPL"}}}}
ERROR: NoMethodError: undefined method `[]' for nil:NilClass

Usage with non-persisted entities

Does this library support rendering resources that are not backed by a database model?

For example, how would one go about creating an endpoint (in rails) that handled exchanging access tokens (e.g. a short lived token for an extended one) for Facebooks OpenGraph API?

It looks like I can access the POST data via deserialized_params.attributes (bad idea?), but I am unsure how to render the results. Creating a custom resource adapter seems like it might get me there, but also does not seems appropriate for this use case.

How to fetch nested associated has_many data on Resource

Thanks for all the hard work you are putting into this. Really appreciate it.

I've been working on a project on and off for a couple of months. I have something called a workout, when fetching it I would like to be able to list equipment that it has associated (a couple of levels down). Basically, when I'm using through.

class Workout < ApplicationRecord
  has_many :workout_exercises, dependent: :destroy
  has_many :exercises, through: :workout_exercises, dependent: :destroy
  has_many :equipment, through: :exercises, dependent: :destroy
end
class Exercise < ApplicationRecord
  has_many :equipment_exercises
  has_many :equipment, through: :equipment_exercises, dependent: :destroy
end

How should the WorkoutResource association for this look like? I've tried a lot of different versions of has_many :equipment, link: false and overriding the lookup but no luck.

The link I'm using is ``workouts/1?include=blocks,author,workout_exercises,exercises,equipment`

(I had a similar question back when I was using json api suite, #47)

Versioning

Is there any particular reason that only specs are version name-spaced? For example,

rails g jsonapi:resource User first_name:string last_name:string 

creates

spec/api/v1/users/...

but controllers, resources and serializers are created in directories

/controllers/
/resources/
/serializers/

Persisting of empty Integer values fails

When clearing an existing integer value in the client, Spraypaint sends an empty string to the server to represent the change, however Graphiti doesn't like an empty string (nor 'nil' or 'null').

ERROR: Graphiti::Errors::TypecastFailed: MMApi::AssetResource: Failed typecasting :year! Given "" but the following error was raised:

invalid value for Integer(): ""

/Users/lonnie/Projects/MachineTools/.mt3.gems/gems/dry-types-0.13.4/lib/dry/types/constructor.rb:54:in `Integer'
/Users/lonnie/Projects/MachineTools/.mt3.gems/gems/dry-types-0.13.4/lib/dry/types/constructor.rb:54:in `[]'
/Users/lonnie/Projects/MachineTools/.mt3.gems/gems/dry-types-0.13.4/lib/dry/types/constructor.rb:54:in `call'
/Users/lonnie/Projects/MachineTools/.mt3.gems/bundler/gems/graphiti-ae91899884f0/lib/graphiti/types.rb:60:in `block in <class:Types>'
class MyResource < Graphiti::Resource
  attribute :year, :integer
end
irb(main):001:0> Dry::Types['coercible.integer']['']
ArgumentError: invalid value for Integer(): ""
    from (irb):1
irb(main):002:0> Dry::Types['coercible.integer']['nil']
ArgumentError: invalid value for Integer(): "nil"
    from (irb):2
irb(main):003:0> Dry::Types['coercible.integer']['null']
ArgumentError: invalid value for Integer(): "null"
	from (irb):3

scoping resource associations with state

Hi Lee,
Thanks for the great set of libraries, it has greatly simplified writing a jsonapi compatible application.
Frequently I find myself scoping queries through some sort of user state, for example in this simplified situation:

class Foo < ApplicationRecord
  has_and_belongs_to_many :bars
end

class Bar < ApplicationRecord
  has_and_belongs_to_many :foos
  has_many :users
end

I might want to scope all requests to the Bar index endpoint, to only return bars that are associated to the current_user. However, if I request GET /foo?include=bar I will still get other Bars associated to the Foo that are not relevant to the user.

How would one accomplish this sort of relationship filtering with the jsonapi_suite? My initial thought would be to allow passing some sort of global scope into render_jsonapi which can be used by the association scope in the FooResource, similar to how ActiveRecord handles scopes:

class FooResource < ApplicationResource
  type :foo

  has_many :bars,
    foreign_key: { foos_bars: :foo_id },
    scope: lambda |current_user| { Bar.where(user: current_user) },
    resource: BarResource
end

class FooController < ApplicationController
  def index
    render_jsonapi Foo.all, current_user: @current_user  # could be generalized to any state
  end
end

What are your thoughts on how this could be handled? I'm happy to implement a PR.

Resolve polymorphic resource before typecasting the attributes in Util::Persistence initializer

Using polymorphic resources, the expectation one has is that the child resources can define additional attributes and relations (as per tasks in employee_directory example), whilst still using the base resource to create them - using appropriate payload.

Unfortunately this will not work for attributes as the Util::Persistence in the initializer typecasts the attributes before resolving the @resource to the child resource; in which case the typecast fails because the attribute is only defined by the child resource.

Should resolving the @resource happen before the attributes typecast?
Code from line 19->24 be before @attributes.each_pair at line 15?

# Find the correct child resource for a given jsonapi type

Filtring with url params

The JSON API specification states this:

Note: JSON API is agnostic about the strategies supported by a server. The filter query parameter can be used as the basis for any number of filtering strategies.

Therefore, it is possible to filter results using the URL params like shown here: http://jsonapi.org/recommendations/#filtering

In #9, association filtering is used to filter the include part of the response. Is there a way to change this default behavior to filter the returned array instead of the include's content?

I know that I can use allow_filter with a block but I can't use the name of the relationship, I need to use another name.

Are you planning to implement this behavior? What are your thoughts about such behavior and where should I look to if I want to implement this?

Decide on a strategy for filtering/sorting extra_attributes

Originally graphiti-api/graphiti_old_archived#54

Currently trying to sort or filter on something defined with extra_attributes will raise an error that the filter or sort is not defined. The DSL will accept both filterable: true and sortable: true arguments as an override, and while this removes the helpful error, it doesn't actually wire up the filters or sorts correctly so it blows up in a much less helpful fashion. We should be explicit about what we want to support here and provide useful errors when users try to do things outside of that.

Preserving the initial chat between me and @richmolj which spawned this issue:

@wadetandy: Just noticed that setting “filterable: true” on extra_attributes doesn’t actually create the filters as one would expect (though doesn’t yell at you either). Oversight or intended? If the former, we should extract this to a private method or something:
@richmolj: A little bit of each. Im not sure if this is in the docs, but extra attrs are read only.
Bc their orig use case was computed values, so you wouldnt filter on em
But i think the legacy id thing is a good use case to reconsider
I think there may be adverse effects of making them more than read only. At min that has to be the default
@wadetandy: Yeah, I could see basically raising an error if you provided a block AND filterable/sortable = true or something,
And disallowing writes

Relationship updates belongs_to record

Hi,

I have the following relationship;

class OrganizationResource < JsonapiCompliable::Resource
  type :organizations

  model Organization
  
  use_adapter JsonapiCompliable::Adapters::ActiveRecord
....
....
....
  belongs_to :parent, 
    foreign_key: :parent_id,
    scope: -> { Organization.all },
    resource: OrganizationResource

When trying to create a new record;

Strong Resources

StrongResources.configure do
  strong_resource :organization do
    attribute :name, :string

  end

Controller

class Api::OrganizationsController < ApiController
  jsonapi resource: OrganizationResource

  strong_resource :organization do
    belongs_to :parent
  end
  before_action :apply_strong_params, only: [:create, :update]

  def create
    organization, success = jsonapi_create.to_a
    if success
        render_jsonapi(organization, scope: false)
      else
        render_errors_for(organization)
    end
  end

Params

 {"data"=>
  {"attributes"=>
    {"name"=>"testname",
   ...
   ...
   ...
},
   "relationships"=>{"parent"=>{"data"=>{"type"=>"organizations", "id"=>"1"}}},
   "type"=>"organizations"},
 "action"=>"create",
 "controller"=>"api/organizations"}

it will try to update the related parent instead of just using its id for the new record's parent_id attribute
and throw any errors that are related with it (such as name can't be blank).

I understand that this is maybe caused because the gem treats all realtionship data as nested associations when deserializing thud trying to update all records inside the relationships value of the params.

Is there a way that I can setup my configuration so this doesn't happen but simply create/update a record with the appropriate relationships?

rails model validations depending on related entities can break certain create and update scenarios

Probably best understood via example:

In the graphiti specs, add the following validation to the Employee class:

validates :positions, length: { minimum: 1, too_short: 'An employee must have at least one position!' }

This validation will break the persistence > nested create > creates the objects spec. Despite the fact this spec attempts to create an employee with positions, the error response will indicate "An employee must have at least one position!"


This appears to be a consequence of how graphiti creates and associates entities as it processes the payload. Looking at Graphiti::Util::Persistence#run, an entity is attempted to be persisted only once and only using its primary attributes. Only after attempting to persist the entity are its associations (parents and children) processed. This means that model validations depending upon parents and/or children do not work as expected.

I've only given the most simple example here but this bug can manifest in different ways depending upon the exact scenario (i.e. the exact validations, relationships, and payload)

Polymorphic nested include broken in 0.6

Hey Lee,

I got around to upgrading jsonapi_suite in 0.6 and implementing resource etc. I'm having trouble with something that seemed to work previously, which is a nested include on a polymorphic object.

class WatchedAt < ApplicationRecord
  belongs_to :watchable, :polymorphic => true
end

class Movie < ApplicationRecord
  has_many :watched_at, :as => :watchable
end

class Episode < ApplicationRecord
  belongs_to :show
  has_many :watched_at, :as => :watchable
end

class Show < ApplicationRecord
  has_many :episodes
end

I'm trying to issue the following query:
/watched_ats?include=watchable,watchable.show

My WatchedAt Resource / Controller:

class WatchedAtResource < JsonapiCompliable::Resource
  type :watched_at
  use_adapter JsonapiCompliable::Adapters::ActiveRecord
  allow_filter :watchable_type

  polymorphic_belongs_to :watchable,
    group_by: proc { |watchable| watchable.watchable_type },                                                                                                                             groups: {
      'Episode' => {
        foreign_key: :watchable_id,
        resource: EpisodeResource,
        scope: -> { Episode.all }
      },                                                                                                                                                                                   'Movie' => {
        foreign_key: :watchable_id,
        resource: MovieResource,
        scope: -> { Movie.all }
      }
    }
end

class WatchedAtsController < ApplicationController
  jsonapi resource: WatchedAtResource do
     sideload_whitelist({
        index: [{ watchable: :show }, :watchable],
        show: [{ watchable: :show }, :watchable]
      })
  end
  def index
    query = WatchedAt.where(user_id: current_user.id).includes(:watchable)
    render_jsonapi(query)
  end
end
class EpisodeResource < JsonapiCompliable::Resource
  type :episode
  use_adapter JsonapiCompliable::Adapters::ActiveRecord
  allow_filter :title
  belongs_to :show,
    foreign_key: :show_id,
    scope: -> { Show.all },
    resource: ShowResource
end

The watchables from each "WatchedAt" object are being included (movies and episodes), however, the show objects on the episodes are not being included. I've also tried things like [{episode: :show}] in my controller sideload_whitelist to no avail. Off the top of your head, do you know why this would be failing?

Thanks for your help.

Remote Resources: Unlimited pagination

When sideloading, we don't apply a pagination limit by default. However we DO apply this when calling remote APIs (we don't send any pagination info, so likely hits a default of 20).

Instead, we should have unlimited pagination, firing multiple requests if number of records exceeds 999.

Unnecessary Dependency on activerecord

Could the dependency on activerecord be moved to development_dependency? We are not using activerecord and including this gem unnecessarily causes its inclusion in our project. People who use activerecord will include the gem explicitly anyway.

There was an error while trying to load the gem 'jsonapi_spec_helpers'

I has this error, maybe its because of this, not sure, better make add_development_dependency, though I fixed this with require: false

gem 'jsonapi_suite'
gem 'jsonapi-rails'

group :development, :test do
  gem 'jsonapi_spec_helpers', require: false
end
$ rails s
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:94:in `rescue in block (2 levels) in require': There was an error while trying to load the gem 'jsonapi_spec_helpers'. (Bundler::GemRequireError)
Gem Load Error is: uninitialized constant RSpec::Matchers
Backtrace for gem load error is:
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/jsonapi_spec_helpers-0.4.0/lib/jsonapi_spec_helpers/matchers.rb:1:in `<top (required)>'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/jsonapi_spec_helpers-0.4.0/lib/jsonapi_spec_helpers.rb:3:in `<top (required)>'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:91:in `require'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:91:in `block (2 levels) in require'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:86:in `each'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:86:in `block in require'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:75:in `each'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:75:in `require'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler.rb:107:in `require'
/home/bjorn/projects/ngrx-todolist/config/application.rb:17:in `<top (required)>'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/commands/server/server_command.rb:129:in `require'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/commands/server/server_command.rb:129:in `block in perform'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/commands/server/server_command.rb:126:in `tap'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/commands/server/server_command.rb:126:in `perform'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/thor-0.19.4/lib/thor/command.rb:27:in `run'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/thor-0.19.4/lib/thor/invocation.rb:126:in `invoke_command'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/thor-0.19.4/lib/thor.rb:369:in `dispatch'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/command/base.rb:63:in `perform'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/command.rb:44:in `invoke'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/commands.rb:16:in `<top (required)>'
/home/bjorn/projects/ngrx-todolist/bin/rails:9:in `require'
/home/bjorn/projects/ngrx-todolist/bin/rails:9:in `<top (required)>'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/client/rails.rb:28:in `load'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/client/rails.rb:28:in `call'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/client/command.rb:7:in `call'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/client.rb:30:in `run'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/bin/spring:49:in `<top (required)>'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/binstub.rb:31:in `load'
/home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/binstub.rb:31:in `<top (required)>'
/home/bjorn/.rvm/rubies/ruby-2.4.0/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:68:in `require'
/home/bjorn/.rvm/rubies/ruby-2.4.0/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:68:in `require'
/home/bjorn/projects/ngrx-todolist/bin/spring:15:in `<top (required)>'
bin/rails:3:in `load'
bin/rails:3:in `<main>'
Bundler Error Backtrace:
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:90:in `block (2 levels) in require'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:86:in `each'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:86:in `block in require'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:75:in `each'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler/runtime.rb:75:in `require'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/bundler-1.14.6/lib/bundler.rb:107:in `require'
        from /home/bjorn/projects/ngrx-todolist/config/application.rb:17:in `<top (required)>'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/commands/server/server_command.rb:129:in `require'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/commands/server/server_command.rb:129:in `block in perform'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/commands/server/server_command.rb:126:in `tap'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/commands/server/server_command.rb:126:in `perform'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/thor-0.19.4/lib/thor/command.rb:27:in `run'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/thor-0.19.4/lib/thor/invocation.rb:126:in `invoke_command'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/thor-0.19.4/lib/thor.rb:369:in `dispatch'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/command/base.rb:63:in `perform'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/command.rb:44:in `invoke'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/railties-5.1.1/lib/rails/commands.rb:16:in `<top (required)>'
        from /home/bjorn/projects/ngrx-todolist/bin/rails:9:in `require'
        from /home/bjorn/projects/ngrx-todolist/bin/rails:9:in `<top (required)>'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/client/rails.rb:28:in `load'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/client/rails.rb:28:in `call'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/client/command.rb:7:in `call'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/client.rb:30:in `run'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/bin/spring:49:in `<top (required)>'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/binstub.rb:31:in `load'
        from /home/bjorn/.rvm/gems/ruby-2.4.0@ngrx-todolist/gems/spring-2.0.1/lib/spring/binstub.rb:31:in `<top (required)>'
        from /home/bjorn/.rvm/rubies/ruby-2.4.0/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:68:in `require'
        from /home/bjorn/.rvm/rubies/ruby-2.4.0/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:68:in `require'
        from /home/bjorn/projects/ngrx-todolist/bin/spring:15:in `<top (required)>'
        from bin/rails:3:in `load'
        from bin/rails:3:in `<main>'

Generators do not handle namespaces correctly

When using the generator it creates invalid ruby code for specs and bad routes. For example:

rails g graphiti:resource LocaL::User username:string

Generates specs with fixtures names :local/user and a route to local_users instead of local/users.

Allow multiple operators on the same attribute while filtering

Hey Lee, first of all i need to thank you for this great library! 🥇 thanks for all the effort and time you put into this amazing gem.

My issue is regarding the option to allow multiple operators to be used for filtering on a single attribute. We have the use case, that we have an attribute :date and we want to filter all Resources where this date is between a given range of dates.

I know that i could achieve this, by defining my own custom filter for that, but i'm wondering why it is not supported to do something like

MyResource.all({filter: {date: { gte: Date.today, lte: Date.today+2.months }}}) ?

In the code

value = param_value.values.first
you are using only the first operator and the corresponding value. Is there any reason why you did it like that and not use all passed operators and values?

If this would be possible, sure it would allow ambiguous requests to filter for example the same attribute on eq and not_eq with the same value, which would be kind of stupid.

Would appreciate any help on this topic. If you want, i can also prepare a PR to make this possible.
Best regards 😊 🙏

Nested sideloaded relationship on return scope for added sidepost not loading

If I have a three level deep model where the third level is retrieved via remote resource, when I add a new item to the second level with spraypaint.js and ask for a return scope with all three levels, the response payload returns the second level with the third level relationship but the data is null and the third level record isn't included in the included response payload. One thing to note is that the scenario where this behavior is exhibited has a second level that is a polymorphic relationship and have not tested when the second level is not polymorphic.

Here is a pseudo representation:

class ParentResource < ApplicationResource
  has_many :polymorphic_throughs
end

class PolymorphicThroughResource < ApplicationResource
  polymorphic_belongs_to :child do
    group_by :child_type do
      on(:toddler).belongs_to :toddler,
      resource: RemoteToddlerResource,
      foreign_key: :child_id
    end
  end
end

class RemoteToddlerResource < ApplicationResource
  self.adapter = Graphiti::Adapters::GraphitiAPI
  self.remote = "http://children-api/toddlers"
end

When a PolymorphicThrough instance is added to parent, the spraypaint save looks like:

parent.save({returnScope: Parent.includes['polymorphic_throughs', 'polymorphic_throughs.child'], with: ['polymorphic_throughs']})

Then the PATCH URL looks like:

http://localhost/parents/1?include=polymorphic_throughs,polymorphic_throughs.child

And the PATCH response payload looks like:

{data: {id: "1", type: 'parents'}, included: [{0: {attributes: {...}, id: 1, relationships: {child: {data: null}}, ....}, ...]}

With no "toddler" in the included list.

Payload does not handle DateTime type as expected

I have registered a Payload type for a simple class:

JsonapiSpecHelpers::Payload.register(:course) do
  key(:name, String)
  key(:description, String)
  key(:next_start_time, DateTime)
end

And I have written an RSpec test based on the #show template:

require 'rails_helper'

RSpec.describe "courses#show", type: :request do
  let(:params) { {} }

  subject(:make_request) { jsonapi_get "/api/v2/courses/#{course.id}", params: params }

  describe 'basic fetch' do
    before { login_as_user }
    let!(:course) { create(:course, description: 'A test course', next_start_time: '2018-07-01 06:00:00') }

    it 'serializes the resource correctly' do
      make_request
      assert_payload(:course, course, json_item)
    end
  end
end

When I run the test, the result is:

Expected JSON payload key 'next_start_time' to have type DateTime but was String
./spec/controllers/api/v2/courses/show_spec.rb:14:in `block (3 levels) in <main>'
-e:1:in `<main>'

I get the same response if I parse the string in advance, like so:

let!(:course) { create(:course, description: 'A test course', next_start_time: DateTime.parse('2018-07-01 06:00:00')) }

The test passes if I change the registration in the Payload registry to indicate that next_start_time is a String.

I would have expected if a key is registered as DateTime in the Payload registry, the test suite would attempt to parse the JSON response string to determine if it was a valid datetime.

Pagination Links Error

@sideshowbandana one of my users had an issue with this right after release.

ERROR: NoMethodError: undefined method `/' for nil:NilClass
graphiti (1.0.rc.10) lib/graphiti/delegates/pagination.rb:40:in `last_page'
graphiti (1.0.rc.10) lib/graphiti/delegates/pagination.rb:15:in `block in links'
graphiti (1.0.rc.10) lib/graphiti/delegates/pagination.rb:13:in `tap'
graphiti (1.0.rc.10) lib/graphiti/delegates/pagination.rb:13:in `links'
graphiti (1.0.rc.10) lib/graphiti/renderer.rb:47:in `block in render'
graphiti (1.0.rc.10) lib/graphiti.rb:141:in `block in broadcast'

The larger issue is that we should have these links off by default. I did that here.

There also seemed to be some spec code that was problematic locally

#RSpec.shared_context_metadata_behavior = :apply_to_host_groups
#config.include_context "pagination_context", include_shared: true

the merged code was rspec on line 70 and both seem to be in the wrong spot.

Could you look into these issues?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.