Send message en hook success - Delayed_job - ruby-on-rails

How can I send a message when the job finishes successfully? I would like to send the message and show it in a swal in javascript when the work finishes correctly, but I do not know how to do this, any suggestions?
I do not need to do anything other than send a message
class CompileProjectJob < Struct.new(:url)
def perform
end
def success(job)
#send message when the work is successful
end
end

At the end of perform method queue new delayed job for sending the message
class CompileProjectJob < Struct.new(:url)
def perform
# the code here of this job
# queue new job
end
end
the code of the perform method is executed sequentially as any regular code
Update
to send the message to the front end there are two ways (push and pull) more info
- push: using web sockets you push the message from the backend to the front end
- pull: the front end sends requests every certain period to check if the backend has a new data
and you can use any of these techniques to solve the problem
if you used pulling you will make the job update a data store as an example Redis or mysql. the front end will send a request every interval to check for the new data in some scenarios this will be a better solution but i think you are looking for the other technique
pushing:
here you can use something like active cable https://guides.rubyonrails.org/action_cable_overview.html
or a third party like pusher https://www.pusher.com/tutorials/realtime-table-ruby-rails
the main idea here your frontend app will open a websocket connection with your server. this socket will stay opened and listen for any updates from the backend through a channel so when you send the update after finishing the job through this channel it will be received by the front end so you can add code to show the message

Related

Webhook handling with background job?

I just wonder how I could handle webhook from third party API in general.
In my case, I need to handle Webhooks from Stripe.
So I use
StripeEvent to handle and listen for the entrance of webhook handlers. It provides an easy to use interface for handling events from Stripe.
The main implementation is:
take the ID from the POSTed event data
stripe doesn't sign events, so to verify by fetching event from Stripe API.
store events (id) and reject IDs that we've seen already to protect against replay attacks.
Everything works so far.
However, let's assume that
handling little complex logic within in webhook hanlder
listening many webhook requests
In this case, I feel I need to consider to use background job.
Best practices in stripe doc
If your webhook script performs complex logic, or makes network calls, it's possible the script would timeout before Stripe sees its complete execution. For that reason, you may want to have your webhook endpoint immediately acknowledge receipt by returning a 2xx HTTP status code, >and then perform the rest of its duties.
Here is my code,
I've just wondered which part I should bundle and enqueue?
StripeEvent.event_retriever = lambda do |params|
return nil if StripeWebhook.exists?(stripe_id: params[:id])
StripeWebhook.create!(stripe_id: params[:id])
return Stripe::Event.construct_from(params.deep_symbolize_keys) if Rails.env.test? # fetching the event from Stripe API
return Stripe::Event.retrieve(params[:id])
end
StripeEvent.configure do |events|
events.subscribe 'invoice.created', InvoiceCreated.new # handling the invoice.created event in service object
events.subscribe 'invoice.payment_succeeded', InvoicePaymentSucceeded.new
...
end
Short answer, just send all of it by serializing the Stripe::Event instance to a string with Marshal::dump, then deserialize back to a Stripe::Event in your background worker with Marshal::load.
We also wanted to process the webhook requests in the background using our delayed_job system, which stores the jobs in a database table and the job arguments in a string column.
To do that we first needed to serialize the Stripe::Event to a string in our StripeEvent.configure block:
# event is an instance of Stripe::Event
serialized_event = Marshal::dump(event)
Then we queue the background job rather than handling it synchronously, passing our serialized event as a string to where it is stored (in our case a database table) to await being processed.
Then our background worker code can deserialize the string it reads back to a Stripe::Event:
event = Marshal::load(serialized_event)

Actioncable broadcasts not hitting Received JS function

