graphiti-api / graphiti Goto Github PK
View Code? Open in Web Editor NEWStylish Graph APIs
Home Page: https://www.graphiti.dev/
License: MIT License
Stylish Graph APIs
Home Page: https://www.graphiti.dev/
License: MIT License
since the version 0.2.0 pagination links are not rendered anymore. Is this intended?
We need it for the d
method used here:
https://github.com/graphiti-api/graphiti/blob/master/lib/generators/graphiti/templates/index_request_spec.rb.erb
Relatedly, the docs say that jsonapi_data
is aliased to d
but AFAICT, Sugar
is not mentioned. https://www.graphiti.dev/guides/concepts/testing#jsonapi-data
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.
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).
Particularly for cross-api requests that require authentication.
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
Pet
Cat
and Dog
that inherit from Pet
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}
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.
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
I am trying render_jsonapi(projects, scope: false)
and it raise #<NoMethodError: undefined method
type' for #User:0x00000006711910>`, backtrace
Tries to require the swagger helpers without either checking failing gracefully or having it as a dependency: https://github.com/jsonapi-suite/jsonapi_suite/blob/master/lib/jsonapi_suite.rb#L7
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.
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.
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?
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".
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.
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!
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!
The passed in scope as the second argument to Graphiti::Resource#find
works for the actual render of a resource, however is not used/honored for the #update_attributes
or #destroy
methods.
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?
deserialized_params
to return an empty objectnil
payloadI'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>'```
(In latest Safari on latest macOS... no idea about others right now)
First of all, just loading the main landing page produces some errors:
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.
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.
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?
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.
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]
has_many
, belongs_to
relationships with disassociate
, destroy
, etc in the controller? (still don't quite understand this, really) 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
scope: false
doing? I see that quite a bit.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
.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!
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.
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?
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!
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!
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
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.
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)
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/
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
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.
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?
graphiti/lib/graphiti/util/persistence.rb
Line 19 in ae91899
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?
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
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?
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)
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.
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.
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.
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>'
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
.
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
graphiti/lib/graphiti/scoping/filter.rb
Line 82 in fc35e6f
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 😊 🙏
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.
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.
@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
Lines 70 to 73 in 47cea9c
the merged code was rspec
on line 70 and both seem to be in the wrong spot.
Could you look into these issues?
Support use cases where backend either does not support offset or it is expensive operation as in SQL databases on larger datasets
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.