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

[WEBSOCKET]: Initial Implementation #1415

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

[WEBSOCKET]: Initial Implementation #1415

wants to merge 6 commits into from

Conversation

amadolid
Copy link
Collaborator

@amadolid amadolid commented Oct 31, 2024

FEATURES:

  • User Notifications
  • Channel Notifications
  • Client Notifications
  • Support Cross Instance Notifications
  • Support Running Multiple Worker Uvicorn
  • Includes Tester Files
    • runner file for multiple worker uvicorn
    • websocket.jac for sample usage

WEBSOCKET

Walker Declaration

  • walker can be declared as websocket thru specs configurations.
  • it can still work along with other http methods however, the only limitation is it doesn't support file (maybe in the future).
walker your_event_name {
    has val: int;
    can enter with `root entry {
        report "Do something!";
    }

    class __specs__ {
        has methods: list = ["websocket"];
    }
}

Websocket Connection

PROTOCOL: ws
URL: /websocket
HEADER (optional): Authorization: Bearer {{USER-TOKEN}}
QUERY PARAM (optional): ?channel_id=anystring

  • once connected, you will recieved first event for connection information
  • there's two type of connection
    • Authenticated - with valid Authorization Token
    • Non Authenticated
  • you may specify your desired channel_id via query param
    • this will recieved notification from specific channel
    • usage: group chat/notification
{
	"type": "connection",
	"data": {
        # your websocket client id
		"client_id": "1730887348:f46d85203c704c099e9f44e948322a20",

        # user's root_id
		"root_id": "n::672b35cec309e5ef8469c372",

        # non authenticated
		# "root_id": "n::000000000000000000000001",

        # user's channel id, random if not specified
		"channel_id": "1730887348:796ad2e9fa3e484ebe01f071c381b7e8"
	}
}

Client Valid Events

Walker

  • this will trigger a normal walker as if it was trigger via rest API
{
    # event type
	"type": "walker",

    # walker's name
	"walker": "your_event_name",

    # if you want to recieve a notification for response
	"response": true,

    # walker's request context
	"context": {
        "val": 1
    }
}

User

  • this will send notification to target user's clients
  • if target user/s has multiple clients, all of it will get notified
{
    # event type
	"type": "user",

    # target user/s via root_id
    "root_ids": ["n::672b35cec309e5ef8469c372"],

    # data you want to send
	"data": {
        "val": 1
    }
}

Channel

  • this will send notification to target channel/s
  • all clients that's subcribed to the channel will get notified
{
    # event type
	"type": "channel",

    # target channel_id/s
    "channel_ids": ["anystring"],

    # data you want to send
	"data": {
        "val": 1
    }
}

Client

  • this will send notification to target client/s
{
    # event type
	"type": "client",

    # target client_id/s
    "client_ids": ["1730887348:f46d85203c704c099e9f44e948322a20"],

    # data you want to send
	"data": {
        "val": 1
    }
}

Walker Client Notification

PREREQUISITE

import:py from jac_cloud.plugin {WEBSOCKET_MANAGER as socket}

Self

  • this will send notification to current websocket it was from (only valid on websocket walker event)
  • if via walker api, nothing will happen
socket.notify_self({"any_field": "for_progress", "progress": "0%", "status": "started"});

User

  • this will send notification to target user's clients
  • if target user/s has multiple clients, all of it will get notified
socket.notify_users([root], {"any_field": "for_progress", "progress": "0%", "status": "started"});

Channel

  • this will send notification to target channel/s
  • all clients that's subcribed to the channel will get notified
socket.notify_channels([channel_id], {"any_field": "for_progress", "progress": "0%", "status": "started"});

Client

  • this will send notification to target client/s
socket.notify_clients([client_id], {"any_field": "for_progress", "progress": "0%", "status": "started"});

@amadolid
Copy link
Collaborator Author

amadolid commented Nov 4, 2024

@amadolid amadolid changed the title [INPROGRESS][WEBSOCKET]: Initial Implementation [WEBSOCKET]: Initial Implementation Nov 4, 2024
@amadolid amadolid marked this pull request as ready for review November 4, 2024 08:23
@amadolid
Copy link
Collaborator Author

amadolid commented Nov 4, 2024

Will continue to upgrade to support channels using
https://github.com/encode/broadcaster

@amadolid
Copy link
Collaborator Author

REBASED

@amadolid amadolid force-pushed the websocket branch 2 times, most recently from 634df8c to 16dea9f Compare November 29, 2024 07:41
@ypkang
Copy link
Contributor

ypkang commented Jan 6, 2025

@amadolid Looking at this and have some question:

  1. How do we set up a server-side walker that listens on a connected web socket and continuously receive data from it? For example, something like this
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f"Message text was: {data}")
  1. Does the notify_user/channel/client accept string or does it have to be json? The example above is just sending a string via send_text

  2. For the unauthenticated case, it is only useful for broadcasting messages to unauthenticated users that establish connection with a specific connection ID, right? is there another use case?

@ypkang
Copy link
Contributor

ypkang commented Jan 6, 2025

Also, I think we are going to need a different type of documentation on this. We need to include end-to-end examples, like what fastAPI has https://fastapi.tiangolo.com/advanced/websockets/#await-for-messages-and-send-messages.

We need maybe 3 common use case:

  1. A user establishing a connection with server and continuously exchanging messages
  2. Multiple users (unauth) establish connections via a specific channel id and listening on messages. An authenticated user broadcast a message to the channel via restAPI call.
  3. A user establish WS connections with multiple clients. Server send messages to one or many clients.

jac-cloud/jac_cloud/tests/jac-cloud-websocket.insomnia Outdated Show resolved Hide resolved
if (
authorization := websocket.headers.get("Authorization")
) and authorization.lower().startswith("bearer"):
token = authorization[7:]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a nitpick: I'd use len("bearer") instead of 7. A bit more readable.

@@ -0,0 +1,44 @@
"""Sample Runner."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm is this a helper script? do we need to commit this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be good example for deploying using uvicorn

@amadolid
Copy link
Collaborator Author

amadolid commented Jan 7, 2025

Also, I think we are going to need a different type of documentation on this. We need to include end-to-end examples, like what fastAPI has https://fastapi.tiangolo.com/advanced/websockets/#await-for-messages-and-send-messages.

We need maybe 3 common use case:

1. A user establishing a connection with server and continuously exchanging messages

2. Multiple users (unauth) establish connections via a specific channel id and listening on messages. An authenticated user broadcast a message to the channel via restAPI call.

3. A user establish WS connections with multiple clients. Server send messages to one or many clients.

Agree, tho the sample insomnia export has the complete use case including the request examples. Might need to convert it to docs type

@ypkang
Copy link
Contributor

ypkang commented Jan 7, 2025

Also, I think we are going to need a different type of documentation on this. We need to include end-to-end examples, like what fastAPI has https://fastapi.tiangolo.com/advanced/websockets/#await-for-messages-and-send-messages.
We need maybe 3 common use case:

1. A user establishing a connection with server and continuously exchanging messages

2. Multiple users (unauth) establish connections via a specific channel id and listening on messages. An authenticated user broadcast a message to the channel via restAPI call.

3. A user establish WS connections with multiple clients. Server send messages to one or many clients.

Agree, tho the sample insomnia export has the complete use case including the request examples. Might need to convert it to docs type

Yeah but the insomnia doesn't include the server side implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants