Skip to content

Commit 7d59cd7

Browse files
committed
refactor and add gitignore
1 parent e8a4124 commit 7d59cd7

11 files changed

Lines changed: 290 additions & 117 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,5 @@ target/
6262
.python-version
6363
.DS_Store
6464

65-
.mypy_cache/
65+
.mypy_cache/
66+
.vscode/

examples/flask_gevent/__init__.py

Whitespace-only changes.
Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,37 @@
1-
from flask import Flask, make_response
2-
from flask_sockets import Sockets
3-
from graphql_ws.server import GeventSubscriptionServer
41
import json
5-
from template import render_graphiql
2+
63
import graphene
7-
import gevent
4+
from flask import Flask, make_response
85
from flask_graphql import GraphQLView
9-
import asyncio
6+
from flask_sockets import Sockets
107
from rx import Observable
118

9+
from graphql_ws import GeventSubscriptionServer
10+
from template import render_graphiql
11+
1212

1313
class Query(graphene.ObjectType):
1414
base = graphene.String()
1515

1616

17+
class RandomType(graphene.ObjectType):
18+
seconds = graphene.Int()
19+
random_int = graphene.Int()
20+
21+
1722
class Subscription(graphene.ObjectType):
1823

19-
count_seconds = graphene.String()
24+
count_seconds = graphene.Int()
25+
26+
random_int = graphene.Field(RandomType)
2027

2128

2229
def resolve_count_seconds(root, info):
2330
return Observable.interval(1000).map(lambda i: "{0}".format(i))
2431

32+
def resolve_random_int(root, info):
33+
import random
34+
return Observable.interval(1000).map(lambda i: RandomType(seconds=i, random_int=random.randint(0, 500)))
2535

2636
schema = graphene.Schema(query=Query, subscription=Subscription)
2737

@@ -52,4 +62,4 @@ def echo_socket(ws):
5262
from gevent import pywsgi
5363
from geventwebsocket.handler import WebSocketHandler
5464
server = pywsgi.WSGIServer(('', 5000), app, handler_class=WebSocketHandler)
55-
server.serve_forever()
65+
server.serve_forever()

examples/flask_gevent/src/graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit ebcd7f29878f995b3cfd91050f2117a87c368e47

