Skip to content

Commit

Permalink
Implement cli
Browse files Browse the repository at this point in the history
  • Loading branch information
patrick91 committed Feb 23, 2022
1 parent bb325ce commit a62454a
Show file tree
Hide file tree
Showing 13 changed files with 87 additions and 29 deletions.
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ per-file-ignores =
tests/test_forward_references.py:E800
tests/schema/test_resolvers.py:E800
tests/types/test_string_annotations.py:E800
tests/codegen/test_query_codegen.py:E501
tests/codegen/test_query_codegen_typescript.py:E501
tests/federation/test_printer.py:E800
2 changes: 2 additions & 0 deletions strawberry/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import click

from .commands.codegen import codegen as cmd_codegen
from .commands.export_schema import export_schema as cmd_export_schema
from .commands.server import server as cmd_server

Expand All @@ -11,3 +12,4 @@ def run(): # pragma: no cover

run.add_command(cmd_server)
run.add_command(cmd_export_schema)
run.add_command(cmd_codegen)
30 changes: 30 additions & 0 deletions strawberry/cli/commands/codegen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import click

from strawberry.cli.utils import load_schema
from strawberry.codegen import QueryCodegen
from strawberry.codegen.plugins.python import PythonPlugin


@click.command(short_help="Generate code from a query")
@click.argument("schema", type=str)
@click.argument("query", type=str)
@click.option(
"--app-dir",
default=".",
type=str,
show_default=True,
help=(
"Look for the module in the specified directory, by adding this to the "
"PYTHONPATH. Defaults to the current working directory. "
"Works the same as `--app-dir` in uvicorn."
),
)
def codegen(schema: str, query: str, app_dir: str):
schema_symbol = load_schema(schema, app_dir)

code_generator = QueryCodegen(schema_symbol, plugins=[PythonPlugin()])

with open(query) as f:
code = code_generator.codegen(f.read())

print(code)
17 changes: 3 additions & 14 deletions strawberry/cli/commands/export_schema.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import sys

import click

from strawberry import Schema
from strawberry.cli.utils import load_schema
from strawberry.printer import print_schema
from strawberry.utils.importer import import_module_symbol


@click.command(short_help="Exports the schema")
Expand All @@ -20,15 +17,7 @@
"Works the same as `--app-dir` in uvicorn."
),
)
def export_schema(schema: str, app_dir):
sys.path.insert(0, app_dir)
def export_schema(schema: str, app_dir: str):
schema_symbol = load_schema(schema, app_dir)

try:
schema_symbol = import_module_symbol(schema, default_symbol_name="schema")
except (ImportError, AttributeError) as exc:
message = str(exc)
raise click.BadArgumentUsage(message)
if not isinstance(schema_symbol, Schema):
message = "The `schema` must be an instance of strawberry.Schema"
raise click.BadArgumentUsage(message)
print(print_schema(schema_symbol))
13 changes: 2 additions & 11 deletions strawberry/cli/commands/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@

import click

from strawberry import Schema
from strawberry.cli.constants import DEBUG_SERVER_SCHEMA_ENV_VAR_KEY
from strawberry.utils.importer import import_module_symbol
from strawberry.cli.utils import load_schema


@click.command("server", short_help="Starts debug server")
Expand Down Expand Up @@ -42,15 +41,7 @@ def server(schema, host, port, log_level, app_dir):
)
raise click.ClickException(message)

try:
schema_symbol = import_module_symbol(schema, default_symbol_name="schema")
except (ImportError, AttributeError) as exc:
message = str(exc)
raise click.BadArgumentUsage(message)

if not isinstance(schema_symbol, Schema):
message = "The `schema` must be an instance of strawberry.Schema"
raise click.BadArgumentUsage(message)
load_schema(schema, app_dir=app_dir)

os.environ[DEBUG_SERVER_SCHEMA_ENV_VAR_KEY] = schema
app = "strawberry.cli.debug_server:app"
Expand Down
23 changes: 23 additions & 0 deletions strawberry/cli/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import sys

import click

from strawberry import Schema
from strawberry.utils.importer import import_module_symbol


