Ruby on Rails client app and api app - ruby-on-rails

I have a Ruby on Rails application that serves as an API to other web apps. This API has a controller named AcademicTitlesController, in this controller i have a method active_academic_titles.
class AcademicTitlesController < ApplicationController
def index
....
end
def active_academic_titles
#active_academic_titles = AcademicTitle.where(:academic_unit_id => params[:academic_unit_id]).order('name')
end
end
this method renders a rabl view.
On my routes file i have this
get 'active_academic_titles' => 'academic_titles#active_academic_titles'
Now on the client web application i want to display the json format text(rabl) with html and erb.
On the client web app i have i model
class Api < ActiveResource::Base
MODE = 'dev' # api or dev
self.site = 'http://localhost:3003/'
self.format = :json
end
This model makes the connection to the API, and each model in the client app inheris from this Api model.
My question is this, what do i need or add to my client app so that i can get the data from the api view?

I am not what your goal is, but to my understanding the controller will render the view unless otherwise specified. So if you want it to act as an API just return in JSON form from the server for example add render :json => somedata

You can make changes in Api class as
class Api < ActiveResource::Base
MODE = 'dev' # api or dev
self.site = 'http://localhost:3003/'
self.element_name = "" # You can also use self.collection_name = "" instead
self.format = :json
end
And to access json from get 'active_academic_titles' => 'academic_titles#active_academic_titles'
You need to write
Api.get('active_academic_titles')
Note: Make sure that server port is 3003 and server sends json if at endpoint http://localhost:3003/active_academic_titles.json
Extra Info:
This would not be the most effective way to use of ActiveResource. Generally we should use class name of class inheriting from ActiveResource i.e 'Api' in your case, to be same as endpoint i.e AcademicTitle and also should avoid using self.element_name = "" and instead create a endpoint as http://localhost:3003/academic_titles/active_academic_titles.json
Reference link:
http://api.rubyonrails.org/v3.2.6/classes/ActiveResource/Base.html

Related

HTTParty: Post action is resulting in error Net::HTTPServerException (403 "Forbidden")

I am trying to implement post action using httparty gem and this is what I have. I am running everything in docker and I have code below that will run as active job. I is in one service and I am trying to make post to api in other service. I am able to do get but not having any luck with post. I looked and searched a lot online but I am not sure what is it I am doing wrong. I always get error 403 at self.class.post line. I also tried to do a postman call to api and I am able to hit the api but with the code below its not even reaching to the other service.
Any help is appreciated. Thanks.
require 'uri'
class CustomerProductAPI
include HTTParty
format :json
def initialize(customer_product_id)
#customer_product = CustomerProduct.find(customer_product_id)
#customer = Customer.find(#customer_product.student_id)
#product = Product.find(#customer_product.product_id)
self.class.base_uri environment_based_uri + '/customer_product_api'
end
def create_customer_product
uri = URI(self.class.base_uri + "/customer/#{customer.id}")
self.class.post(uri, body: body_hash).response.value
end
private
attr_reader :customer_product, :customer, :product
def body_hash
{
token: ENV['CUSTOMER_PRODUCT_API_TOKEN'],
customer: customer.name,
product: product.name,
}
end
def environment_based_uri
ENV['CUSTOMER_PRODUCT_URL']
end
end
While we can't actually be sure of exactly what the server thats accepting the response expects you're definately doing quite a few non-idiomatic things here which will aggrevate trouble shooting.
base_uri should just be set in the class body. Not in initialize for each instance. You also do not need to construct a URI with HTTParty. Just pass a path and it will construct the request uri relative to the base_uri.
When getting configuration from ENV use ENV.fetch instead of the bracket accessors as it will raise a KeyError instead of just letting a nil sneak through.
Your HTTP client class should not be concerned with querying the database and handling the potential errors that can occur if the records cannot be found. That should be the responsibility of the controller/job/service object that calls the client. Since you're only actually using three simple attributes it doesn't actually need records at all as input and its actually better that it doesn't have to know about your models and their assocations (or lack thereof in this case).
class CustomerProductAPI
# lets you stub/inspect the constant
CUSTOMER_PRODUCT_URL = ENV.fetch('CUSTOMER_PRODUCT_URL') + '/customer_product_api'
include HTTParty
format :json
base_uri CUSTOMER_PRODUCT_URL
def initialize(id:, product_name:, customer_name:)
#id = id
#product_name = product_name
#customer_name = customer_name
end
def create_customer_product
self.class.post("/customer/#{#id}", body: {
token: ENV.fetch('CUSTOMER_PRODUCT_API_TOKEN'),
customer: #customer_name,
product: #product_name
})
# don't return .response.value as it will make error handling impossible.
# either handle unsuccessful responses here or return the whole response
# for the consumer to handle it.
end
end

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

How to fetch local JSON data from Rails e.g bookings.json