examples/flask_gevent/template.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
2+
from string import Template
3+
4+
5+
def render_graphiql():
6+
return Template('''
7+
<!DOCTYPE html>
8+
<html>
9+
<head>
10+
<meta charset="utf-8" />
11+
<title>GraphiQL</title>
12+
<meta name="robots" content="noindex" />
13+
<style>
14+
html, body {
15+
height: 100%;
16+
margin: 0;
17+
overflow: hidden;
18+
width: 100%;
19+
}
20+
</style>
21+
<link href="//cdn.jsdelivr.net/graphiql/${GRAPHIQL_VERSION}/graphiql.css" rel="stylesheet" />
22+
<script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script>
23+
<script src="//cdn.jsdelivr.net/react/15.0.0/react.min.js"></script>
24+
<script src="//cdn.jsdelivr.net/react/15.0.0/react-dom.min.js"></script>
25+
<script src="//cdn.jsdelivr.net/graphiql/${GRAPHIQL_VERSION}/graphiql.min.js"></script>
26+
<script src="//unpkg.com/subscriptions-transport-ws@${SUBSCRIPTIONS_TRANSPORT_VERSION}/browser/client.js"></script>
27+
<script src="//unpkg.com/graphiql-subscriptions-fetcher@0.0.2/browser/client.js"></script>
28+
</head>
29+
<body>
30+
<script>
31+
// Collect the URL parameters
32+
var parameters = {};
33+
window.location.search.substr(1).split('&').forEach(function (entry) {
34+
var eq = entry.indexOf('=');
35+
if (eq >= 0) {
36+
parameters[decodeURIComponent(entry.slice(0, eq))] =
37+
decodeURIComponent(entry.slice(eq + 1));
38+
}
39+
});
40+
// Produce a Location query string from a parameter object.
41+
function locationQuery(params, location) {
42+
return (location ? location: '') + '?' + Object.keys(params).map(function (key) {
43+
return encodeURIComponent(key) + '=' +
44+
encodeURIComponent(params[key]);
45+
}).join('&');
46+
}
47+
// Derive a fetch URL from the current URL, sans the GraphQL parameters.
48+
var graphqlParamNames = {
49+
query: true,
50+
variables: true,
51+
operationName: true
52+
};
53+
var otherParams = {};
54+
for (var k in parameters) {
55+
if (parameters.hasOwnProperty(k) && graphqlParamNames[k] !== true) {
56+
otherParams[k] = parameters[k];
57+
}
58+
}
59+
var fetcher;
60+
if (true) {
61+
var subscriptionsClient = new window.SubscriptionsTransportWs.SubscriptionClient('${subscriptionsEndpoint}', {
62+
reconnect: true
63+
});
64+
fetcher = window.GraphiQLSubscriptionsFetcher.graphQLFetcher(subscriptionsClient, graphQLFetcher);
65+
} else {
66+
fetcher = graphQLFetcher;
67+
}
68+
// We don't use safe-serialize for location, because it's not client input.
69+
var fetchURL = locationQuery(otherParams, '${endpointURL}');
70+
// Defines a GraphQL fetcher using the fetch API.
71+
function graphQLFetcher(graphQLParams) {
72+
return fetch(fetchURL, {
73+
method: 'post',
74+
headers: {
75+
'Accept': 'application/json',
76+
'Content-Type': 'application/json',
77+
},
78+
body: JSON.stringify(graphQLParams),
79+
credentials: 'include',
80+
}).then(function (response) {
81+
return response.text();
82+
}).then(function (responseBody) {
83+
try {
84+
return JSON.parse(responseBody);
85+
} catch (error) {
86+
return responseBody;
87+
}
88+
});
89+
}
90+
// When the query and variables string is edited, update the URL bar so
91+
// that it can be easily shared.
92+
function onEditQuery(newQuery) {
93+
parameters.query = newQuery;
94+
updateURL();
95+
}
96+
function onEditVariables(newVariables) {
97+
parameters.variables = newVariables;
98+
updateURL();
99+
}
100+
function onEditOperationName(newOperationName) {
101+
parameters.operationName = newOperationName;
102+
updateURL();
103+
}
104+
function updateURL() {
105+
history.replaceState(null, null, locationQuery(parameters) + window.location.hash);
106+
}
107+
// Render <GraphiQL /> into the body.
108+
ReactDOM.render(
109+
React.createElement(GraphiQL, {
110+
fetcher: fetcher,
111+
onEditQuery: onEditQuery,
112+
onEditVariables: onEditVariables,
113+
onEditOperationName: onEditOperationName,
114+
}),
115+
document.body
116+
);
117+
</script>
118+
</body>
119+
</html>''').substitute(
120+
GRAPHIQL_VERSION='0.10.2',
121+
SUBSCRIPTIONS_TRANSPORT_VERSION='0.7.0',
122+
subscriptionsEndpoint='ws://localhost:5000/subscriptions',
123+
# subscriptionsEndpoint='ws://localhost:5000/',
124+
endpointURL='/graphql',
125+
)

examples/src/graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Subproject commit 6df8a6312b579a6a1454bcf29a566ce5d0fa9849
1+
Subproject commit ebcd7f29878f995b3cfd91050f2117a87c368e47

