Giter Site home page Giter Site logo

mwpastore / sinja Goto Github PK

View Code? Open in Web Editor NEW
85.0 8.0 8.0 327 KB

RESTful, {json:api}-compliant web services in Sinatra

Home Page: http://sinja-rb.org

License: MIT License

Ruby 99.37% Shell 0.63%
sinatra json-api ember-data ruby-framework ruby-gem web-framework

sinja's Issues

All creates yield `Malformed {json:api} request payload`

The issue seems to be that all my attempts to create new resources via the JSON API using sinja seem to not work. It is entirely possible that I'm doing something wrong, but I can't find it looking through both the README and demo app.

I created a repo with the most essential components of my app captured:
https://github.com/jgnagy/test-api

Notice the /keys endpoint supports both show and create. The show works fine, per this curl command:

curl -i  -H 'Accept: application/vnd.api+json' http://localhost:9292/keys/1

Which yields something like:

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
Last-Modified: Mon, 13 Feb 2017 21:04:00 GMT
Content-Length: 260
Server: WEBrick/1.3.1 (Ruby/2.3.1/2016-04-26)
Date: Mon, 13 Feb 2017 21:04:00 GMT
Connection: Keep-Alive

{
  "data": {
    "type": "keys",
    "id": "1",
    "attributes": {
      "name": "A Key",
      "created-at": "2017-02-13 13:04:00 -0800"
    },
    "links": {
      "self": "/keys/1"
    }
  },
  "jsonapi": {
    "version": "1.0"
  },
  "included": [

  ]
}

However, attempting to create a new resource via this curl command:

curl -i -H 'Accept: application/vnd.api+json' -H 'Content-Type: application/vnd.api+json' --data '{"data":{"type":"keys", "attributes":{"name":"foo"}}}' http://localhost:9292/keys

Returns the following:

HTTP/1.1 400 Bad Request
Content-Type: application/vnd.api+json
Content-Length: 199
Server: WEBrick/1.3.1 (Ruby/2.3.1/2016-04-26)
Date: Mon, 13 Feb 2017 21:04:02 GMT
Connection: Keep-Alive

{
  "errors": [
    {
      "id": "0d741b83-7a52-499a-8365-c278cd161e3a",
      "title": "Bad Request Error",
      "detail": "Malformed {json:api} request payload",
      "status": "400"
    }
  ]
}

I'm uncertain why this isn't working, and I've tried making additional Sinatra apps just passing "normal" JSON around and that works fine. When I involve sinja, it seems unhappy and I can't see why.

I'm using ruby 2.3.1 (about to upgrade ruby and see if that helps) via RVM on macOS.

I appreciate the help!

getting Malformed JSON in the request body when request.body is not rewindable

I'm using Falcon as http server for sinatra app that uses sinja for json:api.
I've encountered a problem that my request.body is always malformed json.
After digging in I've found that Falcon uses Protocol::HTTP1::Body::Fixed as body class which is not rewindable. method #content? reads first 2 bytes to check if request has body. after that #deserialize_request_body always retrieves part of content, because in this case env['rack.input'] is an instance of Falcon::Adapters::Input which silently ignores rewind, because it's underlying body Protocol::HTTP1::Body::Fixed does not respond to it. I want to leave this issue here - maybe someone will looking solution for same problem.

I think it worth for PR - we can use #empty? on body if it responds to #empty?.

my bugfix is following

module SinjaPatch
  def content?
    return request.body.size > 0 if request.body.respond_to?(:size)
    return !request.body.empty? if request.body.respond_to?(:empty?)
    request.body.rewind
    request.body.read(1)
  end
end

module FalconAdaptersInputPatch
  def empty?
    @body.respond_to?(:empty?) ? @body.empty? : @body.size
  end
end

Falcon::Adapters::Input.include FalconAdaptersInputPatch
# must be added after `register Sinatra::JSONAPI` performed.
MyApp.prepend SinjaPatch

TODO

Meta

  • Documentation
    • README
    • Examples
    • RDoc/YARD
  • Tests ๐Ÿ˜ญ
    • Helpers
    • Utility Classes
    • Middleware
    • Integration
  • Feature comparisons (e.g. cf. this)
  • Performance and memory analyses
  • Swagger/schema.org/?? catalog/discovery integration
    • Support HEAD requests for method discovery and CORS

