graphql-python / graphql-ws Goto Github PK
View Code? Open in Web Editor NEWGraphQL websockets
License: MIT License
GraphQL websockets
License: MIT License
I checked the flask_gevent
example and finded out the resolve stream does not ended when client is disconnected.
I mean the lambda inside of this
def resolve_random_int(root, info):
return Observable.interval(1000).map(lambda i: RandomType(seconds=i, random_int=random.randint(0, 500)))
continues be called every 1000 ms after a client breaks ws connection.
Is there way to break resolving somehow? I want to a client be subscribed all time and got a new portion of data time by time
Hi @SmileyChris
I am wondering if you are gonna create a pre-release of the project to support graphene 3, and when would that be ?
Best
I have forked graphql-ws-next (fork) and made changes to make it compatible with graphql-core v3. Would there be any interest in integrating these chagnes into graphql-ws or are the two totally split now?
Is it possible to use resolve_
method on nested objects like in following updated aiohttp example code?
import random
import asyncio
import graphene
class Query(graphene.ObjectType):
base = graphene.String()
class RandomType(graphene.ObjectType):
seconds = graphene.Int()
random_int = graphene.Int()
async def resolve_random_int(_, __):
return random.randint(0, 500)
class Subscription(graphene.ObjectType):
random_int = graphene.Field(RandomType)
async def resolve_random_int(root, info):
i = 0
while True:
yield RandomType(seconds=i)
await asyncio.sleep(1.)
i += 1
schema = graphene.Schema(query=Query, subscription=Subscription)
I'm getting following error message
{
"id": 26,
"type": "data",
"payload": {
"data": {
"random_int": null
},
"errors": [
{
"message": "int() argument must be a string, a bytes-like object or a number, not 'AnonymousObservable'"
}
]}}
Not sure if this is intended or not but shouldn't the context be taken outside the message processing?
In base.py:
def execute(self, request_context, params):
return graphql(
self.schema, **dict(params, allow_subscriptions=True))
Should be:
self.schema, **dict(params, context_value= request_context, allow_subscriptions=True))
Otherwise, how can you pass the request_context
? Does it make sense to be per message? Or is it intended for another use?
GraphQL-ws seems to work fine for me but when I'm using it it seems like I have a version of GraphIQl where the "HTTP Headers" window is missing. How could I solve that ?
In my settings.py : INSTALLED_APPS = [ ... 'channels', 'graphql_ws.django', 'graphene_django', ]
queries using SSL from the GraphQL playground work; subscriptions do not.
I added an ssl_context to web.run_app; queries worked fine from the Playground client.
But when I tried a subscription (and it worked without SSL!):
Playground: "error": "Could not connect to websocket endpoint ws://myhostnamealias.com:9000/subscriptions. Please check if the endpoint url is correct."
Server: SSL error in data received protocol: <asyncio.sslproto.SSLProtocol object at 0x7f03ce8cbe48> transport: <_SelectorSocketTransport closing fd=8 read=idle write=<idle, bufsize=0>> Traceback (most recent call last): File "/usr/lib/python3.7/asyncio/sslproto.py", line 526, in data_received ssldata, appdata = self._sslpipe.feed_ssldata(data) File "/usr/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata self._sslobj.do_handshake() File "/usr/lib/python3.7/ssl.py", line 763, in do_handshake self._sslobj.do_handshake() ssl.SSLError: [SSL: HTTP_REQUEST] http request (_ssl.c:1056)
Now one curious thing I noticed: I was using certs for a specific host, and was using that same host in my URL in the GraphQL playground; but the playground error was using an alias host for the box. Is that coming from GraphQL Playground doing a reverse IP lookup? Or was aiohttp or graphql-ws doing a reverse name lookup somewhere because I used the aiohttp default host of "0.0.0.0"??
To rule that out, I reran the server with certificates for the other host name GraphQL had somewhere got and thought was the "right" one to use, and on aiohttp's web.run_app I used a "host" argument with the fully qualified host name the playground seemed to think was the "correct" one. Same result: queries worked; a subscription query reissued the same "Could not connect to websocket endpoint..." error. Removed the SSL from aiohttp and used "http:" in my queries+subscriptions -all worked.
Are there perhaps extra config options for the Playground when using SSL+subscriptions? I couldn't find any relevant ones in a search. Who's injecting that alias name into the Playground's error message? (even though my URL in playground did NOT use it...) Playground? aiohttp? Or graphql-ws??
Something else I just noticed: the Playground client error is showing a "ws:" scheme, as opposed to a "wss:" one. Perhaps that has to be configured in the Playground client? In the graphql-ws aiohttp example, "protocols" is set to "graphql-ws"; is "graphql-wss" also supported? (or about to be supported??)
After python setup.py develop
and pip install -r requirements_dev.txt
I tried to run example and got this error. I guess requirements_dev.txt
should include websockets
dependency.
I have the flask subscription example up and running and everything worked perfect. I'm trying to get it to work using flask socketio without any luck. Has anybody else ran into this using flask socketio? I feel like it's probably really obvious but I've searched pretty extensively with no luck.
Here is the original flask example(I left out the rest of the file since it's identical to the example) with the flask sockets commented out.
#sockets = Sockets(app)
socket_io = SocketIO(app)
# @sockets.route('/subscriptions')
# def echo_socket(ws):
# subscription_server.handle(ws)
# return []
@socket_io.on('subscriptions', namespace='/subscriptions')
def echo_socket(ws):
subscription_server.handle(ws)
return []
There was no errors, but also nothing returned in GraphiQL when I tested the subscription.
Attempting to run the example aiohttp application and retrieve subscription information in graphql playground.
I am running the example app via app.py in the examples/aiohttp directory.
python app.py
Server starts and is listening on port 8000.
I then start graphql playground and point it to http://localhost:8000/graphql.
It retrieves the schema correctly as follows:
type Query {
base: String
}
type RandomType {
seconds: Int
randomInt: Int
}
type Subscription {
countSeconds(upTo: Int): Float
randomInt: RandomType
}
I attempt to open up the websocket connection to listen for server updates via:
subscription {
countSeconds
}
What happened:
The server logged the following output
Error handling request
Traceback (most recent call last):
File "/home/danny/.virtualenvs/subs/lib/python3.6/site-packages/aiohttp/web_protocol.py", line 418, in start
resp = await task
File "/home/danny/.virtualenvs/subs/lib/python3.6/site-packages/aiohttp/web_app.py", line 458, in _handle
resp = await handler(request)
File "./app.py", line 15, in graphql_view
payload = await request.json()
File "/home/danny/.virtualenvs/subs/lib/python3.6/site-packages/aiohttp/web_request.py", line 584, in json
return loads(body)
File "/usr/lib/python3.6/json/__init__.py", line 354, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.6/json/decoder.py", line 339, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.6/json/decoder.py", line 357, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Graphql playground displayed the following error:
{
"error": "Could not connect to websocket endpoint ws://localhost:8000/graphql. Please check if the endpoint url is correct."
}
What I expected:
I expected to see the normal response in graphql playground for a subscription ->
Listening ...
Hello maintainers,
Thank you for this repo ๐
Just wanted to know if you have any plans for releasing the current version of the master branch to PyPI?
Since release 0.3.1 does not include Django sub-module.
I'm have a subscription going, an async def
method (like the example in the readme) but it uses while True
instead of range
, so it never ends, it is constantly sending (yield
) new values. I can connect to it using Relay, and I can see the frames coming in DevTools, and I can see the frame telling the GraphQL server to stop on that subscription, and then the frames stop coming.
After the stop
frame I get errors like this in the console:
2017-12-27 12:01:43,393 [25768] ERROR asyncio: Task was destroyed but it is pending!
task: <Task pending coro=<AiohttpSubscriptionServer.on_start() done, defined at ~/.env/lib/python3.6/site-packages/graphql_ws/aiohttp.py:80> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7fcb0dc4b918>()]>>
And sometime I have to wait a bit for the server to shut down (by hitting Ctrl-C
), like if there are more "tasks" pending.
This is the subscription class I'm using:
class DataPoint(graphene.ObjectType):
time = graphene.Float(required=True)
value = graphene.Float(required=True)
class Subscriptions(graphene.ObjectType):
graph_data = graphene.List(DataPoint, object_id=graphene.ID(required=True))
async def resolve_graph_data(self, info, object_id=None):
time = 0
logger.debug('>>>>>>>> Start graph')
while True:
yield [DataPoint(time=time, value=randint(100, 400))]
await asyncio.sleep(1.)
time += 1
logger.debug('>>>>>>>> End graph')
I can see the Start graph
line being printed, but I never see the End graph
line.
I guess what we need is some way of "breaking" the while True:
loop from outside, right?
The current pypi release is pretty outdated, especially the AiohttpSubscriptionServer. Would a new release be possible currently?
Attempting to run the example gevent application and retrieve subscription information in graphql playground.
I am running the example app via app.py in the examples/flask-gevent directory.
python app.py
Server starts and is listening on port 5000.
I then start graphql playground and point it to http://localhost:5000/graphql.
It retrieves the schema correctly as follows:
type Query {
base: String
}
type RandomType {
seconds: Int
randomInt: Int
}
type Subscription {
countSeconds(upTo: Int): Int
randomInt: RandomType
}
I attempt to open up the websocket connection to listen for server updates via:
subscription {
countSeconds
}
The server displayed no output whatsoever.
Graphql playground displayed the following error:
{
"error": "Could not connect to websocket endpoint ws://localhost:5000/graphql. Please check if the endpoint url is correct."
}
What I expected:
I expected to see the normal response in graphql playground for a subscription ->
Listening ...
I'm baffled why I can't find any issues on using django-channels since channels have been out with v2 as default since february, and nor the example or the code is compatible..
There are a couple of pr's (#18 and #9), but none of them is merged..
Any status on when we will have a v2 compatible release? What is missing?
PubSub seems to broken, getting this error
ModuleNotFoundError: No module named 'graphql_ws.pubsub'
following this repo https://github.com/hballard/graphql-ws
Accessing a field with an async resolver in an object type that resolves with an async resolver doesn't seem to work.
Consider the following modification to https://github.com/graphql-python/graphql-ws/blob/master/examples/aiohttp/schema.py
import asyncio
import graphene
class Query(graphene.ObjectType):
base = graphene.String()
class NewType(graphene.ObjectType):
new_field = graphene.String()
async def resolve_new_field(self, info):
await asyncio.sleep(1.)
return 'hello'
class Subscription(graphene.ObjectType):
new_type = graphene.Field(NewType)
async def resolve_new_type(root, info):
while True:
yield NewType()
await asyncio.sleep(1.)
schema = graphene.Schema(query=Query, subscription=Subscription)
Results in
TypeError: Object of type 'Promise' is not JSON serializable
--
Traceback (most recent call last):
File "src/cars/sub/app.py", line 43, in <module>
web.run_app(app, port=8000)
File "C:\programs\anaconda3\envs\cars\lib\site-packages\aiohttp\web.py", line
121, in run_app
loop.run_until_complete(runner.cleanup())
...
Changing resolve_new_field
to
def resolve_new_field(self, info):
return 'hello'
works as expected, but I should think having an async resolver here should be fine as well.
Then I close ws connection to server after subscription some asyncio tasks are not destroyed. If I reload page of my webapp several times, there are a lot of existing tasks. It's possible memory leak.
Example app:
from aiohttp.web import Application, run_app, WebSocketResponse
from graphql_ws.aiohttp import AiohttpSubscriptionServer
from graphene import Schema, ObjectType, Field, ID
# Schema
class HistoryRecord(ObjectType):
id = ID()
class Query(ObjectType):
history = Field(HistoryRecord)
def resolve_history(self, info):
return HistoryRecord(id='first')
class Subscription(ObjectType):
history = Field(HistoryRecord)
def resolve_history(self, info):
async def resolver(info):
yield HistoryRecord(id='second')
return resolver(info)
schema = Schema(query=Query, subscription=Subscription)
# Server
class Server(AiohttpSubscriptionServer):
async def on_open(self, *args, **kwargs):
print('Connection open')
await super().on_open(*args, **kwargs)
def on_close(self, *args, **kwargs):
print('Connection close')
super().on_close(*args, **kwargs)
subscription_server = Server(schema)
# WS handler
async def subscriptions(request):
ws = WebSocketResponse(protocols=('graphql-ws',))
await ws.prepare(request)
await subscription_server.handle(ws)
return ws
# Application
app = Application()
app.router.add_get('/ws', subscriptions)
# List tasks
from asyncio import Task, get_event_loop, sleep
from sys import getrefcount
from time import time
async def test():
count = 0
while True:
await sleep(15)
count += 1
print('. Timer iteration N', count)
tasks = Task.all_tasks()
print('.. Total tasks: %s' % len(tasks))
for task in sorted(tasks, key=lambda x: str(x)):
print('... refs={} {}'.format(getrefcount(task), task))
loop = get_event_loop()
loop.create_task(test())
# Fire!
run_app(app, host='0.0.0.0', port=8888)
Task list after 3 connections. Full log attached.
Connection open
. Timer iteration N 6
.. Total tasks: 14
... refs=4 <Task finished coro=<AiohttpSubscriptionServer._handle() done, defined at /home/gik/src/graphql/graphql-ws/graphql_ws/aiohttp.py:54> result=None>
... refs=4 <Task finished coro=<AiohttpSubscriptionServer._handle() done, defined at /home/gik/src/graphql/graphql-ws/graphql_ws/aiohttp.py:54> result=None>
... refs=4 <Task finished coro=<AiohttpSubscriptionServer.on_connection_init() done, defined at /home/gik/src/graphql/graphql-ws/graphql_ws/aiohttp.py:93> result=None>
... refs=4 <Task finished coro=<AiohttpSubscriptionServer.on_connection_init() done, defined at /home/gik/src/graphql/graphql-ws/graphql_ws/aiohttp.py:93> result=None>
... refs=5 <Task finished coro=<AiohttpSubscriptionServer.on_connection_init() done, defined at /home/gik/src/graphql/graphql-ws/graphql_ws/aiohttp.py:93> result=None>
... refs=6 <Task finished coro=<AiohttpSubscriptionServer.on_start() done, defined at /home/gik/src/graphql/graphql-ws/graphql_ws/aiohttp.py:101> result=None>
... refs=4 <Task finished coro=<AiohttpSubscriptionServer.on_start() done, defined at /home/gik/src/graphql/graphql-ws/graphql_ws/aiohttp.py:101> result=None>
... refs=4 <Task finished coro=<AiohttpSubscriptionServer.on_start() done, defined at /home/gik/src/graphql/graphql-ws/graphql_ws/aiohttp.py:101> result=None>
... refs=6 <Task finished coro=<RequestHandler.start() done, defined at /usr/lib/python3/dist-packages/aiohttp/web_protocol.py:340> result=None>
... refs=5 <Task finished coro=<RequestHandler.start() done, defined at /usr/lib/python3/dist-packages/aiohttp/web_protocol.py:340> result=None>
... refs=4 <Task finished coro=<RequestHandler.start() done, defined at /usr/lib/python3/dist-packages/aiohttp/web_protocol.py:340> result=None>
... refs=5 <Task pending coro=<AiohttpSubscriptionServer._handle() running at /home/gik/src/graphql/graphql-ws/graphql_ws/aiohttp.py:62> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7fcec832af48>()]> cb=[shield.<locals>._done_callback() at /usr/lib/python3.6/asyncio/tasks.py:688]>
... refs=8 <Task pending coro=<RequestHandler.start() running at /usr/lib/python3/dist-packages/aiohttp/web_protocol.py:376> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at0x7fcec832ab58>()]>>
... refs=6 <Task pending coro=<test() running at ./bin/ex.py:63>>
Connection close
I've implemented graphql_ws to subscribe to updates from a Notification
model that uses multiple GenericForeignKeys
.
My setup works well, except when I try to query information from those foreign objects. I then get the following error:
graphql.error.located_error.GraphQLLocatedError: You cannot call this from an async context - use a thread or sync_to_async.
From what I seem to understand, that's because some database query operations are being done outside the async context of get_notifications
(that is, in the resolve_actor
function). But I'm really not clear on how I can pull the operations of resolve_actor
into the async context.
I've unsuccessfully tried using prefetch_related
(plus I'm not sure it'll work with multiple content types per Django's docs).
models.py
class Notification(TimeStampedModel):
# ...
actor_content_type = models.ForeignKey(ContentType, related_name='notify_actor', on_delete=models.CASCADE)
actor_object_id = models.CharField(max_length=255)
actor = GenericForeignKey('actor_content_type', 'actor_object_id')
# ...
schema.py
class ActorTypeUnion(graphene.Union):
"""
All possible types for Actors
(The object that performed the activity.)
"""
class Meta:
types = (UserType,) # here's there's only one type, but other fields have multiple
class NotificationType(DjangoObjectType):
actor = graphene.Field(ActorTypeUnion)
def resolve_actor(self, args):
if self.actor is not None:
model_name = self.actor._meta.model_name
app_label = self.actor._meta.app_label
model = ContentType.objects.get(app_label=app_label, model=model_name)
return model.get_object_for_this_type(pk=self.actor_object_id)
return None
# ...
class Meta:
model = Notification
class Subscription(graphene.ObjectType):
unread_notifications = graphene.List(NotificationType)
async def resolve_unread_notifications(self, info, **kwargs):
user = info.context['user']
if user.is_anonymous:
raise Exception('Not logged in!')
@database_sync_to_async
def get_notifications(user):
notifications = Notification.objects.filter(
recipient=user,
read=False,
organization=user.active_organization,
)
return [notifications]
while True:
await asyncio.sleep(1)
yield await get_notifications(user)
actor
)subscription {
unreadNotifications {
id,
read,
actor {
... on UserType {
__typename,
id
}
}
}
}
Traceback (most recent call last):
File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/graphql/execution/executor.py", line 452, in resolve_or_error
return executor.execute(resolve_fn, source, info, **args)
File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/graphql/execution/executors/asyncio.py", line 74, in execute
result = fn(*args, **kwargs)
File "/Users/benjaminsoukiassian/Projects/logbook-back/logbook/notifications/schema.py", line 52, in resolve_actor
model = ContentType.objects.get(app_label=app_label, model=model_name)
File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/query.py", line 431, in get
num = len(clone)
File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/query.py", line 262, in __len__
self._fetch_all()
File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/query.py", line 1324, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/query.py", line 51, in __iter__
results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1173, in execute_sql
cursor = self.connection.cursor()
File "/Users/benjaminsoukiassian/.pyenv/versions/logbook/lib/python3.8/site-packages/django/utils/asyncio.py", line 24, in inner
raise SynchronousOnlyOperation(message)
graphql.error.located_error.GraphQLLocatedError: You cannot call this from an async context - use a thread or sync_to_async.
Thank you very much for your help
I'm trying to run a basic Django server with subscriptions enabled and I'm receiving an error on the command line.
I prepared a basic Django server, enabled channels, and confirmed everything was working.
I then setup graphql-ws as described in the README. When I launch the server I see the following on the command line (it gets printed repeatedly):
Exception inside application: object.__init__() takes exactly one argument (the instance to initialize)
Traceback (most recent call last):
File "/home/luke/.cache/pypoetry/virtualenvs/annai-EdSLGIjn-py3.9/lib/python3.9/site-packages/channels/staticfiles.py", line 44, in __call__
return await self.application(scope, receive, send)
File "/home/luke/.cache/pypoetry/virtualenvs/annai-EdSLGIjn-py3.9/lib/python3.9/site-packages/channels/routing.py", line 71, in __call__
return await application(scope, receive, send)
File "/home/luke/.cache/pypoetry/virtualenvs/annai-EdSLGIjn-py3.9/lib/python3.9/site-packages/channels/routing.py", line 150, in __call__
return await application(
File "/home/luke/.cache/pypoetry/virtualenvs/annai-EdSLGIjn-py3.9/lib/python3.9/site-packages/asgiref/compatibility.py", line 33, in new_application
instance = application(scope)
File "/home/luke/.cache/pypoetry/virtualenvs/annai-EdSLGIjn-py3.9/lib/python3.9/site-packages/channels/generic/websocket.py", line 159, in __init__
super().__init__(*args, **kwargs)
TypeError: object.__init__() takes exactly one argument (the instance to initialize)
I am trying to get clients to subscribe to my graphql with a nested object in django. The request looks like this:
subscription {
taskUpdated(id:"04a5e92bc9084ad981481ab4314a1d33", updateInterval: 3) {
id
token
result {
id,
profiles {
id
}
}
}
}
Tasks contain one result which contain multiple profiles.
In my subscription class I used the sync_to_async method which works if used for the first level:
class Subscription(graphene.ObjectType):
task_updated = graphene.Field(
TaskType, id=graphene.String(), update_interval=graphene.Int()
)
async def resolve_task_updated(root, info, id, update_interval):
print("TRYING TO RESOLVE")
while True:
print("While true")
task = await sync_to_async(Task.objects.get, thread_sensitive=True)(
pk=id
)
yield task
await asyncio.sleep(update_interval)
Using the query only targeting task.id everything works perfectly. Once I try to target task.result (which is a new object). I receive the error:
You cannot call this from an async context - use a thread or sync_to_async.
I managed to get a workaround by updating the TaskType and updating the resolver for the result like so:
class TaskType(DjangoObjectType):
class Meta:
model = TaskModel
fields = "__all__"
result = graphene.Field(TaskResultType)
async def resolve_result(self, info):
return await sync_to_async(
TaskResult.objects.get, thread_sensitive=True
)(task=self.id)
That way I could get to the next level (task -> result). When I try to subscribe to task -> result -> profiles there is no way to receive the data anymore. The same error from above is thrown. Even if I update the ResultType accordingly to fetch all profiles async:
class TaskResultType(DjangoObjectType):
class Meta:
model = TaskResult
fields = "__all__"
profiles = graphene.List(ProfileType)
async def resolve_profiles(self, info):
return await sync_to_async(
Profile.objects.filter, thread_sensitive=True
)(task_result=self.id)
I thought dynamic querying of objects and subscribing to nested objects would be an out of the box functionality but it is really difficult. I can't be the only person using this repo to actually receive ORM data?
Hi, we are using graphql-ws
as part of our system. We have a meta-package that installs multiple dependencies including one JS app. Among the dependencies, the last external dependency not already in Conda Forge is graphql-ws
.
We are starting the process to publish it there, which is pretty straightforward, and I am adding myself as maintainer (basically will update recipe and merge/create PR's/issues if necessary).
Created recipe for Conda Forge conda-forge/staged-recipes#9765 with myself as maintainer.
Happy if any project maintainer would like to be a Conda recipe maintainer too - this would allow for faster release to Conda Forge and/or create any automation later.
Thanks!
I'm trying to set up GraphQL Subscriptions with Django Channels 2, but the code keeps on throwing this error:
ModuleNotFoundError: No module named 'channels.generic.websockets'
I checked the Django Channels docs and also the source code of graphql_ws and figured a typo:
In the first line of django_channels.py, instead of
from channels.generic.websockets import JsonWebsocketConsumer
should be
from channels.generic.websocket import JsonWebsocketConsumer
reference: https://channels.readthedocs.io/en/latest/topics/consumers.html#jsonwebsocketconsumer
python manage.py runserver
I'm running Daphnie with asgi.py but when I try to test it with wscat
it gives 403 (access denied) error in logs in daphne servers.
asgi.py
import os
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import OriginValidator
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "company.settings.base")
from graphql_ws.django.routing import websocket_urlpatterns
# This configures route to /subscriptions.
# https://github.com/graphql-python/graphql-ws/blob/474f733761c04f39ccd9367190b5a85a5033fc2c/graphql_ws/django/routing.py#L20
application = ProtocolTypeRouter({
"websocket": URLRouter(websocket_urlpatterns),
})
settings.py
# ...
INSTALLED_APPS = (
# ...
'channels',
"graphql_ws.django",
'graphene_django',
# ...
)
GRAPHENE = {
'SCHEMA': 'schema.schema',
'MIDDLEWARE': [
'graphql_jwt.middleware.JSONWebTokenMiddleware',
],
# https://docs.graphene-python.org/projects/django/en/latest/subscriptions/#subscriptions
"SUBSCRIPTION_PATH": "/subscriptions",
}
ASGI_APPLICATION = 'app.asgi.application'
# ...
Then I run daphne -p 8001 app.asgi:application -v 2
In other console, I ruh websocket commands with wscat -c ws://localhost:8001/subscriptions
it was giving me the logs (errors)
2022-01-21 12:23:10,555 INFO Listening on TCP address 127.0.0.1:8001
127.0.0.1:54400 - - [21/Jan/2022:12:23:16] "WSCONNECTING /subscriptions" - -
2022-01-21 12:23:16,476 DEBUG Upgraded connection ['127.0.0.1', 54400] to WebSocket
2022-01-21 12:23:16,476 INFO failing WebSocket opening handshake ('Access denied')
2022-01-21 12:23:16,477 WARNING dropping connection to peer tcp4:127.0.0.1:54400 with abort=False: Access denied
2022-01-21 12:23:16,477 DEBUG WebSocket ['127.0.0.1', 54400] rejected by application
127.0.0.1:54400 - - [21/Jan/2022:12:23:16] "WSREJECT /subscriptions" - -
2022-01-21 12:23:16,477 DEBUG WebSocket closed for ['127.0.0.1', 54400]
127.0.0.1:54400 - - [21/Jan/2022:12:23:16] "WSDISCONNECT /subscriptions" - -
wscat
> root # wscat -c ws://localhost:8001/subscriptions
error: Unexpected server response: 403
Seems like authorization error. I've tried with
OriginValidator(URLRouter(websocket_urlpatterns), ["*"]),
but no luck. I think it's related to graphql-ws configurations that I set up or maybe some steps I'm missing?
Paste the command(s) you ran and the output.
If there was a crash, please include the traceback here.
When testing the Django Channels examples, I went through some errors:
1: graphql_ws/django_channels.py
file is not on the latest package version. But I downloaded it manually inside the package.
2: I was getting the error "Subscriptions are not allowed. You will need to either use the subscribe function or pass allow_subscriptions=True"
. And, since the GraphQLView
object does not support the allow_subscriptions
parameter, I changed it inside the package to True
(just for testing).
3. After that, I was getting the error Subscription must return Async Iterable or Observable
. Reading this issue, I decided to add 'MIDDLEWARE: []
into the GRAPHENE
settings variable.
4. After that, I started receiving this error: 'AnonymousObservable' object has no attribute 'errors'
. Then I got a little bit frustrated and stop trying ๐
Does anyone have a clue why this is happening?
Thanks!
Hello!
Is there any plan to update graphql-core dependency to the latest version?
The Django Channels Routing example is outdated:
ImportError: cannot import name 'route_class' from 'channels.routing' (/home/MYUSERNAME/.pyenv/versions/3.9.0/envs/ENVNAME/src/channels/channels/routing.py)
route_class
seems to be from 1.1.8x
, which is from 2017. It's too old of a package for me to revert to. There are nested dependencies in Django and Channels. There should at least be an alternative in the instructions on how to achieve the same result in Channels 2.4.0
+.
Hello, how would I do if I wanted to bind the subscription to an update from a mutation ? For instance, I would like to subscribe to the adding of a particular type of object, a sort of "AddObjectMutation". The example in the documentation is nice but quite far from a real usage.
Thanks in advance,
simple example:
from graphql_ws.aiohttp import AiohttpSubscriptionServer
from .schema import schema
subscription_server = AiohttpSubscriptionServer(schema)
async def subscriptions(request):
ws = web.WebSocketResponse(protocols=('graphql-ws',))
await ws.prepare(request)
# payload = request.get('payload') ???
await subscription_server.handle(ws, request_context=payload)
return ws
app = web.Application()
app.router.add_get('/subscriptions', subscriptions)
web.run_app(app, port=8000)
How to get payload when init connection to socket?
{"type":"connection_init","payload":{"FOO":"BAR"}}
hello I am very new to this but wanted to know if there is a way to use this with django signals so as to send object to user on creation
Hello, i have a simple subscription which receive a just number. It works fine when i execute in Altair, but when i try to test it (I use pytest) it fails when i try to send my request
Here is my subscription (work fine)
@async_generator
async def resolve_get_number(self, info, number):
await yield_(number)
await asyncio.sleep(1.)
### my test
```
async def test_url(
cli):
async with cli.ws_connect("/subscriptions") as websocket:
my_dict = dict(id=1, type='start', payload={ # NOQA
'query': 'subscription{getNumber(number:10)}',
'variables': {},
})
ss = await websocket.send_str(json.dumps(my_dict)) # None
msg = await websocket.recieve() # recieve empty WSMessage(type=<WSMsgType.CLOSED: 257>, data=None, extra=None)
await websocket.close()
assert 'ok' == 'ok'
File "/home/dmitriy/.pyenv/versions/3.5.3/lib/python3.5/asyncio/futures.py", line 380, in __iter__
yield self # This tells Task to wait for completion.
```
Hi!
I'm using graphql-ws
module to make a simple subscription. To write this webapp's part I followed documentation posted on git. I thought all was ok but when I run my server it raises this error:
File "/home/maestro/exelio/gpexe/gpexe/urls.py", line 16, in <module>
from graphql_ws.django_channels import GraphQLSubscriptionConsumer
File "/home/maestro/exelio/gpexe/env2/lib/python3.8/site-packages/graphql_ws/django_channels.py", line 11, in <module>
from .base_sync import BaseSyncSubscriptionServer
File "/home/maestro/exelio/gpexe/env2/lib/python3.8/site-packages/graphql_ws/base_sync.py", line 1, in <module>
from graphql.execution.executors.sync import SyncExecutor
ModuleNotFoundError: No module named 'graphql.execution.executors'
I read that grapql-ws requires graphql-core==2.* , but i need the latest version of it...
Here is a part of my requirments:
graphene~=3.0b7
graphene-django==3.0.0b7
django-graphql-jwt==0.3.2
graphql-ws==0.4.4
graphene-django-optimizer==0.9.0
graphene_file_upload==1.3.0
graphql-core==3.1.6
How can I fix it? Thanks for the help in advance!
Django channels implements the ASGI spec. Many other frameworks also implement that spec, and any ASGI-compliant "app" can be mounted as part of another ASGI app(though usually framework-specific integrations are created to better exploit framework-specific features).
Recent versions of django channels and Starlette implements ASGI 3.0.
An ASGI implementation should enable integration with django-channels, starlette(and starlette-based frameworks) and any other asgi-based frameworks.
Python 3.5.* cannot work with the current package contents because PEP 525 doesn't introduce inspect.isasyncgen
until Python 3.6. This function is imported but never used, and injecting a fake version lets subscriptions work (but can have unintended consequences on other libraries).
It's just an import- try running it on Python 3.5.3+ with AIOHTTP if you need a repro.
When implementing this configuration https://www.apollographql.com/docs/angular/features/subscriptions.html#authentication I could not access connectionParams property on async def subscriptions(request):
Trying to figure out how to know when someone isn't subscribed anymore.
Ran the example which works great but unable to know when someone is disconnected..
import asyncio
import graphene
class Subscription(graphene.ObjectType):
count_seconds = graphene.Float(up_to=graphene.Int())
async def resolve_count_seconds(root, info, up_to=5):
for i in range(up_to):
print("YIELD SECOND", i)
yield i
await asyncio.sleep(1.)
yield up_to
schema = graphene.Schema(subscription=Subscription)
Seems nothing is in the progress on the library since July. Are there any plans on supporting the GraphQL Subscription anytime soon?
GeventSubscriptionServer ignore any schema validation errors.
I use Altair GraphQL client. When I send subscription request with schema text errors (for example: wrong field name), this error is covered by GeventSubscriptionServer and just raise "A subscription must return an observable"
I think problem in following lines in gevent.py
, line 74:75:
assert isinstance(execution_result, Observable), \ "A subscription must return an observable"
There's check only for Observable, but in case of schema error its return ExecutionResult object with errors, then assert is failed, another error is raising
Any subscription query with any schema error
This: graphql-core>=2.0<3
will install graphql-core-3.1.0b0
which has incompatible changes.
The correct format: graphql-core>=2.0,<3
or use the compatible release operator: graphql-core~=2.0
How to reproduce:
pip instal -r graphql-ws/examples/aiohttp/requirements.txt
python app.py
Subscriptions per the flask_gevent example, nearly verbatim.
Graphiql wouldn't render, nadda zilch. Looked at js console (after many other things I expected that I did wrong)
The CDN wasn't providing the CSS or graphiql js.
Switched out the graphiql js.min and css for https://cdnjs.com/libraries/graphiql/0.11.7 one from cloudflare and it rendered / things worked just dandy.
It's a two line change in the template.py, so doesn't seem worth the effort of a pull request :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.