examples/template.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def render_graphiql():
119119
</html>''').substitute(
120120
GRAPHIQL_VERSION='0.10.2',
121121
SUBSCRIPTIONS_TRANSPORT_VERSION='0.7.0',
122-
subscriptionsEndpoint='ws://localhost:5000/subscriptions',
122+
subscriptionsEndpoint='ws://localhost:8000/subscriptions',
123123
# subscriptionsEndpoint='ws://localhost:5000/',
124124
endpointURL='/graphql',
125125
)

graphql_ws/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99

1010
from .observable_aiter import setup_observable_extension
1111
from .server import WebSocketSubscriptionServer
12-
12+
from .gevent_server import GeventSubscriptionServer
1313

1414
setup_observable_extension()

graphql_ws/constants.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
GRAPHQL_WS = 'graphql-ws'
2+
WS_PROTOCOL = GRAPHQL_WS
3+
4+
GQL_CONNECTION_INIT = 'connection_init' # Client -> Server
5+
GQL_CONNECTION_ACK = 'connection_ack' # Server -> Client
6+
GQL_CONNECTION_ERROR = 'connection_error' # Server -> Client
7+
8+
# NOTE: This one here don't follow the standard due to connection optimization
9+
GQL_CONNECTION_TERMINATE = 'connection_terminate' # Client -> Server
10+
GQL_CONNECTION_KEEP_ALIVE = 'ka' # Server -> Client
11+
GQL_START = 'start' # Client -> Server
12+
GQL_DATA = 'data' # Server -> Client
13+
GQL_ERROR = 'error' # Server -> Client
14+
GQL_COMPLETE = 'complete' # Server -> Client
15+
GQL_STOP = 'stop' # Client -> Server

graphql_ws/gevent_server.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import json
2+
3+
from graphql import format_error, graphql
4+
from graphql.execution import ExecutionResult
5+
from graphql.execution.executors.sync import SyncExecutor
6+
from rx import Observer
7+
from promise import is_thenable, Promise
8+
from .server import BaseWebSocketSubscriptionServer, ConnectionContext, ConnectionClosedException
9+
from .constants import *
10+
11+
12+
class GEventConnectionContext(ConnectionContext):
13+
14+
def receive(self):
15+
msg = self.ws.receive()
16+
return msg
17+
18+
def send(self, data):
19+
if self.closed:
20+
return
21+
self.ws.send(data)
22+
23+
@property
24+
def closed(self):
25+
return self.ws.closed
26+
27+
def close(self, code):
28+
self.ws.close(code)
29+
30+
class GeventSubscriptionServer(BaseWebSocketSubscriptionServer):
31+
32+
def get_graphql_params(self, *args, **kwargs):
33+
params = super(GeventSubscriptionServer, self).get_graphql_params(*args, **kwargs)
34+
return dict(params, executor=SyncExecutor())
35+
36+
def handle(self, ws):
37+
connection_context = GEventConnectionContext(ws)
38+
self.on_open(connection_context)
39+
while True:
40+
try:
41+
if connection_context.closed:
42+
raise ConnectionClosedException()
43+
message = connection_context.receive()
44+
except ConnectionClosedException:
45+
self.on_close(connection_context)
46+
return
47+
self.on_message(connection_context, message)
48+
49+
def on_message(self, connection_context, message):
50+
try:
51+
parsed_message = json.loads(message)
52+
assert isinstance(
53+
parsed_message, dict), "Payload must be an object."
54+
except Exception as e:
55+
self.send_error(connection_context, None, e)
56+
return
57+
58+
self.process_message(connection_context, parsed_message)
59+
60+
def on_open(self, connection_context):
61+
pass
62+
63+
def on_connect(self, connection_context, payload):
64+
pass
65+
66+
def on_close(self, connection_context):
67+
remove_operations = list(connection_context.operations.keys())
68+
for op_id in remove_operations:
69+
self.unsubscribe(connection_context, op_id)
70+
71+
def on_connection_init(self, connection_context, op_id, payload):
72+
try:
73+
self.on_connect(connection_context, payload)
74+
self.send_message(connection_context, op_type=GQL_CONNECTION_ACK)
75+
76+
except Exception as e:
77+
self.send_error(connection_context, op_id, e, GQL_CONNECTION_ERROR)
78+
connection_context.close(1011)
79+
80+
def on_connection_terminate(self, connection_context, op_id):
81+
connection_context.close(1011)
82+
83+
84+
def on_start(self, connection_context, op_id, params):
85+
try:
86+
execution_result = graphql(
87+
self.schema, **params, allow_subscriptions=True
88+
)
89+
execution_result.subscribe(SubscriptionObserver(
90+
connection_context,
91+
op_id,
92+
self.send_execution_result,
93+
self.send_error,
94+
self.on_close
95+
)
96+
)
97+
except Exception as e:
98+
self.send_error(connection_context, op_id, str(e))
99+
100+
def on_stop(self, connection_context, op_id):
101+
self.unsubscribe(connection_context, op_id)
102+
103+
104+
class SubscriptionObserver(Observer):
105+
106+
def __init__(self, connection_context, op_id, send_execution_result, send_error, on_close):
107+
self.connection_context = connection_context
108+
self.op_id = op_id
109+
self.send_execution_result = send_execution_result
110+
self.send_error = send_error
111+
self.on_close = on_close
112+
113+
def on_next(self, value):
114+
self.send_execution_result(self.connection_context, self.op_id, value)
115+
116+
def on_completed(self):
117+
self.on_close(self.connection_context)
118+
119+
def on_error(self, error):
120+
self.send_error(self.connection_context, self.op_id, error)

0 commit comments

Comments
 (0)