Problem with current_user in GrapghqlController - ruby-on-rails

I'm trying to assign a value to current_user in GraphqlController but it's not working.
this is my code in graphql_controller.rb
class GraphqlController < ApplicationController
# If accessing from outside this domain, nullify the session
# This allows for outside API access while preventing CSRF attacks,
# but you'll have to authenticate your user separately
# protect_from_forgery with: :null_session
def execute
variables = ensure_hash(params[:variables])
query = params[:query]
operation_name = params[:operationName]
context = {
# Query context goes here, for example:
session: session,
current_user: current_user
}
result = GraphqlSimpleLoginAppSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
render json: result
rescue => e
raise e unless Rails.env.development?
handle_error_in_development e
end
private
def current_user
return unless session[:token]
AuthToken.verify(session[:token])
end
# Handle form data, JSON body, or a blank value
def ensure_hash(ambiguous_param)
case ambiguous_param
when String
if ambiguous_param.present?
ensure_hash(JSON.parse(ambiguous_param))
else
{}
end
when Hash, ActionController::Parameters
ambiguous_param
when nil
{}
else
raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
end
end
def handle_error_in_development(e)
logger.error e.message
logger.error e.backtrace.join("\n")
render json: { errors: [{ message: e.message, backtrace: e.backtrace }], data: {} }, status: 500
end
end
but current_user remains null. I don't know why. Please can you help me ?

Related

Rails 5 manage result from monads

I've got Rails 5 app with dry-monads on board. Monads are used to create the Appointment object inside create action in AppointmentsController. They return Success or Failure in the last step with below structure:
# services/appointments/create.rb
(...)
def call
Success(appointment_params: appointment_params)
(...)
.bind(method(:save_appointment))
end
private
def save_appointment(appointment)
if appointment.save
Success(appointment)
else
Failure(failure_appointments: appointment, appointments_errors: appointment.errors.full_messages)
end
end
After each action (success or failure) I want to send an email and display the corresponding json in AppointmentsController:
class Api::AppointmentsController < ApplicationController
def create
succeeded_appointments = []
failure_appointments = []
appointments_errors = []
batch_create_appointments_params[:_json].each do |appointment_params|
appointment = ::Appointments::Create.new(appointment_params).call
if appointment.success?
succeeded_appointments << appointment.value!
else
failure_appointments << appointment.failure[:failure_appointments] &&
appointments_errors << appointment.failure[:appointments_errors]
end
end
if failure_appointments.any?
AppointmentMailer.failed_mail(email, failure_appointments.size, appointments_errors).deliver_now
render json: {
error: appointments_errors.join(', '),
}, status: :bad_request
elsif succeeded_appointments.any?
AppointmentMailer.success_mail(email, succeeded_appointments.size).deliver_now
render json: {
success: succeeded_appointments.map do |appointment|
appointment.as_json(include: %i[car customer work_orders])
end,
}
end
end
I wonder if there is a better, faster way to record these errors than declaring 3 different empty arrays (succeeded_appointments, failure_appointments, appointments_errors) like at the beginning of create action? so far the create action looks heavy.
Create a separate service object for bulk creation:
# services/appointments/bulk_create.rb
class Appointments::BulkCreate
def initialize(bulk_params)
#bulk_params = bulk_params
end
def call
if failed_results.any?
AppointmentMailer.failed_mail(email, failed_results_errors.size, failed_results_errors).deliver_now
Failure(failed_results_errors.join(', '))
else
AppointmentMailer.success_mail(email, success_appointments.size).deliver_now
Success(success_appointments)
end
end
private
attr_reader :bulk_params
def failed_results
results.select(&:failure?)
end
def success_results
results.select(&:success?)
end
def success_appointments
#success_appointments ||= success_results.map do |appointment|
appointment.as_json(include: %i[car customer work_orders])
end
end
def failed_results_errors
#failed_results_errors ||= failed_results.map do |failed_result|
failed_result.failure[:appointments_errors]
end
end
def results
#results ||= bulk_params.map do |appointment_params|
::Appointments::Create.new(appointment_params).call
end
end
end
Then your controller will look like this:
class Api::AppointmentsController < ApplicationController
def create
result = ::Appointments::BulkCreate.new(batch_create_appointments_params[:_json]).call
if result.success?
render json: { success: result.value! }, status: :ok
else
render json: { error: result.failure }, status: :bad_request
end
end
end

Best practices for Ruby on Rails service

