I'm trying to use ActionCable as a transport for GraphQL queries (graphql-streaming). GraphQL queries are arbitrary requests for data, so rather than having one cable per query, I'd like to have one cable and multiplex queries over that cable. The flow might be like this:
Connect to ActionCable
Subscribe to a GraphQL query
Push a query result
Something changes in the app, push another query result
User changes pages, we should unsubscribe from that query
I'm implementing subscription events as streams, so a subscription looks like this:
stream_from(event_name) { evaluate_graphql_and_push }
But when the user changes pages, I want to keep the channel connected but stop streaming from that event.
Is this possible with ActionCable?
You can call unsubscribe method on the subscription (a.k.a. channel) object.
i.e.,
channel = App.cable.subscriptions.create "ChannelName"
onPageChange = function() {
channel.unsubscribe()
}
Related
Situation:
I have a small chat app with two users in each room.
Lets call'em Sender and Receiver.
I'd like to make 'read/unread messages'.
To determine if Sender's message is currently being read he needs to know if Receiver is currently subscribed to the channel.
I'm trying to look through subscriptions:
# I can retrieve subscriptions from Redis:
redis_record = Redis.new.pubsub("channels", "action_cable/*")
# I can make such id
key = Base64.encode64("gid://test-app/User/1").tr('=', '').tr('actioncable/', '').chomp
But I don't know how to look for existing record.
Tried:
Redis.new.scan(0, match: key)
With no result.
Question: how to find out if subscription is active? (using Redis is not a keypoint, maybe ActionCable has something to do with it somewhere inside the box)
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)
Can I fetch the data from the my database in realtime using Rails and ReactJS?
I have just started using React with Rails as this is pretty awesome combination. What I want to know is if there is a way to tell the view to update itself after a component is deleted/ created (so a record is removed/ added to the database)? Something like user after entering the page will subscribe to the database event (?).
What I have now is ComponentContainer with a custom fetch method that gets json from the server. I put it in the componentWillMount and inside a setInterval and run it every single second:
var ComponentsContainer = React.createClass({
componentWillMount() {
this.fetchRecords();
setInterval(this.fetchRecords, 1000);
},
fetchRecords() {
$.getJSON(path_to_results,
(data) => this.setState({components: data});
}, ...render methods etc.
});
Sounds like you might be looking for ActionCable, a Rails 5 thing. It uses sockets to provide server/client communication and will enable the server to send or broadcast messages to clients.
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).
Just wondering if any Twilio experts can shed some light onto how I might handle having multiple conferences attached to the same Twilio number happening at once.
In the simplest case I would be looking to handle 2 person conferences (so basically just a regular call).
E.g. lets say I have two 2-person conferences scheduled for 1:00 and those two calls are waiting in a queue to be set up. At 1:00 Twilio would pull the first call from the queue and send out outbound calls to connect User A and User B in the first conference, then it would pull the second call from the queue and send out outbound calls to User C and User D to connect them in a second separate conference. Apart from Twilio’s 1-second per call limit is there anything stopping me from using the same Twilio number to connect both separate conferences?
Is it simply the fact that when my app pulls the second call from the queue it is making separate HTTP requests that keeps the two conferences separate even though they're attached to the same number? I’m working in Rails but I’d appreciate input from anyone as to how I might need to handle that in my code.
Twilio developer evangelist here.
As Akhil says, there's no limit on making multiple conferences from the same caller id.
What you might do to accomplish this is set a URL parameter in the URL you pass to the create call method that indicates which conference your users will join. For example:
client = Twilio::REST::Client.new(ACCOUNT_SID, AUTH_TOKEN)
client.calls.create(
:from => YOUR_TWILIO_NUMBER,
:to => user.phone_number,
:url => "/conference?conference_room=#{user.current_conference}"
)
Then, in your route you can look up the conference name and add the user to it.
def conference
conf_room = params[:conference_room]
twiml = Twilio::TwiML::Response.new do |r|
r.Dial do
r.Conference conf_room
end
end
render :xml => twiml.to_xml
end
Let me know if that helps at all!
There is no limitation in making multiple conferences at once from the same caller ID. You can have any number of simultaneous calls from the same number at a time (Respecting twilio's 1 call per second limit).
The key here is to have a unique name for a conference and join users to the correct conference.