-
Notifications
You must be signed in to change notification settings - Fork 594
/
__init__.py
133 lines (106 loc) · 3.48 KB
/
__init__.py
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
#!/usr/bin/env python3
import ast
import json
import pycodestyle
from gql_checker.__about__ import (
__author__,
__copyright__,
__email__,
__license__,
__summary__,
__title__,
__uri__,
__version__,
)
from graphql import Source, build_client_schema, parse, validate
__all__ = [
"__title__",
"__summary__",
"__uri__",
"__version__",
"__author__",
"__email__",
"__license__",
"__copyright__",
]
GQL_SYNTAX_ERROR = "GQL100"
GQL_VALIDATION_ERROR = "GQL101"
class ImportVisitor(ast.NodeVisitor):
"""
This class visits all the gql calls.
"""
def __init__(self, filename, options):
self.filename = filename
self.options = options or {}
self.calls = []
def visit_Call(self, node): # noqa
if node.func.id == "gql":
self.calls.append(node)
def node_query(self, node):
"""
Return the query for the gql call node
"""
if isinstance(node, ast.Call):
assert node.args
arg = node.args[0]
if not isinstance(arg, ast.Str):
return
else:
raise TypeError(type(node))
return arg.s
class ImportOrderChecker(object):
visitor_class = ImportVisitor
options = None
def __init__(self, filename, tree):
self.tree = tree
self.filename = filename
self.lines = None
def load_file(self):
if self.filename in ("stdin", "-", None):
self.filename = "stdin"
self.lines = pycodestyle.stdin_get_value().splitlines(True)
else:
self.lines = pycodestyle.readlines(self.filename)
if not self.tree:
self.tree = ast.parse("".join(self.lines))
def get_schema(self):
gql_introspection_schema = self.options.get("gql_introspection_schema")
if gql_introspection_schema:
try:
with open(gql_introspection_schema) as data_file:
introspection_schema = json.load(data_file)
return build_client_schema(introspection_schema)
except IOError as e:
raise Exception(
"Cannot find the provided introspection schema. {}".format(str(e))
)
schema = self.options.get("schema")
assert schema, "Need to provide schema"
def validation_errors(self, ast):
return validate(self.get_schema(), ast)
def error(self, node, code, message):
raise NotImplementedError()
def check_gql(self):
if not self.tree or not self.lines:
self.load_file()
visitor = self.visitor_class(self.filename, self.options)
visitor.visit(self.tree)
for node in visitor.calls:
# Lines with the noqa flag are ignored entirely
if pycodestyle.noqa(self.lines[node.lineno - 1]):
continue
query = visitor.node_query(node)
if not query:
continue
try:
source = Source(query, "gql query")
ast = parse(source)
except Exception as e:
message = str(e)
yield self.error(node, GQL_SYNTAX_ERROR, message)
continue
validation_errors = self.validation_errors(ast)
if validation_errors:
for error in validation_errors:
message = str(error)
yield self.error(node, GQL_VALIDATION_ERROR, message)