Rails API: make application controller serve JSON - json

This tutorial, React + Flux Backed by Rails API, by Fancy Pixel, says:
Now we need to change the application controller so that it inherits
from ActionController::API, and kiss the protect_from_forgery goodbye.
Since we are serving only JSON, it makes sense to add
respond_to :json
to the applciation controller, helps DRYing all out. While we are at
it, we might as well delete the assets and views folders, we won’t
need them.
I am not sure about what I am supposed to do.
This is what my application controller looks like:
class ApplicationController < ActionController::API
respond_to :json
end
Is it correct?
Also, should I keep or remove protect_from_forgery with: :null_session :
class ApplicationController < ActionController::API
protect_from_forgery with: :null_session
respond_to :json
end

To communicate from any Rails Backend (not even necessarily just an API) all you have to do is write the following in your API's controller(s):
class Api::V1::SearchController < ApplicationController
#Run an Authentication Method on their API Key
before_action :authenticate
def search
#Performs your backend logic
content_array = get_content_method
render :json => content_array
#Renders up a JSON that you can retrieve with an HTTP get request
end
end
Also on the topic of serving data, you can send your params in an obscured JSON, allowing you to hide your API Key, thus protecting you from unwanted access. My favorite way of doing this is by using the .to_query method to send over the necessary params. I have done it like so:
BASE_URL = "http://yourwebsite.com/search?"
def generate_search_url(params)
BASE_URL + params.to_query + "&Api-Key=#{ENV['API-KEY']}"
end
This way you can work with the data you're given just like any other params, but it would be more difficult to abuse the API without a granted key.

Related

Rails Web App. Help display OpenWeather API icons or json on home page

I'm trying to simply make a third party api call to Open Weather and display the result on the home page of my web app. I am able to successfully make the request in my console by running the file but I cannot get it to show up on my view file. Oddly enough I cannot find anything about actually displaying the returned json in ruby on rails.
<h1 class="hello"><%= #weather.posts %></h1>
The index.html (home)
require 'rubygems'
require 'httparty'
class Weather
include HTTParty
base_uri 'http://https://api.openweathermap.org/'
def posts
self.class.get('https://api.openweathermap.org/data/2.5/weather?q=Memphis&appid=36197f2c3289996e0f0fd7a5ef7d851c')
end
end
#weather = Weather.new
puts #weather.posts
The .rb model
class WeatherController < ActionController::Base
response = HTTParty.get('http://https://api.openweathermap.org/data/2.5/weather?q=Memphis&appid=36197f2c3289996e0f0fd7a5ef7d851c')
JSON.parse response, symbolize_names: true
end
The Controller
So when I start up the server I basically get a blank where the weather icon or json should be. No errors, just blank. Considering how little I could find, it seems this might be helpful to others if answered clearly. Thank You
The code in your controller does not look correct. I guess you want to have something like:
class WeatherController < ActionController::Base
def index
#weather = Weather.new
end
end

Call a controller method via button in Rails

