Skip to content

Commit 41c1166

Browse files
update graphene example with latest schema changes (#236)
Resolves: #185 Co-authored-by: Patrick Arminio <[email protected]>
1 parent 1d946c8 commit 41c1166

File tree

5 files changed

+162
-74
lines changed

5 files changed

+162
-74
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,7 @@ obj
8282
# ignore router executable
8383
router
8484

85-
.bundle
85+
.bundle
86+
87+
# Python - ignore virtual env
88+
venv

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ The following open-source GraphQL server libraries and hosted subgraphs provide
112112
</thead>
113113
<tbody>
114114
<tr><td><a href="https://ariadnegraphql.org/docs/apollo-federation">Ariadne</a></td><td><table><tr><th>_service</th><td>🟢</td></tr><tr><th>@key (single)</th><td>🟢</td></tr><tr><th>@key (multi)</th><td>🟢</td></tr><tr><th>@key (composite)</th><td>🟢</td></tr><tr><th>repeatable @key</th><td>🟢</td></tr><tr><th>@requires</th><td>🟢</td></tr><tr><th>@provides</th><td>🟢</td></tr><tr><th>federated tracing</th><td>🔲</td></tr></table></td><td><table><tr><th>@link</th><td>🟢</td></tr><tr><th>@shareable</th><td>🟢</td></tr><tr><th>@tag</th><td>🟢</td></tr><tr><th>@override</th><td>🟢</td></tr><tr><th>@inaccessible</th><td>🟢</td></tr></table></td></tr>
115-
<tr><td><a href="https://graphene-python.org/">Graphene</a></td><td><table><tr><th>_service</th><td>🟢</td></tr><tr><th>@key (single)</th><td>🟢</td></tr><tr><th>@key (multi)</th><td>🟢</td></tr><tr><th>@key (composite)</th><td>🔲</td></tr><tr><th>repeatable @key</th><td>🔲</td></tr><tr><th>@requires</th><td>🟢</td></tr><tr><th>@provides</th><td>🔲</td></tr><tr><th>federated tracing</th><td>🔲</td></tr></table></td><td><table><tr><th>@link</th><td>❌</td></tr><tr><th>@shareable</th><td>🔲</td></tr><tr><th>@tag</th><td>🔲</td></tr><tr><th>@override</th><td>🔲</td></tr><tr><th>@inaccessible</th><td>🔲</td></tr></table></td></tr>
115+
<tr><td><a href="https://graphene-python.org/">Graphene</a></td><td><table><tr><th>_service</th><td>🟢</td></tr><tr><th>@key (single)</th><td>🟢</td></tr><tr><th>@key (multi)</th><td>🔲</td></tr><tr><th>@key (composite)</th><td>🔲</td></tr><tr><th>repeatable @key</th><td>🔲</td></tr><tr><th>@requires</th><td>🟢</td></tr><tr><th>@provides</th><td>🟢</td></tr><tr><th>federated tracing</th><td>🔲</td></tr></table></td><td><table><tr><th>@link</th><td>❌</td></tr><tr><th>@shareable</th><td>🔲</td></tr><tr><th>@tag</th><td>🔲</td></tr><tr><th>@override</th><td>🔲</td></tr><tr><th>@inaccessible</th><td>🔲</td></tr></table></td></tr>
116116
<tr><td><a href="https://strawberry.rocks">Strawberry</a></td><td><table><tr><th>_service</th><td>🟢</td></tr><tr><th>@key (single)</th><td>🟢</td></tr><tr><th>@key (multi)</th><td>🟢</td></tr><tr><th>@key (composite)</th><td>🟢</td></tr><tr><th>repeatable @key</th><td>🟢</td></tr><tr><th>@requires</th><td>🟢</td></tr><tr><th>@provides</th><td>🟢</td></tr><tr><th>federated tracing</th><td>🔲</td></tr></table></td><td><table><tr><th>@link</th><td>🟢</td></tr><tr><th>@shareable</th><td>🟢</td></tr><tr><th>@tag</th><td>🟢</td></tr><tr><th>@override</th><td>🟢</td></tr><tr><th>@inaccessible</th><td>🟢</td></tr></table></td></tr>
117117
</tbody>
118118
</table>

implementations/graphene/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
FROM python:3.9-alpine
1+
FROM python:3.10-alpine
22
WORKDIR /web
33
COPY requirements.txt ./
4-
RUN pip install -r requirements.txt
4+
# adding git -> temp workaround for graphene-federation not available in pypi
5+
RUN apk add --no-cache git \
6+
&& pip install -r requirements.txt
57
COPY server.py ./
68
EXPOSE 4001
79
CMD python server.py
Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
1-
aniso8601==7.0.0
2-
certifi==2021.5.30
3-
click==8.0.1
1+
aniso8601==9.0.1
2+
click==7.1.2
43
Flask==2.0.1
5-
Flask-GraphQL==2.0.1
6-
graphene==2.1.9
7-
graphene-federation==0.1.0
8-
graphql-core==2.3.2
9-
graphql-relay==2.0.1
10-
graphql-server-core==1.2.0
11-
itsdangerous==2.0.1
12-
Jinja2==3.0.1
4+
graphene==3.1.1
5+
# graphene-federation==3.0.0
6+
graphene-federation @ git+https://github.com/graphql-python/[email protected]
7+
graphql-core==3.2.3
8+
graphql-relay==3.2.0
9+
graphql-server==3.0.0b5
10+
itsdangerous==2.1.2
11+
Jinja2==3.1.2
1312
MarkupSafe==2.0.1
14-
pip==21.2.3
15-
promise==2.3
16-
Rx==1.6.1
17-
setuptools==52.0.0
18-
six==1.16.0
13+
typing_extensions==4.3.0
1914
Werkzeug==2.0.1
20-
wheel==0.37.0

implementations/graphene/server.py

Lines changed: 142 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,137 @@
11
from flask import Flask
2-
from flask_graphql import GraphQLView
3-
from graphene import ObjectType, Field, Float, ID, Int, String
4-
from graphene_federation import build_schema, extend, external, key, provides
2+
from graphql_server.flask import GraphQLView
3+
from graphene import ObjectType, Field, Float, ID, Int, String, List, NonNull
4+
from graphene_federation import build_schema, extend, external, key, provides, requires
5+
6+
# -------- data --------
7+
8+
dimension = {
9+
"size": "small",
10+
"weight": 1,
11+
"unit": "kg",
12+
}
13+
14+
user = {
15+
"email": "[email protected]",
16+
"name": "Jane Smith",
17+
"total_products_created": 1337,
18+
}
19+
20+
deprecated_product = {
21+
"sku": "apollo-federation-v1",
22+
"package": "@apollo/federation-v1",
23+
"reason": "Migrate to Federation V2",
24+
"created_by": user,
25+
}
26+
27+
products_research = [
28+
{
29+
"study": {
30+
"case_number": "1234",
31+
"description": "Federation Study",
32+
},
33+
"outcome": None,
34+
},
35+
{
36+
"study": {
37+
"case_number": "1235",
38+
"description": "Studio Study",
39+
},
40+
"outcome": None,
41+
},
42+
]
43+
44+
products = [
45+
{
46+
"id": "apollo-federation",
47+
"sku": "federation",
48+
"package": "@apollo/federation",
49+
"variation": {"id": "OSS"},
50+
"dimensions": dimension,
51+
"research": [products_research[0]],
52+
"created_by": user,
53+
"notes": None,
54+
},
55+
{
56+
"id": "apollo-studio",
57+
"sku": "studio",
58+
"package": "",
59+
"variation": {"id": "platform"},
60+
"dimensions": dimension,
61+
"research": [products_research[1]],
62+
"created_by": user,
63+
"notes": None,
64+
},
65+
]
66+
67+
68+
# -------- types --------
569

670

771
@extend(fields='email')
872
class User(ObjectType):
73+
average_products_created_per_year = requires(field=Int(), fields=["total_products_created", "years_of_employment"])
974
email = external(ID(required=True))
10-
totalProductsCreated = external(Int())
75+
name = String()
76+
total_products_created = external(Int())
77+
years_of_employment = external(Int(required=True))
78+
79+
def resolve_average_products_created_per_year(self, info, *args, **kwargs):
80+
if self.total_products_created and self.years_of_employment:
81+
return round(self.total_products_created / self.years_of_employment)
82+
83+
return None
84+
85+
def __resolve_reference(self, info, **kwargs):
86+
if user['email'] == self.email:
87+
total_products = user['total_products_created']
88+
if self.total_products_created:
89+
total_products = self.total_products_created
90+
91+
return User(
92+
email=self.email,
93+
name=user['name'],
94+
total_products_created=total_products,
95+
years_of_employment=self.years_of_employment
96+
)
1197

12-
def resolve_total_products_created(parent, info, **kwargs):
13-
return 1337
98+
return self
1499

15100

16101
class ProductVariation(ObjectType):
17-
id = ID(required=True)
102+
id = ID(required=True)
18103

19104

20105
class ProductDimension(ObjectType):
21106
size = String()
22107
weight = Float()
108+
unit = String()
109+
110+
111+
class CaseStudy(ObjectType):
112+
case_number = ID(required=True)
113+
description = String()
114+
115+
116+
# @key(fields="study { caseNumber }")
117+
class ProductResearch(ObjectType):
118+
study = Field(CaseStudy, required=True)
119+
outcome = String()
23120

24121

25122
@key(fields='id')
26-
@key(fields='sku package')
27-
@key(fields='sku variation { id }')
123+
# @key(fields='sku package')
124+
# @key(fields='sku variation { id }')
28125
@provides
29126
class Product(ObjectType):
30127
id = ID(required=True)
31128
sku = String()
32129
package = String()
33130
variation = Field(ProductVariation)
34131
dimensions = Field(ProductDimension)
35-
createdBy = provides(Field(User), fields='totalProductsCreated')
36-
37-
def resolve_variation(self, info, **kwargs):
38-
print(self)
39-
return get_product_variation(self)
40-
41-
def resolve_dimensions(self, info, **kwargs):
42-
return {'size': 'small', 'weight': 1.0}
43-
44-
def resolve_created_by(self, info, **kwargs):
45-
{'email': '[email protected]', 'totalProductsCreated': 1337}
132+
created_by = provides(Field(User), fields='total_products_created')
133+
notes = String()
134+
research = List(NonNull(ProductResearch), required=True)
46135

47136
def __resolve_reference(self, info, **kwargs):
48137
if self.id:
@@ -53,60 +142,60 @@ def __resolve_reference(self, info, **kwargs):
53142
return Product(**get_product_by_sku_and_variation(self.sku, self.variation))
54143

55144

145+
# @key(fields="sku package")
146+
class DeprecatedProduct(ObjectType):
147+
sku = String(required=True)
148+
package = String(required=True)
149+
reason = String()
150+
createdBy = Field(User)
151+
152+
56153
class Query(ObjectType):
57154
product = Field(Product, id=ID(required=True))
155+
deprecated_product = Field(DeprecatedProduct, sku=String(required=True), package=String(required=True),
156+
deprecation_reason="Use product query instead")
58157

59158
def resolve_product(self, info, id):
60159
return get_product_by_id(id)
61160

62-
63-
schema= build_schema(Query, types=[Product])
64-
app = Flask(__name__)
65-
app.add_url_rule('/', view_func=GraphQLView.as_view(
66-
'graphql',
67-
schema=schema
68-
))
69-
70-
products = [
71-
{
72-
"id": "apollo-federation",
73-
"sku": "federation",
74-
"package": "@apollo/federation",
75-
"variation": "OSS",
76-
},
77-
{
78-
"id": "apollo-studio",
79-
"sku": "studio",
80-
"package": "",
81-
"variation": "platform",
82-
},
83-
]
161+
def resolve_deprecated_product(self, info, sku, package):
162+
return get_deprecated_product_by_sku_and_package(sku, package)
84163

85164

86-
def get_product_variation(reference):
87-
if isinstance(reference, Product) and reference.variation:
88-
return {'id': reference.variation}
89-
elif reference["variation"]:
90-
return {'id': reference["variation"]}
91-
variation = next((product for product in products if product['id']
92-
== reference.id), None)
93-
return {'id': variation}
165+
# -------- resolvers --------
94166

95167

96168
def get_product_by_id(id):
97169
return next((product for product in products if product['id']
98-
== id), None)
170+
== id), None)
99171

100172

101173
def get_product_by_sku_and_package(sku, package):
102174
return next((product for product in products if product['sku']
103-
== sku and product['package'] == package), None)
175+
== sku and product['package'] == package), None)
104176

105177

106178
def get_product_by_sku_and_variation(sku, variation):
107179
return next((product for product in products if product['sku']
108-
== sku and product['variation'] == variation), None)
180+
== sku and product['variation'] == variation), None)
181+
182+
183+
def get_deprecated_product_by_sku_and_package(sku, package):
184+
if deprecated_product['sku'] == sku and deprecated_product['package'] == package:
185+
return deprecated_product
186+
187+
return None
109188

110189

190+
# -------- server --------
191+
192+
193+
schema = build_schema(query=Query)
194+
app = Flask(__name__)
195+
app.add_url_rule('/', view_func=GraphQLView.as_view(
196+
'graphql',
197+
schema=schema
198+
))
199+
111200
if __name__ == '__main__':
112201
app.run(host='0.0.0.0', port=4001)

0 commit comments

Comments
 (0)