def parse( self, query: str, full_fragments: str = "", should_validate: bool = True, is_fragment: bool = False, ) -> ParsedQuery: query_document_ast = parse("".join([full_fragments, query])) document_ast = parse(query) if not is_fragment: operation = get_operation_ast(document_ast) if not operation.name: raise AnonymousQueryError() if should_validate: errors = validate( self.schema, query_document_ast, [rule for rule in specified_rules if rule is not NoUnusedFragmentsRule], ) if errors: raise InvalidQueryError(errors) type_info = TypeInfo(self.schema) visitor = FieldToTypeMatcherVisitor(self.schema, type_info, query) visit(document_ast, TypeInfoVisitor(type_info, visitor)) result = visitor.parsed return result
def get_input_type_from_query(query: str) -> Optional[str]: ''' Given a GraphQL query for a DjangoFormMutation, return the input type, e.g.: >>> get_input_type_from_query( ... 'mutation FooMutation($input: BarInput!) { foo(input: $input) }') 'BarInput' If the input type cannot be found, return None. ''' try: ast = parse(query) except GraphQLSyntaxError: return None class InputTypeVisitor(Visitor): in_input_definition: bool = False input_type: Optional[str] = None def enter(self, node, *args): if isinstance(node, VariableDefinition) and node.variable.name.value == 'input': self.in_input_definition = True elif isinstance(node, NamedType) and self.in_input_definition: self.input_type = node.name.value def leave(self, node, *args): if isinstance(node, VariableDefinition) and node.variable.name.value == 'input': self.in_input_definition = False visitor = InputTypeVisitor() visit(ast, visitor) return visitor.input_type
def __init__( self, schema: GraphQLSchema, operation: OperationDefinitionNode, fragments: FragmentMap, auto_fragmentization: bool, ): self.internal_fragments = {} self.internal_fragment_count = 0 self.schema = schema self.operation = operation self.fragments = fragments self.auto_fragmentization = auto_fragmentization variable_definitions = {} class VariableDefinitionVisitor(Visitor): # noinspection PyMethodMayBeStatic def enter_variable_definition(self, definition: VariableDefinitionNode, *_) -> None: variable_definitions[definition.variable.name.value] = definition self.variable_definitions = variable_definitions visit(operation, VariableDefinitionVisitor())
def _calculate_cost(ast_document, max_complexity=100) -> int: context = ValidationContext(schema=post_schema, ast=ast_document, type_info=TypeInfo(post_schema), on_error=on_error_stub) visitor = CostAnalysisVisitor(context=context, max_complexity=max_complexity) visit(ast_document, visitor) return visitor.total_complexity
def parse_result_recursive( schema: GraphQLSchema, document: DocumentNode, node: Node, result: Optional[Dict[str, Any]], initial_type: Optional[GraphQLType] = None, inside_list_level: int = 0, visit_fragment: bool = False, operation_name: Optional[str] = None, ) -> Any: if result is None: return None type_info = TypeInfo(schema, initial_type=initial_type) visited = visit( node, TypeInfoVisitor( type_info, ParseResultVisitor( schema, document, node, result, type_info=type_info, inside_list_level=inside_list_level, visit_fragment=visit_fragment, operation_name=operation_name, ), ), visitor_keys=RESULT_DOCUMENT_KEYS, ) return visited
def parse(self, query: str, should_validate: bool = True) -> ParsedQuery: document_ast = parse(query) operation = get_operation_ast(document_ast) if not operation.name: raise AnonymousQueryError() if should_validate: errors = validate(self.schema, document_ast) if errors: raise InvalidQueryError(errors) type_info = TypeInfo(self.schema) visitor = FieldToTypeMatcherVisitor(self.schema, type_info, query) visit(document_ast, TypeInfoVisitor(type_info, visitor)) result = visitor.parsed return result
def parse(self, query: str, should_validate: bool = True) -> ParsedQuery: document_ast = parse(query) operation = get_operation_ast(document_ast) if not operation.name: raise AnonymousQueryError() if should_validate: errors = validate(self.schema, document_ast) if errors: raise InvalidQueryError(errors) type_info = TypeInfo(self.schema) visitor = FieldToTypeMatcherVisitor(self.schema, type_info, query) visit(document_ast, TypeInfoVisitor(type_info, visitor)) result = visitor.parsed return result
def validate_depth(schema: GraphQLSchema, document: DocumentNode, context_value: Any, errors: List[GraphQLError]): def on_error(error: GraphQLError) -> None: errors.append(error) raise ValidationAbortedError depth_analysis = context_value.get("depth_analysis") if depth_analysis: max_depth = depth_analysis.get("max_depth", 10) context = ValidationContext(schema=schema, ast=document, type_info=TypeInfo(schema), on_error=on_error) visit(document, DepthAnalysisVisitor(context=context, max_depth=max_depth))
def validate_cost(schema: GraphQLSchema, document: DocumentNode, context_value: Any, errors: List[GraphQLError]): def on_error(error: GraphQLError) -> None: errors.append(error) raise ValidationAbortedError cost_analysis = context_value.get("cost_analysis") if cost_analysis: max_complexity = cost_analysis.get("max_complexity", 500) context = ValidationContext(schema=schema, ast=document, type_info=TypeInfo(schema), on_error=on_error) visit( document, CostAnalysisVisitor(context=context, max_complexity=max_complexity)) return []
def get_variable_usages( self, selection_set: SelectionSetNode, fragments: set[FragmentDefinitionNode] ) -> VariableUsages: usages: dict[str, VariableDefinitionNode] = {} # Construct a document of the selection set and fragment definitions so we # can visit them, adding all variable usages to the `usages` object. definitions: list[DefinitionNode] = [ OperationDefinitionNode(selection_set=selection_set, operation=OperationType.QUERY) ] definitions.extend(fragments) document = DocumentNode(definitions=definitions) this = self class VariableVisitor(Visitor): # noinspection PyMethodMayBeStatic def enter_variable(self, node: VariableNode): usages[node.name.value] = this.variable_definitions[node.name.value] visit(document, VariableVisitor()) return usages
def _split_query_one_level( query_node: SubQueryNode, merged_schema_descriptor: MergedSchemaDescriptor, edge_to_stitch_fields: Dict[Tuple[str, str], Tuple[str, str]], name_assigner: IntermediateOutNameAssigner, ) -> None: """Split the query node, creating children out of all branches across cross schema edges. The input query_node will be modified. Its query_ast will be replaced by a new AST with branches leading out of cross schema edges removed, and new property fields and @output directives added as necessary. Its child_query_connections will be modified by tacking on SubQueryNodes created from these cut-off branches. Args: query_node: Query to be split into its child components. Its query_ast will be replaced (but the original AST will not be modified) and its child_query_connections will be modified. merged_schema_descriptor: The schema that the query AST contained in the input query_node targets. edge_to_stitch_fields: Mapping (type name, vertex field name) to (source field name, sink field name) used in the @stitch directive for each cross schema edge. name_assigner: Object used to generate and keep track of names of newly created @output directive. Raises: - GraphQLValidationError if the query AST contained in the input query_node is invalid, for example, having an @output directive on a cross schema edge - SchemaStructureError if the merged_schema_descriptor provided appears to be invalid or inconsistent """ type_info = TypeInfo(merged_schema_descriptor.schema) operation_definition = get_only_query_definition(query_node.query_ast, GraphQLValidationError) if not isinstance(operation_definition, OperationDefinitionNode): raise AssertionError( f"Expected operation_definition to be an OperationDefinitionNode, but it was of" f"type {type(operation_definition)}. This should be impossible.") type_info.enter(operation_definition) new_operation_definition = _split_query_ast_one_level_recursive( query_node, operation_definition, type_info, edge_to_stitch_fields, name_assigner) type_info.leave(operation_definition) if new_operation_definition is not operation_definition: query_node.query_ast = DocumentNode( definitions=[new_operation_definition]) # Check resulting AST is valid validation_errors = validate(merged_schema_descriptor.schema, query_node.query_ast) if len(validation_errors) > 0: raise AssertionError( 'The resulting split query "{}" is invalid, with the following error messages: {}' "".format(query_node.query_ast, validation_errors)) # Set schema id, check for consistency visitor = TypeInfoVisitor( type_info, SchemaIdSetterVisitor(type_info, query_node, merged_schema_descriptor.type_name_to_schema_id), ) visit(query_node.query_ast, visitor) if query_node.schema_id is None: raise AssertionError( 'Unreachable code reached. The schema id of query piece "{}" has not been ' "determined.".format(query_node.query_ast))
def debug_ast(node): graphql.visit(node, DebugVisitor())