def load_schema(schema: str, app_dir: str) -> Schema:
sys.path.insert(0, app_dir)

try:
schema_symbol = import_module_symbol(schema, default_symbol_name="schema")
except (ImportError, AttributeError) as exc:
message = str(exc)

raise click.BadArgumentUsage(message)

if not isinstance(schema_symbol, Schema):
message = "The `schema` must be an instance of strawberry.Schema"
raise click.BadArgumentUsage(message)

return schema_symbol
File renamed without changes.
9 changes: 5 additions & 4 deletions strawberry/codegen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,6 @@ def _field_from_selection(

return GraphQLField(field.name, field_type)

# def _get_field_with_subselection(selection: FieldNode) -> GraphQLField:

def _field_from_selection_set(
self, selection: FieldNode, class_name: str, parent_type: TypeDefinition
) -> GraphQLField:
Expand Down Expand Up @@ -212,8 +210,11 @@ def _field_from_selection_set(
# TODO: this is ugly :D

while isinstance(selected_field_type, StrawberryContainer):
# TODO: this doesn't support lists :'D
field_type = GraphQLOptional(field_type)
wrap = {StrawberryList: GraphQLList, StrawberryOptional: GraphQLOptional}[
type(selected_field_type)
] # type: ignore

field_type = wrap(field_type)

selected_field_type = selected_field_type.of_type

Expand Down
1 change: 1 addition & 0 deletions strawberry/codegen/plugins/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

class PythonPlugin(CodegenPlugin):
SCALARS_TO_PYTHON_TYPES = {
"ID": "str",
"Int": "int",
"String": "str",
}
Expand Down
1 change: 1 addition & 0 deletions strawberry/codegen/plugins/typescript.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

class TypeScriptPlugin(CodegenPlugin):
SCALARS_TO_TS_TYPE = {
"ID": "string",
"Int": "number",
"String": "string",
str: "string",
Expand Down
2 changes: 2 additions & 0 deletions tests/codegen/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ class Query:
integer: int
another_integer: int
optional_int: Optional[int]
list_of_int: List[int]
list_of_optional_int: List[Optional[int]]
person: Person
optional_person: Optional[Person]
list_of_people: List[Person]
enum: Color
json: JSON
union: PersonOrAnimal
Expand Down
13 changes: 13 additions & 0 deletions tests/codegen/test_query_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ def test_codegen_basic(schema):

input_query = """
query OperationName {
id
integer
anotherInteger
}
"""

expected_output = """
class OperationNameResult:
id: str
integer: int
another_integer: int
"""
Expand All @@ -43,6 +45,7 @@ def test_list_and_optional(schema):
input_query = """
query OperationName {
optionalInt
listOfInt
listOfOptionalInt
}
"""
Expand All @@ -52,6 +55,7 @@ def test_list_and_optional(schema):
class OperationNameResult:
optional_int: Optional[int]
list_of_int: List[int]
list_of_optional_int: List[Optional[int]]
"""

Expand All @@ -68,15 +72,24 @@ def test_multiple_types(schema):
person {
name
}
listOfPeople {
name
}
}
"""

expected_output = """
from typing import List
class OperationNameResultPerson:
name: str
class OperationNameResultListOfPeople:
name: str
class OperationNameResult:
person: OperationNameResultPerson
list_of_people: List[OperationNameResultListOfPeople]
"""

result = generator.codegen(input_query)
Expand Down
4 changes: 4 additions & 0 deletions tests/codegen/test_query_codegen_typescript.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ def test_codegen_basic(schema):

input_query = """
query OperationName {
id
integer
anotherInteger
}
"""

expected_output = """
type OperationNameResult = {
id: string
integer: number
another_integer: number
}
Expand All @@ -32,13 +34,15 @@ def test_list_and_optional(schema):
input_query = """
query OperationName {
optionalInt
listOfInt
listOfOptionalInt
}
"""

expected_output = """
type OperationNameResult = {
optional_int: number | undefined
list_of_int: number[]
list_of_optional_int: (number | undefined)[]
}
"""
Expand Down

0 comments on commit a62454a

Please sign in to comment.