I'm writing some mobile otp validation service and below is my service class
require 'nexmo'
class NexmoServices
def initialize api_key = nil, api_secret = nil, opts = {}
api_key = api_key || Rails.application.secrets.nexmo_api_key
api_secret = api_secret || Rails.application.secrets.nexmo_secret_key
#nexmo_client = Nexmo::Client.new(
api_key: api_key,
api_secret: api_secret,
code_length: 6
)
#brand = 'CryptoShop'
end
def send_verification_code opts
#nexmo_client.verify.request(number: opts[:number], brand: #brand)
end
def check_verification_code opts
#nexmo_client.verify.check(request_id: opts[:request_id], code: opts[:verification_code])
end
def cancel_verification_code opts
#nexmo_client.verify.cancel(opts[:request_id])
end
end
in the controller i'm calling the service methods like below
class NexmoController < ApplicationController
def send_verification_code
response = NexmoServices.new.send_verification_code params[:nexmo]
if response.status == '0'
render json: response.request_id.to_json
else
render json: response.error_text.to_json
end
end
def cancel_verification_code
response = NexmoServices.new.cancel_verification_code params[:nexmo]
if response.status == '0'
render json: response.to_json
else
render json: response.error_text.to_json
end
end
end
I have read that usually there will be call method inside service class and controller will call that. service method call will take care of the rest.
My case im instantiating service objects for all the methods if you see my controller(NexmoService.new).
is it correct??? I want to know the best practise must be followed in this scenario.
Thanks,
Ajith

How to send a get to a server and wait a get in my app in Ruby on Rails