I want to create a button in views/companies/edit.html.haml for destroy_referrals method, but I can't get the button to work. To call the action on a button I tried to create a new route, but I am still receiving the error The action 'destroy_referrals' could not be found for CompaniesController
I created a destroy_referrals method in Admin::CompaniesController,
def destroy_referrals
load_company
#company.destroy_referrals
flash[:notice] = 'Referrals deleted'
redirect_to :action => :index
end
it manipulates the company.rb model to find the referrals from users with the same company_id and destroys them,
def destroy_referrals
Referral.joins(:user).where("users.company_id = ?", self.id).destroy_all
end
Some things I've tried below...
routes.rb
post '/destroy_referrals' => 'admin/companies#destroy_referrals', :as => 'destroy_referrals'
views/companies/edit.html.haml
= button_to 'Destroy Referrals', destroy_referral_path
= button_to 'Destroy Referrals', admin_company_destroy_referral_path(#company)
HTML links (not “buttons”) aren't wired up to call “methods” directly.
The button_to method creates an HTML button, which behaves similarly to a link, but with different visual styles.
Ruby has methods, which are a common programing construct.
HTML links have an href attribute that a browser can follow by making a subsequent HTTP request to.
When a browser sends a request to your Rails application, the application uses your routes to determine how to handle that request.
The conventional way to handle a request in Rails is to route it to a controller action.
A RESTful request to /products/1 might, by convention, route to the show action on the ProductsController.
In Rails, an “action” is created by defining a method on the controller.
RESTful routes reflect the representational state transfer paradigm, involving reading, listing, creating, updating, and deleting records/objects.
To delete (or destroy) a record, RESTful convention would be to send a DELETE request to the Rails application, which would be routed to the relevant controller's destroy action, defined by the destroy method.
You can create an HTML link that results in a browser sending such a request using the link_to helper and specifying method: :delete in the parameters.
You can define a route instructing Rails to handle such a request using the built in “resourceful” routing: e.g. resources :products. Alternately, an explicit route can be defined: delete '/products/:id' => 'products#destroy'.
This all said, the error Rails is giving you is that it can't find the destroy_referrals method in CompaniesController. Defining methods in Admin::CompaniesController won't do anything to resolve this.
#Swards the route needed to be delete
'companies/:id/destroy_referrals' => 'companies#destroy_referrals',
:as => 'destroy_referrals'
I thought it might be a simple fix. Do you still need the admin namespace?
Consider this, instead of creating a named route
namespace 'admin' do
resources :companies do
delete 'destroy_referrals', :on => :member, :as => 'destroy_referrals'
end
end
And refer to it as admin_destroy_referrals_company_path
= button_to 'Destroy Referrals', admin_destroy_referrals_company_path(#company), :method => :delete

Single schema multi tenancy in a Rails Web API

I have a single client application - Angular JS frontend and a Rails API backend with a MySQL DB. I'm trying to convert the application into a single schema multi tenant application. I've done a lot of reading and:
I do not want to use a gem if possible - Apartment, which is multiple schemas and uses Postgres doesn't fit the bill, and act_as_tenant seems to use Thread.current to identify the tenant which I do not want to do.
I have read that default_scope should not be used as well, for a host of reasons I won't get into here.
I'm passing a tenant token in the request header from the frontend to the Rails backend, and using the tenant token I identify the tenant in my ApplicationController. I'm now figuring out the best way to both read and write data so that is associated with the tenant that made the request.
Having ruled out the options above, the only option I can see is to go into all of my controller methods and update them wherever data is being written and read. I would much rather apply some sort of callback to each of my Models, so that the tenant id is always written when data is being written and the tenant id is always used as a filter whenever data is being read.
Given that I cannot access the tenant token in the models, I am not sure how to proceed with this other than updating all my controller methods, which would be an arduous and mistake prone process.
Thanks in advance!
Not using default_scope is a good idea - it behaves as sort of a black box and can wreck havoc down the line especially if you ever do anything with paranoid deletion.
One way to do what you asked is to use thread_mattr_accessor. You can define the tenant_id token at the beginning of the web request, and then access it through the class attribute for the duration of the web request. This creates a thread-safe attributes accessor on your tenant model.
In your controller you can detect the tenant for the current request (using subdomain or token) and set the Token.current_id variable. This variable will be available for the duration of the request. Note that it will not automatically be available for any background jobs or other processes because the variable is set inside the current thread.
This method is demonstrated using scopes in this RailsCast, but you don't have to use scopes. You can set a helper method like current_tenant and then explicitly scope all your queries like current_tenant.posts.
# models/tenant.rb
class Tenant < ApplicationRecord
thread_cattr_accessor :current_id
# ...
end
# controllers/application_controller.rb
class ApplicationController < ActionController::Base
around_action :scope_current_tenant
private def scope_current_tenant
Tenant.current_id = current_tenant.id
yield
ensure
Tenant.current_id = nil
end
def current_tenant
#current_tenant ||= Tenant.find_by_token! params[:token]
end
helper_method :current_tenant
end

Static HTML page in Phoenix

I'm trying to build a JSON backend for Elm. I only want to serve one page - elm.html, one js file - Main.js - and one css file.
I tried following these instructions but there is not enough there to help a complete newbie like me.
So now I have router.ex
scope "/", JwtExample do
pipe_through :browser # Use the default browser stack
get "/elm", RootController, :index
get "/", PageController, :index
end
# Other scopes may use custom stacks.
scope "/api", JwtExample do
pipe_through :api
resources "/users", UserController, except: [:new, :edit]
end
This controller
defmodule JwtExample.RootController do
use JwtExample.Web, :controller
plug :action
def index(conn, _params) do
redirect conn, to: "/elm.html"
end
end
And my files in web/static and priv/static
But when I surf to /elm I get
no route found for GET /elm.html (JwtExample.Router)
OK, so based on psantos answer, I needed to change lib/endpoint.ex to read
plug Plug.Static,
at: "/", from: :jwt_example, gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt elm.html)
Here's a solution that also servers the static page for requests to the root url, i. e. https://myapp.test/:
here's a solution that maps a request to the root path to index.html with a short function plug that can be added to your endpoint.ex without involving controllers. It works by defining a short Plug function to change the requested path. My hope is that it's a little faster to to it in the endpoint compared to doing it in a controller.
def redirect_index(conn = %Plug.Conn{path_info: []}, _opts) do
%Plug.Conn{conn | path_info: ["elm.html"]}
end
def redirect_index(conn, _opts) do
conn
end
plug :redirect_index
# This is Phoenix's standard configuration of Plug.Static with
# index.html added.
plug Plug.Static,
at: "/", from: :phoenix_elm_starter_template, gzip: false,
only: ~w(css fonts elm.html images js favicon.ico robots.txt)
Note that in production, you would usually deal with static assets at application server layer, possibly not hitting Phoenix at all.

google app engine: 405 method not allowed

What are the causes for the the error NetworkError: 405 Method Not Allowed
I was using a web service and all of a sudden it started returning that error. its not maintained so it will not get fixed. I am curious if i can do something about this.
The offending web service url is: http://jsonpdb.appspot.com/add
The method (GET/POST/HEAD etc) you're trying to use on that URL is not supported by the app. Are you sure the API expects you to use the method you're using on that URL?
Most common cause is using the wrong 'get' vs 'post' for the response. Verify what's being sent and that the correct method appears in the your handler.
class MainHander(webapp.RequestHandler):
def get(self):
...
def post(self):
....
def delete(self):
....
Another common issue is having the main dispatch section parse urls, but then not supply them in the get/post/delete
def main():
application = webapp.WSGIApplication(
[ (r'/upload/([^/]+)?/?', UploadFileHandler),
The regex there has () in it... that's a parameter in the url path like: /upload/filename
class UploadFileHandler(webapp.RequestHandler):
def post(self, filename):
...
Supplying a link to code would be helpful.
I know this is an old thread but I did not find a satisfactory answer to the question for my own needs. Especially if you're handling an AJAX response, you may want to explicitly allow OPTIONS requests by checking for them in the dispatch of your custom WebApp2 handler:
class MyHandler(webapp2.RequestHandler):
def __init__(self, request, response):
self.initialize(request, response)
#The dispatch is overwritten so we can respond to OPTIONS
def dispatch(self):
self.response.headers.add_header("Access-Control-Allow-Origin", "*")
if self.request.method.upper() == 'OPTIONS':
self.response.status = 204
self.response.write('')
else:
super(MyHandler, self).dispatch();