-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcli.py
More file actions
272 lines (219 loc) · 7.82 KB
/
Copy pathcli.py
File metadata and controls
272 lines (219 loc) · 7.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
"""CLI for inspecting strawberry-orm test schemas via GraphiQL."""
from __future__ import annotations
import contextlib
from enum import Enum
from typing import Any
import typer
app = typer.Typer(help="strawberry-orm schema inspector")
class Backend(str, Enum):
django = "django"
sqlalchemy = "sqlalchemy"
SCHEMA_NAMES = ("main", "self_model", "get_queryset", "multi_type")
# ---------------------------------------------------------------------------
# Django helpers
# ---------------------------------------------------------------------------
def _configure_django() -> None:
import django
from django.conf import settings
if not settings.configured:
settings.configure(
DATABASES={
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
}
},
INSTALLED_APPS=[
"django.contrib.contenttypes",
"django.contrib.auth",
"tests.backends.django.app.TestAppConfig",
],
DEFAULT_AUTO_FIELD="django.db.models.BigAutoField",
SECRET_KEY="cli-secret-key",
USE_TZ=False,
)
django.setup()
def _seed_django() -> None:
from django.db import connection
from tests.backends.django.models import (
Comment as DjComment,
)
from tests.backends.django.models import (
Post as DjPost,
)
from tests.backends.django.models import (
Tag as DjTag,
)
from tests.backends.django.models import (
User as DjUser,
)
with connection.schema_editor() as editor:
for model in (DjUser, DjTag, DjPost, DjComment):
with contextlib.suppress(Exception):
editor.create_model(model)
alice = DjUser.objects.create(id=1, name="Alice", email="alice@example.com")
bob = DjUser.objects.create(id=2, name="Bob", email="bob@example.com")
charlie = DjUser.objects.create(id=3, name="Charlie", email="charlie@test.org")
py = DjTag.objects.create(id=1, name="python")
gql = DjTag.objects.create(id=2, name="graphql")
rs = DjTag.objects.create(id=3, name="rust")
p1 = DjPost.objects.create(
id=1, title="Hello World", body="First post", is_published=True, author=alice
)
p2 = DjPost.objects.create(
id=2,
title="GraphQL Guide",
body="Learn GraphQL",
is_published=True,
author=alice,
)
DjPost.objects.create(
id=3,
title="Draft Post",
body="Not published yet",
is_published=False,
author=bob,
)
p4 = DjPost.objects.create(
id=4,
title="Rust Adventures",
body="Systems programming",
is_published=True,
author=charlie,
)
p1.tags.add(py)
p2.tags.add(py, gql)
p4.tags.add(rs)
DjComment.objects.create(id=1, body="Nice post!", post=p1, author=bob)
DjComment.objects.create(id=2, body="Thanks!", post=p1, author=alice, parent_id=1)
DjComment.objects.create(id=3, body="Great guide", post=p2, author=charlie)
# ---------------------------------------------------------------------------
# SQLAlchemy helpers
# ---------------------------------------------------------------------------
def _seed_sqlalchemy() -> Any:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from tests.backends.sqlalchemy.models import Base, Comment, Post, Tag, User
engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
session = sessionmaker(bind=engine)()
alice = User(id=1, name="Alice", email="alice@example.com")
bob = User(id=2, name="Bob", email="bob@example.com")
charlie = User(id=3, name="Charlie", email="charlie@test.org")
session.add_all([alice, bob, charlie])
session.flush()
py, gql, rs = (
Tag(id=1, name="python"),
Tag(id=2, name="graphql"),
Tag(id=3, name="rust"),
)
session.add_all([py, gql, rs])
session.flush()
p1 = Post(
id=1, title="Hello World", body="First post", is_published=True, author_id=1
)
p2 = Post(
id=2,
title="GraphQL Guide",
body="Learn GraphQL",
is_published=True,
author_id=1,
)
p3 = Post(
id=3,
title="Draft Post",
body="Not published yet",
is_published=False,
author_id=2,
)
p4 = Post(
id=4,
title="Rust Adventures",
body="Systems programming",
is_published=True,
author_id=3,
)
session.add_all([p1, p2, p3, p4])
session.flush()
p1.tags.append(py)
p2.tags.extend([py, gql])
p4.tags.append(rs)
session.flush()
session.add_all(
[
Comment(id=1, body="Nice post!", post_id=1, author_id=2),
Comment(id=2, body="Thanks!", post_id=1, author_id=1, parent_id=1),
Comment(id=3, body="Great guide", post_id=2, author_id=3),
]
)
session.commit()
return session
# ---------------------------------------------------------------------------
# Schema loading
# ---------------------------------------------------------------------------
def _load_schema(backend: Backend, name: str) -> Any:
# Use the node-mutation demo schema for the main example so GraphiQL exposes
# create_node/update_node alongside the standard query surface.
attr = "node_mutation_schema" if name == "main" else f"{name}_schema"
if backend == Backend.django:
_configure_django()
from tests.backends.django import fixtures as mod
else:
from tests.backends.sqlalchemy import fixtures as mod
schema = getattr(mod, attr, None)
if schema is None:
available = sorted(
a.removesuffix("_schema") for a in dir(mod) if a.endswith("_schema")
)
typer.echo(
f"Unknown schema '{name}'. Available: {', '.join(available)}", err=True
)
raise typer.Exit(1)
return schema
# ---------------------------------------------------------------------------
# Command
# ---------------------------------------------------------------------------
@app.command()
def show(
schema_name: str | None = typer.Argument(
None,
help=f"Schema to display. Choices: {', '.join(SCHEMA_NAMES)}. "
"Omit to list available schemas.",
),
backend: Backend = typer.Option(
Backend.django, "--backend", "-b", help="ORM backend."
),
port: int = typer.Option(8420, "--port", "-p", help="Port to serve on."),
no_seed: bool = typer.Option(False, "--no-seed", help="Skip seeding demo data."),
) -> None:
"""Open GraphiQL for a schema in the browser."""
if schema_name is None:
typer.echo(f"Available schemas ({backend.value}):")
for name in SCHEMA_NAMES:
typer.echo(f" {name}")
raise typer.Exit()
import uvicorn
from strawberry.asgi import GraphQL
schema = _load_schema(backend, schema_name)
if backend == Backend.sqlalchemy:
if not no_seed:
session = _seed_sqlalchemy()
else:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from tests.backends.sqlalchemy.models import Base
engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
session = sessionmaker(bind=engine)()
class App(GraphQL):
async def get_context(self, request, response=None):
return {"session": session}
graphql_app = App(schema)
else:
if not no_seed:
_seed_django()
graphql_app = GraphQL(schema)
typer.echo(f"Serving {backend.value}/{schema_name} on http://localhost:{port}")
uvicorn.run(graphql_app, host="127.0.0.1", port=port, log_level="warning")
if __name__ == "__main__":
app()