-
-
Notifications
You must be signed in to change notification settings - Fork 54
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
fix!: realtime v2 #178
Merged
Merged
fix!: realtime v2 #178
Changes from all commits
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
59d49fd
add push
grdsdev bebbe5c
add tests
grdsdev 793ec53
fix: realtime listeners
grdsdev 29583da
send broadcast using _push method
grdsdev d5464d8
change channels storage
grdsdev bbbcbe5
use _on method
grdsdev c692c34
wip
grdsdev 1e0a45a
fix broadcast callback trigger
grdsdev 57b0ef4
use kargs
grdsdev ad63568
fix postgres_changes and add send buffer
grdsdev e4e099e
wip presence
grdsdev bab3cb3
add push buffer
grdsdev 87cf673
fix subscribe callback
grdsdev f3c56d3
fix presence and add tests
grdsdev 2c995ee
run tests on ci
grdsdev 1525189
fix action name
grdsdev c3be09b
store supabase env vars
grdsdev 14f1172
fix issue with format
grdsdev 6666426
skip coverage
grdsdev 21b2bfc
use str enum instead of StrEnum
grdsdev da11ccd
fix issues with older python versions
grdsdev 82a8d9c
fix unsubscribe and rejoin logic
grdsdev 0df42e4
fix format
grdsdev 81f029f
use *args
grdsdev 291e7e3
fire and forget unsubscribe call
grdsdev 9679055
add method for removing channel and update README
grdsdev 6abef2a
update README
grdsdev 9ac7e61
add RealtimeChannelOptions type
grdsdev cde8d9f
remove FIXME
grdsdev 58b6722
make some types and attributes private
grdsdev 30b8ef8
move push to separate file
grdsdev ba62d9b
add tests for postgres changes
grdsdev 3e76efa
remove need of *args from callbacks
grdsdev 80ea82a
fix rls policy
grdsdev 30b9a27
use async events and semaphores when testing
grdsdev c8d1402
fix migration
grdsdev c1b3a68
cancel tasks at test end
grdsdev 0a8d3c9
wip
grdsdev 686e799
reset local db instance onCI
grdsdev 6aa7a31
wait until supabase is ready:
grdsdev 8fb5036
increase timeout on tests
grdsdev 31db199
rename Socket to RealtimeClient
grdsdev 98c262c
run tests twice
grdsdev f022116
fix format
grdsdev d963e38
Generate sync bindings
grdsdev e3df66a
poetry lock
grdsdev 74e7142
format
grdsdev 0ef5398
add filter to postgres_changes
grdsdev f06f478
send filter only when non-None
grdsdev d75c0b9
add example which uses filter
grdsdev 9428989
update sync bindings
grdsdev 4c95572
code review fixes
grdsdev 433fd43
ci
grdsdev 784f28f
poetry lock
grdsdev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,4 +89,4 @@ if (failed) { | |
process.exit(1); | ||
} | ||
|
||
process.exit(0); | ||
process.exit(0); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,4 +40,4 @@ jobs: | |
|
||
node .github/workflows/conventional-commits-lint.js push <<EOF | ||
${{ toJSON(github.event) }} | ||
EOF | ||
EOF |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,73 +1,203 @@ | ||
# realtime-py | ||
Python Client Library to interface with the Phoenix Realtime Server | ||
<br /> | ||
<p align="center"> | ||
<a href="https://supabase.io"> | ||
<picture> | ||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--dark.svg"> | ||
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/supabase-logo-wordmark--light.svg"> | ||
<img alt="Supabase Logo" width="300" src="https://raw.githubusercontent.com/supabase/supabase/master/packages/common/assets/images/logo-preview.jpg"> | ||
</picture> | ||
</a> | ||
|
||
## Requirements | ||
**Python 3 higher** | ||
<h1 align="center">Supabase Realtime Client</h1> | ||
|
||
<h3 align="center">Send ephemeral messages with <b>Broadcast</b>, track and synchronize state with <b>Presence</b>, and listen to database changes with <b>Postgres Change Data Capture (CDC)</b>.</h3> | ||
|
||
<p align="center"> | ||
<a href="https://supabase.com/docs/guides/realtime">Guides</a> | ||
· | ||
<a href="https://supabase.com/docs/reference/python">Reference Docs</a> | ||
· | ||
<a href="https://multiplayer.dev">Multiplayer Demo</a> | ||
</p> | ||
</p> | ||
|
||
# Overview | ||
|
||
This client enables you to use the following Supabase Realtime's features: | ||
|
||
- **Broadcast**: send ephemeral messages from client to clients with minimal latency. Use cases include sharing cursor positions between users. | ||
- **Presence**: track and synchronize shared state across clients with the help of CRDTs. Use cases include tracking which users are currently viewing a specific webpage. | ||
- **Postgres Change Data Capture (CDC)**: listen for changes in your PostgreSQL database and send them to clients. | ||
|
||
# Usage | ||
|
||
## Installing the Package | ||
|
||
## Installation | ||
```bash | ||
pip3 install realtime==1.0.2 | ||
pip3 install realtime==2.0.0 | ||
``` | ||
|
||
## Installation from source | ||
```bash | ||
pip3 install -r requirements.txt | ||
python3 usage.py | ||
## Creating a Channel | ||
|
||
```python | ||
import asyncio | ||
from typing import Optional | ||
from realtime.client import RealtimeClient | ||
from realtime.channel import RealtimeSubscribeStates | ||
|
||
client = RealtimeClient(REALTIME_URL, API_KEY) | ||
channel = client.channel('test-channel') | ||
|
||
def _on_subscribe(status: RealtimeSubscribeStates, err: Optional[Exception]): | ||
if status == RealtimeSubscribeStates.SUBSCRIBED: | ||
print('Connected!') | ||
elif status == RealtimeSubscribeStates.CHANNEL_ERROR: | ||
print(f'There was an error subscribing to channel: {err.message}') | ||
elif status == RealtimeSubscribeStates.TIMED_OUT: | ||
print('Realtime server did not respond in time.') | ||
elif status == RealtimeSubscribeStates.CLOSED: | ||
print('Realtime channel was unexpectedly closed.') | ||
|
||
await channel.subscribe(_on_subscribe) | ||
|
||
# Listen for all incoming events, often the last thing you want to do. | ||
await client.listen() | ||
``` | ||
|
||
## Quick Start | ||
### Notes: | ||
|
||
- `REALTIME_URL` is `ws://localhost:4000/socket` when developing locally and `wss://<project_ref>.supabase.co/realtime/v1` when connecting to your Supabase project. | ||
- `API_KEY` is a JWT whose claims must contain `exp` and `role` (existing database role). | ||
- Channel name can be any `string`. | ||
|
||
## Broadcast | ||
|
||
Your client can send and receive messages based on the `event`. | ||
|
||
```python | ||
from realtime.connection import Socket | ||
# Setup... | ||
|
||
channel = client.channel( | ||
"broadcast-test", {"config": {"broadcast": {"ack": False, "self": False}}} | ||
) | ||
|
||
await channel.on_broadcast("some-event", lambda payload: print(payload)).subscribe() | ||
await channel.send_broadcast("some-event", {"hello": "world"}) | ||
``` | ||
|
||
### Notes: | ||
|
||
def callback1(payload): | ||
print("Callback 1: ", payload) | ||
- Setting `ack` to `true` means that the `channel.send` promise will resolve once server replies with acknowledgement that it received the broadcast message request. | ||
- Setting `self` to `true` means that the client will receive the broadcast message it sent out. | ||
- Setting `private` to `true` means that the client will use RLS to determine if the user can connect or not to a given channel. | ||
|
||
def callback2(payload): | ||
print("Callback 2: ", payload) | ||
## Presence | ||
|
||
if __name__ == "__main__": | ||
URL = "ws://localhost:4000/socket/websocket" | ||
s = Socket(URL) | ||
s.connect() | ||
Your client can track and sync state that's stored in the channel. | ||
|
||
```python | ||
# Setup... | ||
|
||
channel = client.channel( | ||
"presence-test", | ||
{ | ||
"config": { | ||
"presence": { | ||
"key": "" | ||
} | ||
} | ||
} | ||
) | ||
|
||
channel.on_presence_sync(lambda: print("Online users: ", channel.presence_state())) | ||
channel.on_presence_join(lambda new_presences: print("New users have joined: ", new_presences)) | ||
channel.on_presence_leave(lambda left_presences: print("Users have left: ", left_presences)) | ||
|
||
await channel.track({ 'user_id': 1 }) | ||
``` | ||
|
||
channel_1 = s.set_channel("realtime:public:todos") | ||
channel_1.join().on("UPDATE", callback1) | ||
## Postgres CDC | ||
|
||
channel_2 = s.set_channel("realtime:public:users") | ||
channel_2.join().on("*", callback2) | ||
Receive database changes on the client. | ||
|
||
s.listen() | ||
```python | ||
# Setup... | ||
|
||
channel = client.channel("db-changes") | ||
|
||
channel.on_postgres_changes( | ||
"*", | ||
schema="public", | ||
callback=lambda payload: print("All changes in public schema: ", payload), | ||
) | ||
|
||
channel.on_postgres_changes( | ||
"INSERT", | ||
schema="public", | ||
table="messages", | ||
callback=lambda payload: print("All inserts in messages table: ", payload), | ||
) | ||
|
||
channel.on_postgres_changes( | ||
"UPDATE", | ||
schema="public", | ||
table="users", | ||
filter="username=eq.Realtime", | ||
callback=lambda payload: print( | ||
"All updates on users table when username is Realtime: ", payload | ||
), | ||
) | ||
|
||
channel.subscribe( | ||
lambda status, err: status == RealtimeSubscribeStates.SUBSCRIBED | ||
and print("Ready to receive database changes!") | ||
) | ||
``` | ||
|
||
## Get All Channels | ||
|
||
You can see all the channels that your client has instantiated. | ||
|
||
## Sample usage with Supabase | ||
```python | ||
# Setup... | ||
|
||
Here's how you could connect to your realtime endpoint using Supabase endpoint. Correct as of 5th June 2021. Please replace `SUPABASE_ID` and `API_KEY` with your own `SUPABASE_ID` and `API_KEY`. The variables shown below are fake and they will not work if you try to run the snippet. | ||
client.get_channels() | ||
``` | ||
|
||
## Cleanup | ||
|
||
It is highly recommended that you clean up your channels after you're done with them. | ||
|
||
- Remove a single channel | ||
|
||
```python | ||
from realtime.connection import Socket | ||
# Setup... | ||
|
||
SUPABASE_ID = "dlzlllxhaakqdmaapvji" | ||
API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MT" | ||
channel = client.channel('some-channel-to-remove') | ||
|
||
channel.subscribe() | ||
|
||
def callback1(payload): | ||
print("Callback 1: ", payload) | ||
await client.remove_channel(channel) | ||
``` | ||
|
||
- Remove all channels | ||
|
||
```python | ||
# Setup... | ||
|
||
if __name__ == "__main__": | ||
URL = f"wss://{SUPABASE_ID}.supabase.co/realtime/v1/websocket?apikey={API_KEY}&vsn=1.0.0" | ||
s = Socket(URL) | ||
s.connect() | ||
channel1 = client.channel('a-channel-to-remove') | ||
channel2 = client.channel('another-channel-to-remove') | ||
|
||
channel_1 = s.set_channel("realtime:*") | ||
channel_1.join().on("UPDATE", callback1) | ||
s.listen() | ||
await channel1.subscribe() | ||
await channel2.subscribe() | ||
|
||
await client.remove_all_channels() | ||
``` | ||
|
||
Then, go to the Supabase interface and toggle a row in a table. You should see a corresponding payload show up in your console/terminal. | ||
## Credits | ||
|
||
This repo draws heavily from [phoenix-js](https://github.com/phoenixframework/phoenix/tree/master/assets/js/phoenix). | ||
|
||
## License | ||
|
||
MIT. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it was failing on CI, don't know why, need to check it after.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe coverage requirements or missing plugin? In either case I think should be fine to do without
As an aside probably can consider moving towards using poetry scripts in the future when time frees up