I have been trying to get a rails app together to replace a nastily coded php monstrosity. The current incarnation is pulling data from a non-Rails db, putting it into the Rails db and then displaying it in the views. The db is mainly populated with temperature readings that are added every few seconds. I can display a static-ish page in Rails with no problems but, trying to add ActionCable/realtime data has proven problematic. MOST things seem to be working properly but, when I broadcast to my channel, it does not seem to hit the Received function in the mychannel.coffee.
My Setup:
Server - passenger (bundle exec passenger start)
Jobs - Resque
ActionCable - Redis
Data is imported from the legacydb by a job that grabs the raw SQL and creates new records. After this, another job broadcasts to the channel.
The problems are coming from ActionCable, I think. All examples that I can find require user input to trigger the JS, it seems. However, I am trying to trigger things strictly from the server side. This job:
class DatabroadcastJob < ApplicationJob
queue_as :default
self.queue_adapter = :resque
def perform
ActionCable.server.broadcast 'dashboard_channel', content: render_thedata
end
private
def render_thedata
dataArr =[Data1.last, Data2.last, Data3.last]
ApplicationController.renderer.render(partial:
'dashboard/data_tables', locals: {item: dataArr})
end
end
Works. It works. I see the broadcast hitting the dashboard_channel. However, nothing in the dashboard.coffee gets triggered by the broadcast. This is incredibly confusing.
Dashboard.coffee
App.dashboard = App.cable.subscriptions.create "DashboardChannel",
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
# Called when there's incoming data on the websocket for this channel
alert data['content']
Nothing happens. The logs show the broadcast but nothing hits dashboard.coffee and raises an alert in browser. Am I thinking about this the wrong way because of all of the chat examples? Is there another place where I grab the broadcast and push it to subscribers when only making server side changes?
If any other info is needed to address this, please let me know. This issue has been driving me mental for days now.
First, check your frames. Are you sure you're getting the messages you want?
Then, in your channel you should set an ID to your subs. If you have a stream that is related to a model, then the broadcasting used can be generated from the model and channel.
class DashboardChannel < ApplicationCable::Channel
def subscribed
post = Post.find(params[:id])
stream_for post
end
end
Then you can broadcast to your channel like so
DashboardChannel.broadcast_to(#post, #comment)
Otherwise, you should do the following:
class DashboardChannel < ApplicationCable::Channel
def subscribed
stream_from 'dashboard_channel'
end
end
But this is a bad practice, because you won't be able to define which user transmits to your server.
One thing I would add for troubleshooting and testing the coffee/javascript is that console.log is your friend. Adding console.log "First step complete" and so on throughout really helped to trackdown where the errors were occurring.

Rails 5 app (Action Cable) as Socket.io server AND client

I am now familiar with Action Cable (Rails 5 functionality) as an emitter or server of websockets. However, I am supposed to consume an API which sends the data over websockets (e.g. 'socket.provider.com?token=12345').
I made some tests with a plain Ruby file using socket.io-client-simple (https://github.com/shokai/ruby-socket.io-client-simple) and it works, but I am not sure on how this would work on a deployed Rails app. I'm guessing I need a separate process which listens constantly for events emitted by the API provider. Has anyone done something similar? I am going to use Heroku for deployment.
Note: I think using a client-side approach for receiving the websockets and then posting them to my Rails app (i.e. Javascript Socket.IO library) is not an option, since I need to receive AND persist some of the data coming from the events in real time and not depend of the connectivity of at least one client.
I'm also wondering if there is any way to automatically set Action Cable to act as a 'listener of events' somehow. Haven't read anything on that topic so far, but would love to see some suggestions on that direction.
Update: Here is the Ruby code I'm using so far to connect to the provider's websockets API:
require 'rubygems'
require 'socket.io-client-simple'
socket = SocketIO::Client::Simple.connect 'https://api.provider.com', token: '12345'
socket.on :connect do
puts "connect!!!"
end
socket.on :disconnect do
puts "disconnected!!"
end
socket.on :providerevent do |data|
puts data
end
socket.on :error do |err|
p err
end
puts "please input and press Enter key"
loop do
sleep 100
end
ActionCable can't be listening to an external site's event on its own, so you'll have to combine socket.io and ActionCable.
ActionCable can send updates to the channel like this:
ActionCable.server.broadcast "channel_name", param1: your_param1, param2: your_param2
to update the channel when an event occured. The
received action of your channel's coffeescript file is where you have to do something with it.
From what I understand, you're looking for something like this in the controller where you would be listening for events:
def listen
socket.on :connect do
# persist your data
# example: #post = Post.create(...)
ActionCable.server.broadcast "channel_name", post_title: #post.title
end
end
and in your channel_name.coffee:
received: (data) ->
console.log(data["post_title"])
# do something
With this setup, you would be receiving events from the api, and broadcasting it to your channel. The page were the channel is setup would be updated each time your socket receives an event.
You should first follow DDH's tutorial, and then you'll probably understand better my solution (which is pretty easy to implement).

How to stop action execution with another action in rails?

I have following actions in a Test controller:
def run
while true do
# code
end
end
def stop
# stop the run action
end
how can stop action be implemented to halt the run action?
Because a client will wait for a response from a server, you can't have a loop in an endpoint that waits for another endpoint to be called.
In this case, a client will visit /test/run, but since the server won't return anything until the loop finishes, the client will just keep waiting.
This means (unless you specifically configured your webserver to do so), that another connection can't be made to the server to reach the test/stop endpoint.
If you must have a "job" that runs and be cancel-able by an endpoint, turn it into an actual background task.

In rails 4 - how to process data from a controller into jquery for purposes of a progressbar

I'm new to rails and jquery/css and webapps in general. I need guidance in building progress bar functionality. My rails app basically inserts file data into an elasticsearch engine. The data is a user's uploaded csv/excel file.
From my controller, what is the best/cleanest way to get progress-bar type percentage from the controller into a coffeescript or jquery code. I'm clueless about how status-percentage type data from the server can be rendered in the view. Below I have the controller that is of relevance. The #upload page has a button that triggers the import action. The import action renders index action once the data is loaded into elasticsearch.
The FileProcessorService is just a ruby class that does the parsing of the file and inserting each record into elasticsearch and returns data.
Here is my controller:
class FileProcessorController < ApplicationController
def index
end
def import
initialize_processor(params[:file])
if (#file_sample != nil || #index_name != nil) then
render 'index'
end
end
def upload
end
def initialize_processor(file_in)
File.open(Rails.root.join('public', 'uploads', file_in.original_filename), 'wb') do |file|
file.write(file_in.read)
end
#file_processor = FileProcessorService.new(file_in)
#file_sample = #file_processor.present_data_sample()
#index_name = #file_processor.load_index()
end
end
Since you mention you're "clueless" about how to approach this, I'll give you some ideas:
Progress
To handle a "progress bar", you're going to need away to receive regular updates at intervals. I don't know if FileProcessorService will do this - but your controller will need to send updates to your JS front-end somehow
Even if you don't have a percentage-based update from your controller, you'll want some event triggers to send updates to your system
Asynchronous
What you're dealing with is called an "asynchronous" request. This is a request outside the normal scope of HTTP requests, whereby your browser will initiate technology such as Javascript to send a request on your behalf
This basically means no refresh for the browser
You'll have to send an asynchronous request via JS, and then listen for the response. The response will be what determines your progress bar status
Pub/Sub
Asynchronous functionality gives you two "methods" to send/receive data - ajax (single request) or pub/sub (multiple requests). Pub/sub is basically how every chat application sends data -- each user gets their own "channel" and the server sends updates to it
I would recommend using a Pub/Sub service called Pusher to achieve the "live" data updates, which you can tie to the progress bar's status
Code
I've not done this before, but this is what you'd need:
You'll need to send events from your controller to a pub/sub channel (Pusher highly recommended)
The user's browser will "listen" to the updates through Pusher - allowing you to assign progress bar status each time an update is
posted

Resources