Hi all very noob question.
I'm trying to store data in a react calendar but it needs to store it using JSON.
I've noticed that when you scaffold, rails automatically also gives you a JSON version.
In my case - http://localhost:3000/users/1/bookings.json
Which returns [{"first_name":"Fake Name","booking_time":"2019-04-22T02:03:00.000Z","pick_up_time":"2019-04-22T02:03:00.000Z"}] in JSON.
I know how to fetch JSON data from a external URL and parse it through however all these external URL's are public whereas in my case the bookings are private.
Is there a way for me to fetch from bookings.json and store it in a variable and also by making it private where I wouldn't need to publicise it?
class HomeController < ApplicationController
def dashboard
#lookup_booking = ???("/users/1/bookings.json")???
end
end
React dashboard
<%= react_component("Booking", { booking: #lookup_booking})%>
You could make a local request to the Bookings JSON endpoint the same way you'd make any external request - using something like HTTParty or Faraday might work:
#lookup_booking = HTTParty.get(user_bookings_url(1))
But this won't be authenticated, so it'll need the same authentication as any other request.
It's a little weird to do it this way unless whatever is generating the bookings is a separate service, or if you want it to be one. If you're going to be using one codebase, you might want to do something similar to what arieljuod suggested in the comments, and simply share the code.
You could break the BookingsController code into an ActiveSupport::Concern or a module, or a Service Object (or, more simply, a method on the User class) and that would then allow you to cleanly share the code between the BookingsController and HomeController. It might look something like this:
# app/services/lookup_user_bookings.rb
class LookupUserBookings
def self.bookings_as_json(user_id)
# complicated logic to find user bookings goes here...
bookings.as_json
end
end
# bookings_controller.rb
class BookingsController
def index
#bookings = LookupUserBookings.bookings_as_json(current_user)
render json: #bookings
end
end
# home_controller
class HomeController
def dashboard
#bookings = LookupUserBookings.bookings_as_json(current_user)
end
end
# dashboard.html.erb
<%= react_component("Booking", { booking: #bookings.to_json })%>

LinkedIn API Ruby/OAuth2 integration

I'm trying to write an integration with the LinkedIn API for my Rails app using OAuth2 but I'm having a hard time figuring out how to make authenticated API calls. I can't seem to find any resources on the topic that don't rely on the linkedin gem. I already have that gem installed in my app, but need to make some API calls that are not possible using that gem.
What I want to do is set up a controller with some endpoints that my Angular client can ping. When the client pings the controller, it should in turn ping the LinkedIn API. Once it gets a response it will perform some operations to store the information in the database and then pass it along to the Angular client.
I found a single code sample in Ruby from LinkedIn, which I'm attempting to modify into a controller for my app. Here is my controller:
class Employers::LinkedinApiController < ApplicationController
require 'net/http'
layout 'employers/layouts/application'
before_action :authorize_viewer
skip_after_filter :intercom_rails_auto_include
REDIRECT_URI = 'http://app.mysite.local:3000/linkedin/callback'
STATE = SecureRandom.hex(15)
CLIENT = parameterize(client.auth_code.get_token(STATE, redirect_uri: REDIRECT_URI))
def get_update(update_key=nil)
company = current_employer.company
update_key = company.social_media_posts.where("linkedin IS true AND linkedin_id IS NOT NULL").last.linkedin_id
page_id = company.linkedin.page_id
end_point = "https://api.linkedin.com/v1/companies/#{page_id}/updates/key=#{update_key}?format=json"
access_token = OAuth2::AccessToken.new(client, CLIENT['token'], {
:mode => :header,
:header_format => 'Bearer %s'
})
response = access_token.get(end_point)
# Do something with the response to save it to the database
# Pass response on to Angular client
respond_to do
format.json { render json: response }
end
end
private
#Instantiate your OAuth2 client object
def client
OAuth2::Client.new(
ENV['LINKEDIN_APP_ID'],
ENV['LINKEDIN_APP_SECRET'],
authorize_url: "/uas/oauth2/authorization?response_type=code",
token_url: "/uas/oauth2/accessToken",
site: "https://www.linkedin.com"
)
end
def parameterize(string)
parameters = {}
string.split('?')[-1].split('&').each do |param|
parameters[param.split('=')[0]] = param.split('=')[-1]
end
parameters
end
def authorize_viewer
if !employer_signed_in?
redirect_to new_employer_session_path
end
end
end
In the code sample from LinkedIn, the only way to get data from the accept method is to ping the authorize method, whose callback points to the accept method. What I can't figure out is how I can ping the get_update method in my controller and make an authenticated API call, without having to set up an authorize method that calls back to get_update.
How can I do this?

Posting xml to a rest api in rails

I need to post some xml info to a restful api can anyone give me a clue of how to do this? I'm using rails.
In rails, using ActiveResource, you do it like this:
class PersonResource < ActiveResource::Base
self.site = "http://api.people.com:3000/"
self.proxy = "http://user:password#proxy.people.com:8080"
end
ryan = Person.new(:first => 'Ryan', :last => 'Daigle')
# the next line posts this object serialized to xml to the configured url
ryan.save # => true
http://api.rubyonrails.org/classes/ActiveResource/Base.html
If the site you are posting to has a custom API (not active resource) you must use Net:HTTP

Resources