Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a websocket forwarding updates of steps #77

Open
19 tasks
zaratan opened this issue Sep 10, 2018 · 0 comments
Open
19 tasks

Add a websocket forwarding updates of steps #77

zaratan opened this issue Sep 10, 2018 · 0 comments
Labels
1 2 3 5 8 13 Good practice Make your overall project better

Comments

@zaratan
Copy link

zaratan commented Sep 10, 2018

Why ?

During a lesson we don't want that neither the student nor the teacher had to refresh the page.

There is two way to do that:

  • Long polling (pull): IE, every 5sec I'll make a query to my backend/API to see if there's no new data.

    • Pro: Easy to implement
    • Con: So many useless queries and doesn't scale very well.
  • Websockets: IE, I open a new permanent connection with the API so the backend can decide when to push me some new data.

    • Pro: Elegant. "Instantaneous". Scale.
    • Con: Hard to implement correctly.

What is a websocket: It's… A bi-directonal tube between 2 machine. Both of them can push data in it so the other side will get it.

Must have

This will be fun™.

To be able to properly instanciate websocket connection we have to do it securely. BUT the websocket protocol doesn't allow headers (you see where we are going ?) so we have to secure the connection negociation ourselves.

Then we have to provide the websocket connection.

Then we have to write data in it :D.

We will heavily rely on ActionCable for handling most of the websocket complexity
We will use action-cable-testing for testing purposes. This gem will be part of rails 6.

  • Read the ActionCable documentation so the concepts will be clearer
  • Read the action-cable-testing so you can have a glimpse of what's coming.

Websocket connection negociation

We need a way to be sure that our user is authorized and we can't really use headers nor cookies. We will rely on temporary tokens to do so.

  • Add a field in the user to store its temporary permanent WS tokenS (Postgres Array is a good candidate here.). Don't forget to add an index.
  • Add a way to create a new token for a user (with SecureRandom). Here you have two choices:
    • Either add it to the user model (the lazy spaghetti way)
    • Create a new interactor for this.
  • Create a new job (ActiveJob) that will try to find the token in the user and if it exists remove the token.
  • After token generation, always start a job that will execute 5 minute later to destroy the token. (hint: use an interactor and an organizer to do both in sequence).
  • Provide an URL to ask for a new token for a WS connection. It will create the token, start the job and the return it to the user. (POST on /websocket ?). You can verify that the user is logged in at that time.
  • Of course everything should be tested.

Now that we can negotiate for a short-lived auth token we can use it to securely create a bi-directional connection \o/

Websocket connection

  • Plug ActionCable in your app on /cable/:token
  • Create a new connection object.
  • Make it fetch the user from the token and be identified_by it. (Hint: A quick way to get the token from the url: token = request.path.gsub(%r{.*/}, ''))
  • Remember to remove the token from the user instantly.
  • Test eveything

Now we can open the tube and start thinking about throwing data in it \o/

Data broadcasting

  • Create a new Lesson Channel.
  • That channel should reject connection if:
    • There's no id provided;
    • The lesson doesn't exist;
    • The user is not a member (either teacher or student) of that lesson.
  • And then stream_for the lesson
  • When a step is ticked from a student, broadcast for its lesson a payload of this form:
{
  "type": "STEP_TICKED",
  "value": {"lesson_id": "abcdef", "step_id": "ghijklm", "completness_percentage": "34.67"}
}
  • When a step is unticked from a student, broadcast for its lesson a payload of this form:
{
  "type": "STEP_UNTICKED",
  "value": {"lesson_id": "abcdef", "step_id": "ghijklm", "completness_percentage": "34.67"}
}
  • TEST EVERYTHING

You now have a full flow of websocketing \o/ Adding new channels and infos in your app is easy because you already have the connection flow. Congrats. You have done a hard ticket and you are ready to face the challenges outside of this formation :)

Reading list

@zaratan zaratan added Project Something about the project 1 2 3 5 8 13 Good practice Make your overall project better and removed Project Something about the project labels Sep 10, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
1 2 3 5 8 13 Good practice Make your overall project better
Projects
None yet
Development

No branches or pull requests

1 participant