Grape API (swagger doc) - Global configuration of 'desc' - ruby-on-rails

Its been an interesting an busy week. I am working on a Rails project and included Grape to implement the API.
The API has 2 sections
No auth required (no headers)
Auth required
I setup the app with and all is working...
Grape
Grape Swagger
Grape Swagger Rails
For stating that a header is required I use some thing like this...
class ProfilesApi < Grape::API
resource :profiles do
desc 'List all profiles' do
headers Authorization: {
description: 'Validates identity through JWT provided in auth/login',
required: true
}
end
get do
present User.all, with: Presenters::ProfilePresenter
end
end
end
Now the problem is that I this description in a lot of similar mountable API classes.
Is there a way that can kind of make this common (kind of inherited) so that I don't need to define it wit every Grape method.
desc 'List all profiles' do
headers Authorization: {
description: 'Validates identity through JWT provided in auth/login',
required: true
}
end
Thanks in advance and hope you guys enjoy the weekend.

Yes, there's a way. I achieve that by defining a method in class API so that it's accessible in everything that inherits from API. Something like:
module Myapp
class API < Grape::API
def self.auth_headers
{ Authorization: { description: 'Validates identity through JWT provided in auth/login',required: true}}
end
end
end
And you access it like that:
desc "List all profiles", {
headers: Myapp::API.auth_headers
}
Of course, there're much more ways but they depend on your implementation.

i think this may be an updated version of grape-api thing, I've had to get this working with :
desc 'My description text' do
headers Authorization: {description: "pass the access token as Bearer", required: true }
end

Related

Getting CoinMarketCap API to work with Ruby on Rails 2020?