Features

  • Pass-through JSONAPI::Serializer options
    • Class level, root and defaults (e.g. set :jsonapi_serializer_options, :meta => {})
      • Inheritable?
    • In return results from actions (e.g. Post[id], :include=>:author)
    • Punch through to relationships
  • JSON:API states that the server must return 400 if the client passes unhandled/able query parameters. What's a nice DSL to express this?
  • Logging
    • Configurable progname
    • Add logging where appropriate
    • Audit halt/fail/raise codes and messages
  • More configurables in general!
    • Strictness level
    • Error messages
  • Control which relationships get passed through (or don't) on create/update
    • Enhance graft/merge to take a :passthru option that can be :create, :update, or both?
  • Handle exceptions that should be 409
  • Handle exceptions that should be 422
  • Handle exceptions that should be 404

Un-features

  • There has to be a better way to delegate requests to relationship routes and bypass the after filters on the response than this
    • Some middleware that intercepts failed requests with a certain status code and delegates to an object/class wrapped in env?
    • Sinatra::Namespace? To the rescue!
  • Keeping e.g. action_roles, action_conflicts, and relationships on the registering app's settings (class) is kind of janky. Can we do a better job of hiding this storage from the registering app? It's still on settings but now at least it's tucked into a Config object.
  • Defer extending Sinatra DSL, parameterize ACTIONS into a method? It's just clumsy and weird. Well, that's a little better.
    • Don't load unnecessary relationship helps (e.g. has-many actions on has-one routes)
  • It's possible that requiring show for update, destroy, and relationships is somewhat misguided. I can see a situation where a user has access to e.g. delete something without being able to view it. Maybe we need to abstract out a "finder" helper (or unrestricted action helper) that is used by show, update, etc.?

Known Bugs

  • Relationship dispatch on POST and PATCH is completely untested and probably buggy
    • Null foreign keys and order of operations issue
  • Can't have more than one action helper of the same name per resource block ๐Ÿ˜•
  • Version dependencies in gemspec need to be relaxed. Can we make this extension compatible with Sinatra 1.x?

Generate Swagger documentation

Enhance the resource, has_one, and has_many keywords to track routing keywords (and any custom route names, see also #8) in a global structure, e.g. _sinja.route_config, which we can then interrogate (along with _sinja.resource_config) to generate Swagger/RAML/WADL, etc. in combination with some configurables and/or on-disk template(s).

Custom routes within resource block not working

I'm trying to add a custom route (similar to get '/top10' in your README) and I only get 404s trying to access it. Using pry and inspecting the App::routes on the Sinatra application, I can see the route listed as:

[#<Mustermann::Concat:(regular:"\\/organizations(?![^\\/])" + sinatra:"/mine")>, [#<UnboundMethod: Authify::API::Services::API#`block in registered' condition>], #<Proc:0x007fc26c3ab048@/Users/jgnagy/.rvm/gems/ruby-2.3.1/gems/sinatra-2.0.0.beta2/lib/sinatra/base.rb:1643>]

The goal with the above is to add a custom route @ /organizations/mine on an otherwise normal {json:api} resource /organizations.

I've modified my simplified test-api to duplicate the issue: https://github.com/jgnagy/test-api/blob/master/lib/test/api.rb

In the test-api app above, the custom route should be /testkeys/foo, but hitting that with:

curl -i -H 'Accept: application/vnd.api+json' -H 'Content-Type: application/vnd.api+json' http://localhost:9292/testkeys/foo

yields:

HTTP/1.1 404 Not Found
Content-Type: application/vnd.api+json
X-Cascade: pass
Content-Length: 141
Server: WEBrick/1.3.1 (Ruby/2.3.1/2016-04-26)
Date: Wed, 15 Feb 2017 15:43:49 GMT
Connection: Keep-Alive

{
  "errors": [
    {
      "id": "bbf08504-d69f-44d5-bb94-b3d84d5e4911",
      "title": "Not Found Error",
      "status": "404"
    }
  ]
}

I added a vanilla /bar namespace and tested a /baz route and that works as expected:

curl -i -H 'Accept: application/vnd.api+json' -H 'Content-Type: application/vnd.api+json' http://localhost:9292/bar/baz

returning:

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
Content-Length: 519
Server: WEBrick/1.3.1 (Ruby/2.3.1/2016-04-26)
Date: Wed, 15 Feb 2017 15:34:47 GMT
Connection: Keep-Alive

{
  "data": [
    {
      "type": "testkeys",
      "id": "11",
      "attributes": {
        "name": "A Key",
        "created-at": "2017-02-15 07:34:47 -0800"
      },
      "links": {
        "self": "/testkeys/11"
      }
    },
    {
      "type": "testkeys",
      "id": "12",
      "attributes": {
        "name": "A Key",
        "created-at": "2017-02-15 07:34:47 -0800"
      },
      "links": {
        "self": "/testkeys/12"
      }
    }
  ],
  "jsonapi": {
    "version": "1.0"
  },
  "included": [

  ]
}

I admit that I might be doing something wrong, but I have more-or-less copied and pasted from the README for this test app.

Allow custom route names

Enhance the resource, has_one, and has_many keywords to take an options hash, which may include a custom route name to be used when drawing namespaces. (See also #9.)

Implement Ruby 3 support?

Hi!

I realize this might be far-fetched since the gem has not been updated in a long time, but is there any possibility that support for Ruby 3.x will be implemented?

Ruby 3 introduces breaking changes related to keyword arguments and I suspect this causes errors with Sinja. Declaring a resource causes an ArgumentError in Ruby 3.0.1. For instance, this program:

require 'sinatra/base'
require 'sinatra/jsonapi'
require 'jsonapi-serializers'

class App < Sinatra::Base
  register Sinatra::JSONAPI

  resource :foos do
  end 
end

App.run!

yields:

.../.rvm/gems/ruby-3.0.1/gems/sinatra-2.1.0/lib/sinatra/base.rb:1650:in `route': wrong number of arguments (given 4, expected 2..3) (ArgumentError)
  from .../.rvm/gems/ruby-3.0.1/gems/sinja-1.3.0/lib/sinja.rb:386:in `route'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-2.1.0/lib/sinatra/base.rb:1443:in `options'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:341:in `prefixed'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:234:in `block (2 levels) in prefixed'
  from .../.rvm/gems/ruby-3.0.1/gems/sinja-1.3.0/lib/sinja/resource_routes.rb:12:in `registered'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:249:in `block in register'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:247:in `each'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:247:in `register'
  from .../.rvm/gems/ruby-3.0.1/gems/sinja-1.3.0/lib/sinja/resource.rb:28:in `registered'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:249:in `block in register'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:247:in `each'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:247:in `register'
  from .../.rvm/gems/ruby-3.0.1/gems/sinja-1.3.0/lib/sinja.rb:356:in `block in resource'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:199:in `class_eval'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:199:in `block in new'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:189:in `initialize'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:189:in `new'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:189:in `new'
  from .../.rvm/gems/ruby-3.0.1/gems/sinatra-contrib-2.1.0/lib/sinatra/namespace.rb:219:in `namespace'
  from .../.rvm/gems/ruby-3.0.1/gems/sinja-1.3.0/lib/sinja.rb:333:in `resource'
  from sinja_demo.rb:8:in `<class:App>'
  from sinja_demo.rb:5:in `<main>'

whereas it works fine when using Ruby 2.7.x.

I think Sinja looks really nice and it would definitely suit my needs, so fingers crossed this will be considered :)

Thanks!

NoMethodError - undefined method `nullif'

I have an error trying to do has_many:

  ERROR -  NoMethodError - undefined method `nullif' for Server::ApiV3:Class:
 /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-2.0.0/lib/sinatra/base.rb:1624:in `block in compile!'
/home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-2.0.0/lib/sinatra/base.rb:1624:in `block in compile!': undefined method `nullif' for Server::ApiV3:Class (NoMethodError)
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-2.0.0/lib/sinatra/base.rb:1624:in `each_pair'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-2.0.0/lib/sinatra/base.rb:1624:in `compile!'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-2.0.0/lib/sinatra/base.rb:1601:in `route'
	from /vagrant/lib/sinja.rb:381:in `route'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-2.0.0/lib/sinatra/base.rb:1394:in `patch'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:338:in `prefixed'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:229:in `block (2 levels) in prefixed'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:338:in `prefixed'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:229:in `block (2 levels) in prefixed'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinja-1.2.5/lib/sinja/relationship_routes/has_many.rb:33:in `registered'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:244:in `block in register'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:242:in `each'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:242:in `register'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinja-1.2.5/lib/sinja/resource.rb:109:in `block (3 levels) in <module:Resource>'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:200:in `class_eval'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:200:in `block in new'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:190:in `initialize'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:190:in `new'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:190:in `new'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:220:in `namespace'
	from /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinja-1.2.5/lib/sinja/resource.rb:92:in `block (2 levels) in <module:Resource>'

It's just an simple:

      has_many :programmes do
        fetch do
          resource.programmes
        end
      end

Issue with Padrino

Hi @mwpastore,
I have an issue trying to load sinja with Padrino.
The error is:

  ERROR -  NoMethodError - undefined method `+' for #<Mustermann::Regular:"\\/channels(?![^\\/])">:
 /home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:332:in `prefixed_path'
/home/vagrant/.rvm/gems/ruby-2.4.1/gems/sinatra-contrib-2.0.0/lib/sinatra/namespace.rb:332:in `prefixed_path': undefined method `+' for #<Mustermann::Regular:"\\/channels(?![^\\/])"> (NoMethodError)

Basically the sub app loded is

require 'sinja'
require 'sinja/sequel/core'

module Server
  class ApiV3 < Padrino::Application
    #register Padrino::Mailer
    #register Padrino::Helpers
    register Sinja
    #enable :sessions

    #prerequisites << Padrino.root('api_v3', 'controller', '*.rb')

    resource :channel do

      index do
        Channel.all
      end

      # ..
    end

  end
end

Really slow require of Sinja

Hi,
Sinja has a really slow require time in padrino:

   8881.54  padrino
  10214.73  dry-types
  10736.37  grape
  22786.43  sinja

Not sure why though.

Question about filters

Hi @mwpastore,
Sorry about all the issue tickets, this is merely a question about filters.

I want to be able to do ?filter[date]=2017-09-24&filter[otherfilter][]=1&filter[otherfilter][]=2

But i'm not sure how to do it. There is both show_many and index with filters. show_many doesn't seem to do (filter_by: [:date, :otherfilter]).

What is the correct way of handling it in Sinja? The docs are quite small on the filters part.

Add examples and extensions for other DALs and ORMs

  • Relational Databases
    • ActiveRecord
  • Non-Relational Databases
    • Mongo

Guidelines:

  1. Hard-fork sinja-sequel.
  2. Rename the project and update the gemspec and file and directory names:
    • sinja-sequel.gemspec
    • lib/sinja-sequel.rb
    • lib/sinja/sequel.rb
    • lib/sinja/sequel/
    • lib/sinatra/jsonapi/sequel.rb
  3. Minimally, update core.rb to provide the basic configuration and helpers for the library in question:
    • Configuration
      • conflict_exceptions
      • not_found_exceptions
      • validation_exceptions
      • validation_formatter
    • Helpers
      • transaction
      • filter
      • sort
      • finalize
      • validate!
  4. If the library supports pagination, update pagination.rb (and its hook in core.rb):
    • Configuration
      • page_using
    • Helpers
      • page
  5. Add helpers to helpers.rb that will ease the pain of writing action helpers, if any.
  6. Rewrite the extension to generate basic action helpers for resource, has_many, and has_one, if possible.
  7. For bonus credit, import/fork the demo app and port it to your new extension.
  8. Update README.md to reflect your work and document best practices.
  9. Send me a PR to add your extension to the main Sinja README (pending a code review).
    • Alternatively, pending a code review, I would be willing to fork your repo and assume maintainership of the extension, adding you back as a contributor.

Rewrite error handling as middleware

Sinja is jumping through too many hoops to play nicely with Sinatra's built-in error-handling in conjunction with sideloading and UDFs. Let's:

  • Make halt raise errors (as it does currently)
  • Enable :raise_errors
  • Move error normalization and serialization logic to middleware
    • Does an internal call pass through middleware?
  • Don't raise from middleware
  • Allow disabling middleware for testing

Other notes:

  • Make exception config options register error handlers

I think you can do this, we will support you

Hi bro, I think your development ability is very strong. I think you can develop some self-built solos and release them. Let us learn. By the way, you can also filter out the black mining pools outside. We will actively support you

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.