I am using Zapier to search some information in google sheets. I used Webhocks to send a GET to his server with a JSON information. The response of GET is an "OK" and I can't custom this.
So, they will execute a task, find what a I want and return a value, but the response must be a GET in my server, and I don't know how to intercept this response in my route.
I'm trying to study Rails Rack to intercept de request in my app, but I don't know how to send the response to the event that sent the first GET.
How is my middleware:
class DeltaLogger
def initialize app
#app = app
end
def call env
Rails.logger.debug "#{env['QUERY_STRING']}"
#status, #headers, #response = #app.call(env)
[#status, #headers, #response]
end
end
Thanks!
Example
So, to get the value returned from Zapier, I created two routes and a global class cache.
class Zapier
require 'httparty'
def initialize
#answer = ""
#id = 0
end
def request(uri, task)
last_event = Event.last
puts last_event.inspect
if last_event.nil?
last_id = 0
else
last_id = last_event.event_id
end
event_id = last_id + 1
Event.find_or_create_by(event_id: event_id)
result = HTTParty.post(uri.to_str,
:body => {id: event_id, task: task}.to_json,
:headers => {'content-Type' => 'application/json'})
#answer = ""
#id = event_id
end
def response(event_id, value)
if event_id != #id
#answer = ""
else
#answer = value
end
end
def get_answer
#answer
end
end
And my controller:
class ZapierEventsController < ApplicationController
require 'zapier_class'
before_action :get_task, only: [:get]
before_action :get_response, only: [:set]
##zapier ||= Zapier.new
def get
##zapier.request('https://hooks.zapier.com',#task)
sleep 10 #Wait for response
#value = ##zapier.get_answer
render json: { 'value': #value }, status:
end
def set
##zapier.response(#id, #value)
render json: { 'status': 'ok' }, status: 200
end
def get_task
#task = params["task"]
end
def get_response
#id = Integer(params["id"])
#value = params["value"]
end
end
Now i have to make a Task Mananger

Devise Signin Testing with RSpec for API

authenticate_with_http_token do |token, options|
auth_key = AuthKey.find_by(authentication_token: token)
if auth_key.present?
if auth_key.token_valid?
auth_key.touch
sign_in(:user, auth_key.user, store: false, bypass: false) unless current_user.present?
else
render json: { message: t('invalid_otp_access'), errors: [t('token_expired')] }, status: 401 and return
end
else
render json: { message: t('invalid_access_message'), errors: [t('invalid_access')] }, status: 401 and return
end
end
i need to write spec for the above code, in my controller i am using current_user.
My controller looks like below.
def index
schedules = params[:type] == "upcoming" ? :upcoming : :past
schedules = current_user.audit_schedules.send(schedules)
if schedules.present?
paginate json: schedules, per_page:10, root: false, each_serializer: Api::V1::MyAuditSerializer
else
render json: { message: t('.no_audits_scheduled'), errors: [] }
end
end
and i am trying to test my index with passing valid token and params
context "with invalid attributes" do
it "It will return list of audits" do
request.headers["Authorization"] = "Token #{auth_key.authentication_token}"
#request.env["devise.mapping"] = Devise.mappings[:user]
get :index, { params: { type: "upcoming" } }
expect(response.body).to eq 200
end
end
the above spec returning body like
<html><body>You are being redirected.</body></html>
And in my spec helper i included devise helpers like
config.include Devise::TestHelpers, type: :controller
If i remove that helper current_user is always nil. if i add that helper it is redirecting like above, please let me know what i missed and how can i test those spec.
I think you want user_signed_in? vs. current_user.present?. This doesn't fix the problem.
You're sure user is not null? and that the user has been confirmed if you're using confirmable?
Digging through the code, I see this:
if options[:bypass]
warden.session_serializer.store(resource, scope)
elsif warden.user(scope) == resource && !options.delete(:force)
# Do nothing. User already signed in and we are not forcing it.
true
else
warden.set_user(resource, options.merge!(scope: scope))
end
source: https://github.com/hassox/warden/blob/906edf86c6c31be917a921097031b89361d022e8/lib/warden/proxy.rb
You can try adding :force which should force the setting of the user.

Make Rails controller actions atomic?

Sometime one in a long series of events within a controller action fails. For example, a credit card is processed but then an ActiveRecord query times out. Is there any way to make those calls reversible?
E.g. with this controller action:
def process_order
cart = Cart.new(params[:cart])
load_order
response = credit_card.charge
if response
submit_order
order.receipt = Pdf.new(render_to_string(:partial => 'receipt')
order.receipt.pdf.generate
order.receipt.save
render :action => 'finished'
else
order.transaction = response
#message = order.transaction.message
order.transaction.save
render :action => 'charge_failed'
end
end
I would like to be able to put a block around it like so:
def process_order
transaction
cart = Cart.new(params[:cart])
load_order
response = credit_card.charge
if response
submit_order
order.receipt = Pdf.new(render_to_string(:partial => 'receipt')
order.receipt.pdf.generate
order.receipt.save
render :action => 'finished'
else
order.transaction = response
#message = order.transaction.message
order.transaction.save
render :action => 'charge_failed'
end
rollback
credit_card.cancel_charge
...
end
end
This is just a contrived example and I'm not really sure how it would work in practice. What typically happens is we get an exception like ActiveRecord::StatementInvalid: : execution expired for the line with submit_order and then we have to go and manually run the rest of the lines that should have run.
Here's a generic solution.
class Transactable
def initialize(&block)
raise LocalJumpError unless block_given?
#block = block
end
def on_rollback(&block)
raise LocalJumpError unless block_given?
#rollback = block
self
end
def call
#block.call
end
def rollback
#rollback.call if #rollback
end
end
class Transaction
def initialize(tasks)
tasks = Array(tasks)
tasks.each do |t|
Transactable === t or raise TypeError
end
#tasks = tasks
end
def run
finished_tasks = []
begin
#tasks.each do |t|
t.call
finished_tasks << t
end
rescue => err
finished_tasks.each do |t|
t.rollback
end
raise err
end
end
end
if __FILE__ == $0
Transaction.new([
Transactable.new { puts "1: call" }.on_rollback { puts "1: rollback" },
Transactable.new { puts "2: call" }.on_rollback { puts "2: rollback" },
Transactable.new { puts "3: call"; raise "fail!" }.on_rollback { puts "3: rollback" },
]).run
end
Note that it doesn't:
handle errors in the rollback block
call the rollback for the failed task, but that's easy to adjust
Just wrap it in
cart.transaction do
# ...
end
to use transactions. For details see http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
I'm a bit late but I think you should use save! instead of save. save just returns false if something fails within your model but save! raises an exception and your ActiveRecord::Base.transaction do block rolls back your changes correctly...
For example:
def process_order
ActiveRecord::Base.transaction do
begin
cart = Cart.new(params[:cart])
load_order
response = credit_card.charge
if response
submit_order
order.receipt = Pdf.new(render_to_string(:partial => 'receipt')
order.receipt.pdf.generate
order.receipt.save!
render :action => 'finished'
else
order.transaction = response
#message = order.transaction.message
order.transaction.save!
render :action => 'charge_failed'
end
rescue
# Exception raised ... ROLLBACK
raise ActiveRecord::Rollback
end
end

Resources