Before coinmarketcap made their API into tiered free/payed you could get it working with
class Currency < ApplicationRecord
def current_price`
url = 'https://api.coinmarketcap.com/v1/ticker/'
request = HTTParty.get(url + self.slug)
response = JSON.parse(request.body)
end
end
But now it requires an API key which you can get on the basic free tier but I'm at a loss as to where to implement the API key in the above code?
Like I know i need a get and to include the API key but they only mention how to do that with Python C#
The docs clearly state that:
You can supply your API Key in REST API calls in one of two ways:
Preferred method: Via a custom header named X-CMC_PRO_API_KEY
Convenience method: Via a query string parameter named CMC_PRO_API_KEY
With HTTParty providing headers is trivial:
url = 'https://pro-api.coinmarketcap.com/v1/ticker/'
request = HTTParty.get(url + self.slug,
headers: { "X-CMC_PRO_API_KEY" => Rails.application.credentials.coinmarketcap[:pro_api_key] }
)
But you really should avoid doing HTTP calls from your model as it already has way to many responsibilities. Create a separate class instead that touches the application boundary:
class CoinMarketCapClient
include HTTParty
format :json
base_uri "https://pro-api.coinmarketcap.com"
attr_reader :api_key
def intialize(api_key:)
#api_key = api_key
end
def ticker(slug, **opts)
self.class.get("/ticker/#{slug}", headers: {
"X-CMC_PRO_API_KEY" => api_key
})
end
end

Table dosen't exists while setting up new application with grape mounted application

I have mounted grape gem over exsting application & in kept grape api related changes in
app >> controller >> api
directory. (which is auto loaded. No code is written for autoloading)
And in that I have code like fetching values form the database table.
module API
module V1
class Users < Grape::API
include API::V1::Defaults
resource :users do
desc 'Creates a User'
params do
requires :role_id,
type: Integer,
values: Role.all.collect { |role| role.id },
desc: 'Role ID'
Here Role.all getting called while setting up new application, which is doesn't exists yet.
But while setting up new application, when I run "rake db:migrate" it gives table doesn't exists error.
How can I stop auto loading of the "api" folder inside controller while setting up new application so that it will not get called.
Or how can I handle above scenario.
& application.rb file where defined grape
module Api
class Application < Rails::Application
config.middleware.use Rack::Cors do
allow do
origins '*'
resource '*', headers: :any, methods: [:get,
:post, :put, :delete, :options]
end
end
end
end
Thanks in advance.
Above problem was solved by using lazy evaluation of the values.
Used proc for grape values
The :values option can also be supplied with a Proc, evaluated lazily
with each request.
and reframed code will be as below
module API
module V1
class Users < Grape::API
include API::V1::Defaults
resource :users do
desc 'Creates a User'
params do
requires :role_id,
type: Integer,
values: -> { Role.all.collect { |role| role.id } },
desc: 'Role ID'
We can also use class methods inside proc to fasten the query.

Share desc and type across Grape and Grape Entity gems

What I'm trying to do is to reuse type and description across grape and grape-entity gems.
In the documentation I read the following:
You can use entity documentation directly in the params block with using: Entity.documentation.
module API
class Statuses < Grape::API
version 'v1'
desc 'Create a status'
params do
requires :all, except: [:ip], using: API::Entities::Status.documentation.except(:id)
end
post '/status' do
Status.create! params
end
end
end
This allows me to use field description and field type from the documentation defined in the Grape Entity.
Whenever I define an API that requires only 1 field though, I need to do something like this (which I find kind of dirty):
given:
module Entities
class Host < Grape::Entity
expose :id
# ... exposing some other fields ...
expose :mac_address, documentation: { type: String, desc: "The mac address of the host" }
expose :created_at, documentation: { type: DateTime, desc: "Record creation date" }
expose :updated _at, documentation: { type: DateTime, desc: "Record update date" }
end
end
I can do:
params do
requires :mac_address, type: V1::Entities::Host.documentation[:mac_address][:type], desc: V1::Entities::Host.documentation[:mac_address][:desc]
end
I don't like the above solution mainly for 2 reasons:
I don't like to use the field "type" of an helper that was meant to support documentation generation.
It is cumbersome.
Is there a better way to share type and description across the 2 gems?
You can do like this
module API
class Statuses < Grape::API
version 'v1'
desc 'Create a status' do
params API::Entities::Status.documentation.except(:created_at, :updated _at)
end
post '/status' do
Status.create! params
end
end
end
This will give you only mac_address as a params not all.

OmniAuth OAuth2 how to access to a strategy -> client? so that I can refresh token?

background
I have an omniauth-oauth2 subclass strategy working on my rails app. When to refresh access_token, I see I need to create OAuth2::AccessToken. But to create it, it seems it requires OAuth2::Client which I think can obtain from "omniauth-oauth2 subclass strategy."
found this solution Refresh token using Omniauth-oauth2 in Rails application
This is how they solved to obtain a strategy
# the initial param:nil is meant to be a rack object, but since
# we don't use it here, we give it a nil
strategy = OmniAuth::Strategies::YOUR_PROVIDER.new nil, client_id, client_secret
client = strategy.client
your_expired_at_from_your_provider = Time.now.to_i
hash = {
access_token: "your access_token from your provider",
refresh_token: "your refresh_token from your provider",
expires_at: your_expired_at_from_your_provider,
}
access_token_object = OAuth2::AccessToken.from_hash(client, hash)
access_token_object.refresh!
https://github.com/omniauth/omniauth/blob/v1.6.1/lib/omniauth/strategy.rb#L132
https://github.com/intridea/omniauth-oauth2/blob/v1.4.0/lib/omniauth/strategies/oauth2.rb#L35
https://github.com/intridea/oauth2/blob/master/lib/oauth2/access_token.rb#L12
https://github.com/intridea/oauth2/blob/v1.4.0/lib/oauth2/access_token.rb#L82
problem
What I don't understand is, it looks a bit of hacky ways to create a strategy by giving nil to the first argument.
"omniauth-oauth2 subclass strategy" is in rack (like the image below), so I am thinking there is a way to access to a strategy from rack middleware, somewhere?
question
Is creating a strategy like above is the only way to refresh token?
strategy -> client -> access_token_object -> refresh!
I could not find a right way, but make a workaround for my custom omniauth strategy:
class MyOrg < OmniAuth::Strategies::OAuth2
#...
info do
{
'email' => extra['user'].try(:[], 'email'),
# ...
'get_org' => Proc.new do
get_org
end
}
end
def get_org
#org ||= begin
org_id = extra['user'].try(:[], 'org_id')
access_token.get(options[:client_options][:site] + "/v1/orgs/#{org_id}").parsed
end
end
end
Then call it as:
hash[:info][:get_org].call
I leveraged the oauth2 gem to do the refreshing. Here's a complete solution for using the omniauth strategy to access the google APIs: https://stackoverflow.com/a/57191048/2672869

authenticate grape api with doorkeeper

I have currently configured Devise,Doorkeeper and grape in my rails application.
Devise and Doorkeeper are configured so that I can register and login with Devise on the website and Doorkeeper provides oAuth endpoints that can create tokens.
How can I add a token to a HttpRequest and protect the grape API with it?
Edit:
So I tried to implement the Winebouncer implementation Tom Hert suggested.
I followed the instructions on https://github.com/antek-drzewiecki/wine_bouncer
I have installed the gem.
I have defined config/initializers/wine_bouncer.rb as the following.
WineBouncer.configure do |config|
config.auth_strategy = :default
config.define_resource_owner do
User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token
end
end
I have registered Winebouncer as middleware in grape in my base api controller.
app\controllers\api\base.rb
module API
class Base < Grape::API
mount API::V1::Base
use ::WineBouncer::OAuth2
end
end
I mounted my projects controller in my V1 base controller
app\controllers\api\v1\base.rb
module API
module V1
class Base < Grape::API
mount API::V1::Projects
end
end
end
And this is my projectscontroller
app\controllers\api\v1\projects.rb
module API
module V1
class Projects < Grape::API
version 'v1'
format :json
resource :projects do
desc "Return list of projects" , auth: { scopes: [] }
get do
Project.all
end
end
end
end
end
To be honest I don't yet know how the ", auth: { scopes: [] }" in the description is suppossed to work. And how to add the token to a request, but I would expect my request but be blocked when no token is added. But the the request is still producing the json data.
I found quite interesting code here: https://github.com/fuCtor/grape-doorkeeper
It seems to be still maintained. But I think this is good just to get the idea of what is going on there.
I would recommend this: https://github.com/antek-drzewiecki/wine_bouncer
As said on the page:
Protect your precious Grape API with Doorkeeper. WineBouncer uses
minimal modification, to make the magic happen.
obedeijn, i just noticed your question on stackoverflow.
WineBouncer works just like doorkeeper, it looks for the Authorizations header with a "Bearer x" where x is the token.

Resources