autographql 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ """AutoGraphQL: schema-driven generation of NL2GraphQL datasets.
2
+
3
+ Turns a GraphQL SDL schema into validated (schema, natural-language query,
4
+ GraphQL query) triples, covering aliases, directives, fragments, inline
5
+ fragments, filters, pagination and mutations.
6
+ """
7
+ try:
8
+ # Written at build time by hatch-vcs from the git tag.
9
+ from ._version import __version__
10
+ except ImportError: # running from a source tree, not installed
11
+ __version__ = "0.0.0.dev0"
12
+
13
+ from .query_generator_app import GraphQLQueryGeneratorApp
14
+
15
+ __all__ = ["GraphQLQueryGeneratorApp", "__version__"]
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.1.0'
22
+ __version_tuple__ = version_tuple = (0, 1, 0)
23
+
24
+ __commit_id__ = commit_id = None
autographql/cli.py ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env python3
2
+ import os
3
+ import sys
4
+ import argparse
5
+
6
+ root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
7
+ if root_dir not in sys.path:
8
+ sys.path.insert(0, root_dir)
9
+
10
+ from .query_generator_app import GraphQLQueryGeneratorApp
11
+
12
+ def main():
13
+ parser = argparse.ArgumentParser(description='Generate GraphQL queries from multiple schemas')
14
+ parser.add_argument('--schema', type=str, help='Path to a specific GraphQL schema file')
15
+ parser.add_argument('--category', type=str, required=True,
16
+ choices=['external_fragments', 'inline_fragments', 'directives', 'filters', 'aliases', 'mutations', 'pagination', 'all'],
17
+ help='Category of queries to generate')
18
+ parser.add_argument('--count', type=int, required=True, help='Number of unique queries to generate per category')
19
+ parser.add_argument('--max-hops', type=int, default=2, help='Maximum number of hops (query depth) allowed in generated queries')
20
+
21
+ args = parser.parse_args()
22
+
23
+ schema_dir = 'schemas'
24
+ output_dir = 'generated_queries'
25
+
26
+ if not os.path.exists(output_dir):
27
+ os.makedirs(output_dir)
28
+
29
+ if not os.path.exists(schema_dir):
30
+ os.makedirs(schema_dir)
31
+
32
+ app = GraphQLQueryGeneratorApp()
33
+
34
+ if args.schema:
35
+ schema_path = args.schema
36
+
37
+ if os.path.isdir(schema_path):
38
+ index_file = os.path.join(schema_path, 'index.graphql')
39
+ if os.path.isfile(index_file):
40
+ schema_path = index_file
41
+ elif not os.path.exists(schema_path):
42
+ schema_dir_path = os.path.join(schema_dir, schema_path)
43
+ index_file = os.path.join(schema_dir_path, 'index.graphql')
44
+
45
+ if os.path.isdir(schema_dir_path) and os.path.isfile(index_file):
46
+ schema_path = index_file
47
+ else:
48
+ schema_file = schema_path
49
+ if not schema_file.endswith('.graphql'):
50
+ schema_file += '.graphql'
51
+
52
+ schema_path = os.path.join(schema_dir, schema_file)
53
+
54
+ if not os.path.exists(schema_path):
55
+ print(f"Error: Schema '{args.schema}' does not exist at any of the possible locations.")
56
+ return 1
57
+
58
+ app.generate_queries_for_schema(schema_path, args.category, args.count, args.max_hops, output_dir)
59
+ else:
60
+ app.generate_queries_from_directory(schema_dir, args.category, args.count, args.max_hops, output_dir)
61
+
62
+ print("\nQuery generation complete!")
63
+ return 0
64
+
65
+ if __name__ == "__main__":
66
+ sys.exit(main())
@@ -0,0 +1 @@
1
+ """Per-category query generators, selected through `generator_factory`."""
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env python3
2
+ import logging
3
+ from typing import Dict, Any, Tuple, Set
4
+ import random
5
+ from .base_generator import BaseQueryGenerator
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ class AliasQueryGenerator(BaseQueryGenerator):
10
+ def __init__(self, schema, exclude_fields: Set[str] = None, max_hops: int = 2):
11
+ super().__init__(schema, exclude_fields, max_hops)
12
+
13
+ def generate_query(self) -> Tuple[str, Dict[str, Any]]:
14
+ if not self.root_query_fields:
15
+ return "", {}
16
+
17
+ add_directive = self.should_add_feature(0.3)
18
+ add_filter = False if add_directive else self.should_add_feature(0.3)
19
+ query_sub_category = ''
20
+ query_variables = {}
21
+
22
+ available_fields = []
23
+ for field_name in self.root_query_fields.keys():
24
+ if field_name in self.exclude_fields:
25
+ continue
26
+
27
+ field = self.root_query_fields.get(field_name)
28
+ if field:
29
+ return_type = self._get_field_type_name(field)
30
+ if return_type:
31
+ available_fields.append(field_name)
32
+
33
+ if not available_fields:
34
+ return "", {}
35
+
36
+ object_fields = []
37
+ for field_name in available_fields:
38
+ field = self.root_query_fields.get(field_name)
39
+ if not field:
40
+ continue
41
+
42
+ return_type_name = self._get_field_type_name(field)
43
+ if self._is_object_or_interface(return_type_name):
44
+ sub_fields = self._get_type_fields(return_type_name)
45
+ if sub_fields and len(sub_fields) > 0:
46
+ object_fields.append((field_name, field, return_type_name))
47
+
48
+ if object_fields:
49
+ field_name, field, return_type_name = random.choice(object_fields)
50
+
51
+ field_args, field_vars = self._get_field_args(field, use_variables=True)
52
+ query_variables.update(field_vars)
53
+
54
+ if add_filter and not field_args:
55
+ field_name_with_filter, sub_category, filter_variables = self.add_filter_to_field(field, field_name)
56
+ if sub_category == 'filters':
57
+ field_name, field_args = field_name_with_filter.split('(', 1)
58
+ field_args = '(' + field_args
59
+ query_variables.update(filter_variables)
60
+ query_sub_category = 'filters'
61
+
62
+ sub_fields = self._get_type_fields(return_type_name)
63
+ sub_field_names = list(sub_fields.keys())
64
+
65
+ if sub_field_names:
66
+ num_subfields = min(random.randint(2, 5), len(sub_field_names))
67
+ selected_subfields = random.sample(sub_field_names, num_subfields)
68
+
69
+ aliased_fields = []
70
+ for subfield in selected_subfields:
71
+ alias = f"aliased{subfield.capitalize()}"
72
+ subfield_type = self._get_field_type_name(sub_fields[subfield])
73
+ subfield_name = subfield
74
+ apply_directive = add_directive and self.should_add_feature(0.5) and query_sub_category != 'filters'
75
+
76
+ if self._is_object_or_interface(subfield_type):
77
+ nested_fields_str = self._build_fields_string(subfield_type, max_depth=2)
78
+
79
+ if apply_directive:
80
+ subfield_with_directive, sub_category, directive_variables = self.add_directive_to_field(
81
+ subfield_name + " {"
82
+ )
83
+ if sub_category == 'directives':
84
+ query_variables.update(directive_variables)
85
+ query_sub_category = 'directives'
86
+ subfield_opening = subfield_with_directive
87
+ else:
88
+ subfield_opening = f"{subfield_name} {{"
89
+ else:
90
+ subfield_opening = f"{subfield_name} {{"
91
+
92
+ if nested_fields_str:
93
+ indented_nested_str = nested_fields_str.replace('\n', '\n ')
94
+ aliased_fields.append(f"{alias}: {subfield_opening}\n {indented_nested_str}\n }}")
95
+ else:
96
+ default_field = self._get_default_field(subfield_type)
97
+ aliased_fields.append(f"{alias}: {subfield_opening}\n {default_field}\n }}")
98
+ else:
99
+ if apply_directive:
100
+ subfield_with_directive, sub_category, directive_variables = self.add_directive_to_field(subfield_name)
101
+ if sub_category == 'directives':
102
+ query_variables.update(directive_variables)
103
+ query_sub_category = 'directives'
104
+ aliased_fields.append(f"{alias}: {subfield_with_directive}")
105
+ else:
106
+ aliased_fields.append(f"{alias}: {subfield_name}")
107
+ else:
108
+ aliased_fields.append(f"{alias}: {subfield_name}")
109
+
110
+ variable_section = self._format_variable_section(query_variables)
111
+ indented_aliased_fields = "\n ".join(aliased_fields)
112
+ query = f"""query {field_name.capitalize()}AliasQuery{variable_section} {{
113
+ {field_name}{field_args} {{
114
+ {indented_aliased_fields}
115
+ }}
116
+ }}"""
117
+
118
+ final_variables = self._simplify_variables(query_variables)
119
+ return query, final_variables
120
+
121
+ return "", {}
122
+
123
+ def _format_variable_section(self, query_variables: Dict[str, Any]) -> str:
124
+ """Format the variable section for the query."""
125
+ if not query_variables:
126
+ return ""
127
+
128
+ var_defs = []
129
+ for var_name, var_info in query_variables.items():
130
+ var_type = var_info.get('type', 'String')
131
+ if not var_type.endswith('!'):
132
+ var_type += '!'
133
+ var_defs.append(f"${var_name}: {var_type}")
134
+
135
+ return f"({', '.join(var_defs)})" if var_defs else ""
136
+
137
+ def _simplify_variables(self, query_variables: Dict[str, Any]) -> Dict[str, Any]:
138
+ """Simplify variable values for the query."""
139
+ final_variables = {}
140
+ for var_name, var_info in query_variables.items():
141
+ final_variables[var_name] = self._simplify_variable_value(var_info)
142
+ return final_variables