Skip to content

Commit 6754510

Browse files
committed
Add command to export the graphql schema
Add support for fragment within the same file
1 parent bc6bc7f commit 6754510

File tree

8 files changed

+78
-48
lines changed

8 files changed

+78
-48
lines changed

infrahub_sdk/ctl/graphql.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import ast
34
from collections import defaultdict
45
from pathlib import Path
56

@@ -8,8 +9,8 @@
89
from ariadne_codegen.plugins.explorer import get_plugins_types
910
from ariadne_codegen.plugins.manager import PluginManager
1011
from ariadne_codegen.schema import (
11-
filter_operations_definitions,
1212
filter_fragments_definitions,
13+
filter_operations_definitions,
1314
get_graphql_schema_from_path,
1415
)
1516
from ariadne_codegen.settings import ClientSettings, CommentsStrategy
@@ -18,7 +19,9 @@
1819
from rich.console import Console
1920

2021
from ..async_typer import AsyncTyper
22+
from ..ctl.client import initialize_client
2123
from ..ctl.utils import catch_exception
24+
from ..graphql.utils import insert_fragments_inline, remove_fragment_import
2225
from .parameters import CONFIG_PARAM
2326

2427
app = AsyncTyper()
@@ -70,9 +73,13 @@ def get_graphql_query(queries_path: Path, schema: GraphQLSchema) -> tuple[Defini
7073
return queries_ast.definitions
7174

7275

73-
def generate_result_types(directory: Path, package: PackageGenerator) -> None:
76+
def generate_result_types(directory: Path, package: PackageGenerator, fragment: ast.Module) -> None:
7477
for file_name, module in package._result_types_files.items():
7578
file_path = directory / file_name
79+
80+
insert_fragments_inline(module, fragment)
81+
remove_fragment_import(module)
82+
7683
code = package._add_comments_to_code(ast_to_str(module), package.queries_source)
7784
if package.plugin_manager:
7885
code = package.plugin_manager.generate_result_types_code(code)
@@ -87,6 +94,20 @@ def callback() -> None:
8794
"""
8895

8996

97+
@app.command()
98+
@catch_exception(console=console)
99+
async def export_schema(
100+
destination: Path = typer.Option("schema.graphql", help="Path to the GraphQL schema file."),
101+
_: str = CONFIG_PARAM,
102+
) -> None:
103+
"""Export the GraphQL schema to a file."""
104+
105+
client = initialize_client()
106+
response = await client._get(url=f"{client.address}/schema.graphql")
107+
destination.write_text(response.text)
108+
console.print(f"[green]Schema exported to {destination}")
109+
110+
90111
@app.command()
91112
@catch_exception(console=console)
92113
async def generate_return_types(
@@ -113,14 +134,13 @@ async def generate_return_types(
113134
for gql_file in gql_files:
114135
gql_per_directory[gql_file.parent].append(gql_file)
115136

116-
117137
# Generate the Pydantic Models for the GraphQL queries
118138
for directory, gql_files in gql_per_directory.items():
119139
for gql_file in gql_files:
120140
try:
121141
definitions = get_graphql_query(queries_path=gql_file, schema=graphql_schema)
122-
except ValueError as e:
123-
print(f"Error generating result types for {gql_file}: {e}")
142+
except ValueError as exc:
143+
print(f"Error generating result types for {gql_file}: {exc}")
124144
continue
125145
queries = filter_operations_definitions(definitions)
126146
fragments = filter_fragments_definitions(definitions)
@@ -137,12 +157,12 @@ async def generate_return_types(
137157
plugin_manager=plugin_manager,
138158
)
139159

140-
141160
for query_operation in queries:
142161
package_generator.add_operation(query_operation)
143162

144-
package_generator._generate_fragments()
145-
generate_result_types(directory=directory, package=package_generator)
163+
module_fragment = package_generator.fragments_generator.generate()
164+
165+
generate_result_types(directory=directory, package=package_generator, fragment=module_fragment)
146166

147167
for file_name in package_generator._result_types_files.keys():
148168
print(f"Generated {file_name} in {directory}")

infrahub_sdk/graphql/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
from .constants import VARIABLE_TYPE_MAPPING
22
from .query import Mutation, Query
33
from .renderers import render_input_block, render_query_block, render_variables_to_string
4-
from .return_type import GraphQLReturnTypeModel
54

65
__all__ = [
76
"VARIABLE_TYPE_MAPPING",
8-
"GraphQLReturnTypeModel",
97
"Mutation",
108
"Query",
119
"render_input_block",

infrahub_sdk/graphql/return_type.py

Lines changed: 0 additions & 11 deletions
This file was deleted.

infrahub_sdk/graphql/utils.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import ast
2+
3+
4+
def get_class_def_index(module: ast.Module) -> int:
5+
"""Get the index of the first class definition in the module.
6+
It's useful to insert other classes before the first class definition."""
7+
for idx, item in enumerate(module.body):
8+
if isinstance(item, ast.ClassDef):
9+
return idx
10+
return -1
11+
12+
13+
def insert_fragments_inline(module: ast.Module, fragment: ast.Module) -> ast.Module:
14+
"""Insert the Pydantic classes for the fragments inline into the module."""
15+
module_class_def_index = get_class_def_index(module)
16+
17+
fragment_classes: list[ast.ClassDef] = [item for item in fragment.body if isinstance(item, ast.ClassDef)]
18+
for idx, item in enumerate(fragment_classes):
19+
module.body.insert(module_class_def_index + idx, item)
20+
21+
return module
22+
23+
24+
def remove_fragment_import(module: ast.Module) -> ast.Module:
25+
"""Remove the fragment import from the module."""
26+
for item in module.body:
27+
if isinstance(item, ast.ImportFrom) and item.module == "fragments":
28+
module.body.remove(item)
29+
return module
30+
return module

tests/fixtures/unit/test_graphql_plugin/python01.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,34 @@
22

33
from typing import Optional
44

5-
from pydantic import Field
5+
from pydantic import Field, BaseModel
66

7-
from infrahub_sdk.graphql import GraphQLReturnTypeModel
87

9-
10-
class CreateDevice(GraphQLReturnTypeModel):
8+
class CreateDevice(BaseModel):
119
infra_device_upsert: Optional[CreateDeviceInfraDeviceUpsert] = Field(alias="InfraDeviceUpsert")
1210

1311

14-
class CreateDeviceInfraDeviceUpsert(GraphQLReturnTypeModel):
12+
class CreateDeviceInfraDeviceUpsert(BaseModel):
1513
ok: Optional[bool]
1614
object: Optional[CreateDeviceInfraDeviceUpsertObject]
1715

1816

19-
class CreateDeviceInfraDeviceUpsertObject(GraphQLReturnTypeModel):
17+
class CreateDeviceInfraDeviceUpsertObject(BaseModel):
2018
id: str
2119
name: Optional[CreateDeviceInfraDeviceUpsertObjectName]
2220
description: Optional[CreateDeviceInfraDeviceUpsertObjectDescription]
2321
status: Optional[CreateDeviceInfraDeviceUpsertObjectStatus]
2422

2523

26-
class CreateDeviceInfraDeviceUpsertObjectName(GraphQLReturnTypeModel):
24+
class CreateDeviceInfraDeviceUpsertObjectName(BaseModel):
2725
value: Optional[str]
2826

2927

30-
class CreateDeviceInfraDeviceUpsertObjectDescription(GraphQLReturnTypeModel):
28+
class CreateDeviceInfraDeviceUpsertObjectDescription(BaseModel):
3129
value: Optional[str]
3230

3331

34-
class CreateDeviceInfraDeviceUpsertObjectStatus(GraphQLReturnTypeModel):
32+
class CreateDeviceInfraDeviceUpsertObjectStatus(BaseModel):
3533
value: Optional[str]
3634

3735

tests/fixtures/unit/test_graphql_plugin/python01_after_annotation.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,34 @@
22

33
from typing import Optional
44

5-
from pydantic import Field
5+
from pydantic import Field, BaseModel
66

7-
from infrahub_sdk.graphql import GraphQLReturnTypeModel
87

9-
10-
class CreateDevice(GraphQLReturnTypeModel):
8+
class CreateDevice(BaseModel):
119
infra_device_upsert: Optional[CreateDeviceInfraDeviceUpsert] = Field(alias="InfraDeviceUpsert")
1210

1311

14-
class CreateDeviceInfraDeviceUpsert(GraphQLReturnTypeModel):
12+
class CreateDeviceInfraDeviceUpsert(BaseModel):
1513
ok: Optional[bool]
1614
object: Optional[CreateDeviceInfraDeviceUpsertObject]
1715

1816

19-
class CreateDeviceInfraDeviceUpsertObject(GraphQLReturnTypeModel):
17+
class CreateDeviceInfraDeviceUpsertObject(BaseModel):
2018
id: str
2119
name: Optional[CreateDeviceInfraDeviceUpsertObjectName]
2220
description: Optional[CreateDeviceInfraDeviceUpsertObjectDescription]
2321
status: Optional[CreateDeviceInfraDeviceUpsertObjectStatus]
2422

2523

26-
class CreateDeviceInfraDeviceUpsertObjectName(GraphQLReturnTypeModel):
24+
class CreateDeviceInfraDeviceUpsertObjectName(BaseModel):
2725
value: Optional[str]
2826

2927

30-
class CreateDeviceInfraDeviceUpsertObjectDescription(GraphQLReturnTypeModel):
28+
class CreateDeviceInfraDeviceUpsertObjectDescription(BaseModel):
3129
value: Optional[str]
3230

3331

34-
class CreateDeviceInfraDeviceUpsertObjectStatus(GraphQLReturnTypeModel):
32+
class CreateDeviceInfraDeviceUpsertObjectStatus(BaseModel):
3533
value: Optional[str]
3634

3735

tests/fixtures/unit/test_graphql_plugin/python02.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
from typing import Optional
22

3-
from pydantic import Field
3+
from pydantic import Field, BaseModel
44

5-
from infrahub_sdk.graphql import GraphQLReturnTypeModel
65

7-
8-
class CreateDevice(GraphQLReturnTypeModel):
6+
class CreateDevice(BaseModel):
97
infra_device_upsert: Optional["CreateDeviceInfraDeviceUpsert"] = Field(alias="InfraDeviceUpsert")
108

119

12-
class CreateDeviceInfraDeviceUpsert(GraphQLReturnTypeModel):
10+
class CreateDeviceInfraDeviceUpsert(BaseModel):
1311
ok: Optional[bool]
1412
object: Optional[dict]
1513

tests/fixtures/unit/test_graphql_plugin/python02_after_annotation.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22

33
from typing import Optional
44

5-
from pydantic import Field
5+
from pydantic import Field, BaseModel
66

7-
from infrahub_sdk.graphql import GraphQLReturnTypeModel
87

98

10-
class CreateDevice(GraphQLReturnTypeModel):
9+
class CreateDevice(BaseModel):
1110
infra_device_upsert: Optional["CreateDeviceInfraDeviceUpsert"] = Field(alias="InfraDeviceUpsert")
1211

1312

14-
class CreateDeviceInfraDeviceUpsert(GraphQLReturnTypeModel):
13+
class CreateDeviceInfraDeviceUpsert(BaseModel):
1514
ok: Optional[bool]
1615
object: Optional[dict]
1716

0 commit comments

Comments
 (0)