Skip to content

Commit c5d099e

Browse files
committed
user-specified validation rule skips
This feature allows for the user to specify which validation rules they want to skip. Currently we only support 2 rules (the implicit no unused fragments rule and the UniqueFragmentNames rule).
1 parent 11bfe35 commit c5d099e

File tree

4 files changed

+117
-4
lines changed

4 files changed

+117
-4
lines changed

ariadne_codegen/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
get_graphql_schema_from_path,
2020
get_graphql_schema_from_url,
2121
)
22-
from .settings import Strategy
22+
from .settings import Strategy, get_validation_rule
2323

2424

2525
@click.command() # type: ignore
@@ -64,7 +64,7 @@ def client(config_dict):
6464
fragments = []
6565
queries = []
6666
if settings.queries_path:
67-
definitions = get_graphql_queries(settings.queries_path, schema)
67+
definitions = get_graphql_queries(settings.queries_path, schema, [get_validation_rule(e) for e in settings.skip_validation_rules])
6868
queries = filter_operations_definitions(definitions)
6969
fragments = filter_fragments_definitions(definitions)
7070

ariadne_codegen/schema.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from pathlib import Path
22
from typing import Dict, Generator, List, Optional, Tuple, cast
3+
from typing_extensions import Any, Sequence
34

45
import httpx
56
from graphql import (
@@ -14,6 +15,7 @@
1415
IntrospectionQuery,
1516
NoUnusedFragmentsRule,
1617
OperationDefinitionNode,
18+
UniqueFragmentNamesRule,
1719
build_ast_schema,
1820
build_client_schema,
1921
get_introspection_query,
@@ -45,15 +47,15 @@ def filter_fragments_definitions(
4547

4648

4749
def get_graphql_queries(
48-
queries_path: str, schema: GraphQLSchema
50+
queries_path: str, schema: GraphQLSchema, skip_rules: Sequence[Any] = (NoUnusedFragmentsRule,)
4951
) -> Tuple[DefinitionNode, ...]:
5052
"""Get graphql queries definitions build from provided path."""
5153
queries_str = load_graphql_files_from_path(Path(queries_path))
5254
queries_ast = parse(queries_str)
5355
validation_errors = validate(
5456
schema=schema,
5557
document_ast=queries_ast,
56-
rules=[r for r in specified_rules if r is not NoUnusedFragmentsRule],
58+
rules=[r for r in specified_rules if r not in skip_rules],
5759
)
5860
if validation_errors:
5961
raise InvalidOperationForSchema(

ariadne_codegen/settings.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from textwrap import dedent
77
from typing import Dict, List
88

9+
from graphql.validation import UniqueFragmentNamesRule, NoUnusedFragmentsRule
10+
911
from .client_generators.constants import (
1012
DEFAULT_ASYNC_BASE_CLIENT_NAME,
1113
DEFAULT_ASYNC_BASE_CLIENT_OPEN_TELEMETRY_NAME,
@@ -25,6 +27,18 @@ class CommentsStrategy(str, enum.Enum):
2527
STABLE = "stable"
2628
TIMESTAMP = "timestamp"
2729

30+
class ValidationRuleSkips(str, enum.Enum):
31+
UniqueFragmentNames = "UniqueFragmentNames"
32+
NoUnusedFragments = "NoUnusedFragments"
33+
34+
def get_validation_rule(rule: ValidationRuleSkips):
35+
if rule == ValidationRuleSkips.UniqueFragmentNames:
36+
return UniqueFragmentNamesRule
37+
elif rule == ValidationRuleSkips.NoUnusedFragments:
38+
return NoUnusedFragmentsRule
39+
else:
40+
raise ValueError(f"Unknown validation rule: {rule}")
41+
2842

2943
class Strategy(str, enum.Enum):
3044
CLIENT = "client"
@@ -70,6 +84,7 @@ class ClientSettings(BaseSettings):
7084
include_all_enums: bool = True
7185
async_client: bool = True
7286
opentelemetry_client: bool = False
87+
skip_validation_rules: List[ValidationRuleSkips] = field(default_factory=lambda: [ValidationRuleSkips.UniqueFragmentNames,])
7388
files_to_include: List[str] = field(default_factory=list)
7489
scalars: Dict[str, ScalarData] = field(default_factory=dict)
7590

tests/test_schema.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from ariadne_codegen.settings import get_validation_rule
12
import httpx
23
import pytest
34
from graphql import GraphQLSchema, OperationDefinitionNode, build_schema
@@ -15,6 +16,7 @@
1516
read_graphql_file,
1617
walk_graphql_files,
1718
)
19+
from ariadne_codegen.settings import ValidationRuleSkips
1820

1921

2022
@pytest.fixture
@@ -63,6 +65,49 @@ def test_query_2_str():
6365
}
6466
"""
6567

68+
@pytest.fixture
69+
def test_fragment_str():
70+
return """
71+
fragment fragmentA on Custom {
72+
node
73+
}
74+
query testQuery2 {
75+
test {
76+
default
77+
...fragmentA
78+
}
79+
}
80+
"""
81+
82+
@pytest.fixture
83+
def test_duplicate_fragment_str():
84+
return """
85+
fragment fragmentA on Custom {
86+
node
87+
}
88+
fragment fragmentA on Custom {
89+
node
90+
}
91+
query testQuery2 {
92+
test {
93+
default
94+
...fragmentA
95+
}
96+
}
97+
"""
98+
99+
@pytest.fixture
100+
def test_unused_fragment_str():
101+
return """
102+
fragment fragmentA on Custom {
103+
node
104+
}
105+
query testQuery2 {
106+
test {
107+
default
108+
}
109+
}
110+
"""
66111

67112
@pytest.fixture
68113
def single_file_schema(tmp_path_factory, schema_str):
@@ -132,6 +177,24 @@ def single_file_query(tmp_path_factory, test_query_str):
132177
file_.write_text(test_query_str, encoding="utf-8")
133178
return file_
134179

180+
@pytest.fixture
181+
def single_file_query_with_fragment(tmp_path_factory, test_query_str, test_fragment_str):
182+
file_ = tmp_path_factory.mktemp("queries").joinpath("query1_fragment.graphql")
183+
file_.write_text(test_query_str + test_fragment_str, encoding="utf-8")
184+
return file_
185+
186+
@pytest.fixture
187+
def single_file_query_with_duplicate_fragment(tmp_path_factory, test_query_str, test_duplicate_fragment_str):
188+
file_ = tmp_path_factory.mktemp("queries").joinpath("query1_duplicate_fragment.graphql")
189+
file_.write_text(test_query_str + test_duplicate_fragment_str, encoding="utf-8")
190+
return file_
191+
192+
@pytest.fixture
193+
def single_file_query_with_unused_fragment(tmp_path_factory, test_query_str, test_unused_fragment_str):
194+
file_ = tmp_path_factory.mktemp("queries").joinpath("query1_unused_fragment.graphql")
195+
file_.write_text(test_query_str + test_unused_fragment_str, encoding="utf-8")
196+
return file_
197+
135198

136199
@pytest.fixture
137200
def invalid_syntax_query_file(tmp_path_factory):
@@ -434,3 +497,36 @@ def test_get_graphql_queries_with_invalid_query_for_schema_raises_invalid_operat
434497
get_graphql_queries(
435498
invalid_query_for_schema_file.as_posix(), build_schema(schema_str)
436499
)
500+
501+
502+
def test_get_graphql_queries_with_fragment_returns_schema_definitions(
503+
single_file_query_with_fragment, schema_str
504+
):
505+
queries = get_graphql_queries(
506+
single_file_query_with_fragment.as_posix(), build_schema(schema_str)
507+
)
508+
509+
assert len(queries) == 3
510+
511+
def test_get_graphql_queries_with_duplicate_fragment_raises_invalid_operation(
512+
single_file_query_with_duplicate_fragment, schema_str
513+
):
514+
with pytest.raises(InvalidOperationForSchema):
515+
get_graphql_queries(
516+
single_file_query_with_duplicate_fragment.as_posix(), build_schema(schema_str)
517+
)
518+
519+
def test_get_graphql_queries_with_unused_fragment_and_no_skip_rules_raises_invalid_operation(
520+
single_file_query_with_unused_fragment, schema_str
521+
):
522+
with pytest.raises(InvalidOperationForSchema):
523+
get_graphql_queries(
524+
single_file_query_with_unused_fragment.as_posix(), build_schema(schema_str), []
525+
)
526+
527+
def test_get_graphql_queries_with_skip_unique_fragment_names_and_duplicate_fragment_returns_schema_definition(
528+
single_file_query_with_duplicate_fragment, schema_str
529+
):
530+
get_graphql_queries(
531+
single_file_query_with_duplicate_fragment.as_posix(), build_schema(schema_str), [get_validation_rule(ValidationRuleSkips.NoUnusedFragments),get_validation_rule(ValidationRuleSkips.UniqueFragmentNames)]
532+
)

0 commit comments

Comments
 (0)