I configured json-resouce-api.
But the self link generated by json-resource-api is wrong.
The code seems to checks the module hierarchy of the resource class and completely ignores how rails generates the routes.
routes.rb
require 'api_constraints'
Rails.application.routes.draw do
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
# resources :subscriptions, only: [:index, :new, :create]
# jsonapi_resources :subscriptions, only: [:index, :new, :create]
jsonapi_resources :subscriptions
end
end
resouces/api/V1/subscription_recource.rb
class Api::V1::SubscriptionResource < JSONAPI::Resource
attributes :id, :third-service_id, :created_at, :updated_at
model_name 'Subscription'
# def custom_links(options)
# {self: nil}
# end
end
What I got => http://api.localhost.local:3000/api/v1/subscriptions/1
but it should be http://api.localhost.local:3000/subscriptions/1
How can I fix this?
UPDATE
rake routes
[DUPLICATE ATTRIBUTE] `id` has already been defined in SubscriptionResource.
Prefix Verb URI Pattern Controller#Action
api_v1_subscriptions GET /subscriptions(.:format) api/v1/subscriptions#index {:format=>:json, :subdomain=>"api"}
POST /subscriptions(.:format) api/v1/subscriptions#create {:format=>:json, :subdomain=>"api"}
api_v1_subscription GET /subscriptions/:id(.:format) api/v1/subscriptions#show {:format=>:json, :subdomain=>"api"}
PATCH /subscriptions/:id(.:format) api/v1/subscriptions#update {:format=>:json, :subdomain=>"api"}
PUT /subscriptions/:id(.:format) api/v1/subscriptions#update {:format=>:json, :subdomain=>"api"}
DELETE /subscriptions/:id(.:format) api/v1/subscriptions#destroy {:format=>:json, :subdomain=>"api"}
stripe_event /stripe-events StripeEvent::Engine
UPDATE2
This issue is totally the same as github.com/cerebris/jsonapi-resources/issues/591
Monkey Pack can be applied, but it's little bit risky.
For now ( 2016, Oct 5th ), I couldn't find any other ways than
namespace :api do
namespace :v1 do
jsonapi_resources :subscriptions
end
end
You can try it:
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: "" do
namespace :v1, path: "" do
jsonapi_resources :subscriptions
end
end
Related
I've got controller below controller:
module Api
module V1
module Account
class PasswordsController < Devise::PasswordsController
respond_to :json
def create
# some code
end
def update
# some code
end
def is_token_valid
::Account.find_by(reset_password_token: params[:token])
end
end
end
end
end
I want to setup an endpoint for front-end dev where he will check if reset_password_token exist in DB (devise here). I don't know how to made a path like: /api/v1/account/password/is_token_valid
My routes:
namespace :api, defaults: { format: :json } do
namespace :v1 do
namespace :account do
devise_for :accounts, singular: 'account', path: '', controllers: {
sessions: 'api/v1/account/sessions',
registrations: 'api/v1/account/registrations',
confirmations: 'api/v1/account/confirmations',
passwords: 'api/v1/account/passwords',
}
end
resource :account, only: [:show]
EDIT
routes:
root#b2faabb49f91:/usr/src/app# rake routes | grep account
new_account_session GET /api/v1/account/sign_in(.:format) api/v1/account/sessions#new {:format=>:json}
account_session POST /api/v1/account/sign_in(.:format) api/v1/account/sessions#create {:format=>:json}
destroy_account_session DELETE /api/v1/account/sign_out(.:format) api/v1/account/sessions#destroy {:format=>:json}
new_account_password GET /api/v1/account/password/new(.:format) api/v1/account/passwords#new {:format=>:json}
edit_account_password GET /api/v1/account/password/edit(.:format) api/v1/account/passwords#edit {:format=>:json}
account_password PATCH /api/v1/account/password(.:format) api/v1/account/passwords#update {:format=>:json}
I have crafted some nested routes files using the below in my config/initializers:
class ActionDispatch::Routing::Mapper
def draw(routes_name, sub_path=nil)
if sub_path.present?
instance_eval(File.read(Rails.root.join("config/routes/#{sub_path}/#{routes_name}.rb")))
else
instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb")))
end
end
end
However, when used like this in the routes file:
Rails.application.routes.draw do
scope :api do
["v1"].map { |version| draw :base, "api/" + version }
end
end
The routes there do not appear nested as api/users etc. Not sure why the scope is getting ignored there.
EDIT: More Detailed Example
routes.rb
namespace :api, defaults: { format: :json } do
Rails.application.routes.draw do
["v1"].map { |version| draw :base, "api/" + version }
end
end
base.rb
namespace :v1 do
Rails.application.routes.draw do
[:identity].map { |path| draw path, "api/v1"}
end
end
identity.rb
Rails.application.routes.draw do
# Ensures proper namespace when fetching controllers, but does not add to path for routes
scope module: :identity do
namespace :users do
put '/', action: :update
patch '/', action: :update
get 'user_from_token', action: :update
end
end
end
Rails.application.routes.draw was redundant
# config/routes.rb
Rails.application.routes.draw do
scope :api do
["v1"].map { |version| draw :base, "api/" + version }
end
end
# config/routes/api/v1/base.rb
namespace :v1 do
# Rails.application.routes.draw do
[:identity].map { |path| draw path, "api/v1"}
# end
end
# config/routes/api/v1/identity.rb
# Rails.application.routes.draw do
# Ensures proper namespace when fetching controllers, but does not add to path for routes
scope module: :identity do
namespace :users do
put '/', action: :update
patch '/', action: :update
get 'user_from_token', action: :update
end
end
# end
$ rake routes
Prefix Verb URI Pattern Controller#Action
v1_users PUT /api/v1/users(.:format) v1/identity/users#update
PATCH /api/v1/users(.:format) v1/identity/users#update
v1_users_user_from_token GET /api/v1/users/user_from_token(.:format) v1/identity/users#update
rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show
rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
I'm working on a Rails 5.2 API and I'm a bit confused about how to do a correct routing.
When I call my API I'm writing:
localhost:3001/?type=arrival
what I would like to write:
localhost:3001/flights?type=arrival
But I don't know what I should change and also if this can be correct as good practice doing an API.
Or should be something like: URL/api/v1/flights?type=...
I'm not sure what will be the best thing to do and how to change.
My routes.rb:
root to: "api/v1/flights#index"
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/api' do
namespace :v1 do
resources :flights, only: [:index, :destroy_all]
end
end
When setting up an API you rarely want to have both a subdomain constraint and /api in the path:
http://api.example.com/api/v1/flights
Seems kind of silly. I mean what else do you have on the api subdomain?
So either go with a path:
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :flights, only: [:index]
end
end
Or a subdomain:
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: nil do
namespace :v1 do
resources :flights, only: [:index]
end
end
To add additional REST verbs to the collection (like :destroy_all) you pass a block to resources:
resources :flights, only: [:index] do
delete '/', action: :destroy_all, on: :collection
end
How can I have a portion of a route Capitalized? For example I have a route scim/v2/user but I'd like it to be scim/v2/User (User capitalized). How can I achieve this while still using resource.
Routes file:
namespace :scim, defaults: { format: :json } do
namespace :v2 do
resource :user, only: [:create, :update, :show]
end
end
When I run $rake routes, I get this:
scim_v2_user POST /scim/v2/user(.:format) scim/v2/users#create {:format=>:json}
GET /scim/v2/user(.:format) scim/v2/users#show {:format=>:json}
PATCH /scim/v2/user(.:format) scim/v2/users#update {:format=>:json}
PUT /scim/v2/user(.:format) scim/v2/users#update {:format=>:json}
I'd like to either have the routes be /scim/v2/User or have them remain the same but have a way of mapping /scim/v2/User to /scim/v2/user.
By default resource wants a direct mapping between the resource name and the controller, but you can simplify use an upper case resource name and manually specify the controller to get around this:
namespace :scim, defaults: { format: :json } do
namespace :v2 do
resource :User, :controller => 'users', only: [:create, :update, :show]
end
end
Generates
Prefix Verb URI Pattern Controller#Action
scim_v2_User GET /scim/v2/User(.:format) scim/v2/users#show {:format=>:json}
PATCH /scim/v2/User(.:format) scim/v2/users#update {:format=>:json}
PUT /scim/v2/User(.:format) scim/v2/users#update {:format=>:json}
POST /scim/v2/User(.:format) scim/v2/users#create {:format=>:json}
I was able to solve this by manually specifying the path and controller. I specified that path should be Users (capitalized). Below is code in my routes file:
namespace :scim, defaults: { format: :json } do
namespace :v2 do
resources :user,
path: "Users",
controller: "users",
only: [:create, :update, :index, :show]
end
end
I'm working with a web api in rails 4.1.8 and i'm doing some testing but always is returning me
Failure/Error: post '/v1/users', subdomain: 'api', user: #user.to_json, format: :json
ActionController::UrlGenerationError:
No route matches {:action=>"/v1/users", :controller=>"api/v1/users", :format=>:json, :subdomain=>"api", :user=>"JSON_OBJ"}
but if i test it in the browser with Advance Rest Console it works, i don't know what i'm doing wrong
here is me code
routes.rb
namespace :api, path: '/', constraints: {subdomain: 'api'}, defaults: { format: :json } do
namespace :v1 do
with_options except: [:edit, :new] do |except|
except.resources :users do
collection do
post 'login'
post 'showme'
end
end
except.resources :products
except.resources :locations
end
end
end
and my controller spec
module API
module V1
describe UsersController do
before do
request.host = "api.example.com"
expect({:post => "http://#{request.host}/v1/users"}).to(
route_to( controller: "api/v1/users",
action: "create",
subdomain: 'api',
format: :json
)
) # => PASS
# token expectations
#auth_token = allow(JWT::AuthToken).to(
receive(:make_token).and_return("mysecretkey")
)
expect(JWT::AuthToken.make_token({}, 3600)).to eq("mysecretkey")
end
describe "Create User" do
before(:each) do
#user = FactoryGirl.attributes_for :user
end
it 'should return a token' do
post '/v1/users', subdomain: 'api', user: #user.to_json, format: :json # Error
response_body = JSON.parse(response.body, symbolize_names: true)
expect(response_body['token']).to eql "mysecretkey"
end
end
end
end
end
rake routes
login_api_v1_users POST /v1/users/login(.:format) api/v1/users#login {:format=>:json, :subdomain=>"api"}
showme_api_v1_users POST /v1/users/showme(.:format) api/v1/users#showme {:format=>:json, :subdomain=>"api"}
api_v1_users GET /v1/users(.:format) api/v1/users#index {:format=>:json, :subdomain=>"api"}
POST /v1/users(.:format) api/v1/users#create {:format=>:json, :subdomain=>"api"}
api_v1_user GET /v1/users/:id(.:format) api/v1/users#show {:format=>:json, :subdomain=>"api"}
PATCH /v1/users/:id(.:format) api/v1/users#update {:format=>:json, :subdomain=>"api"}
PUT /v1/users/:id(.:format) api/v1/users#update {:format=>:json, :subdomain=>"api"}
DELETE /v1/users/:id(.:format) api/v1/users#destroy {:format=>:json, :subdomain=>"api"}
As described in http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html which is referenced in https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs, the first argument to post in your RSpec example is the name of the controller method to be called (i.e. :create in your case), not the route to that method.