Ejemplo n.º 1
0
    def visit_node(self, obj: Node):
        fields = [self.visit(field) for field in obj.fields]

        return ast.ObjectTypeDefinitionNode(
            name=_name(obj.name),
            fields=fields,
            directives=[self.visit(d) for d in obj.directives])
Ejemplo n.º 2
0
def _get_basic_schema_ast(query_type):
    """Create a basic AST Document representing a nearly blank schema.

    The output AST contains a single query type, whose name is the input string. The query type
    is guaranteed to be the second entry of Document definitions, after the schema definition.
    The query type has no fields.

    Args:
        query_type: str, name of the query type for the schema

    Returns:
        DocumentNode, representing a nearly blank schema
    """
    blank_ast = ast_types.DocumentNode(definitions=[
        ast_types.SchemaDefinitionNode(
            operation_types=[
                ast_types.OperationTypeDefinitionNode(
                    operation=ast_types.OperationType.QUERY,
                    type=ast_types.NamedTypeNode(name=ast_types.NameNode(
                        value=query_type)),
                )
            ],
            directives=[],
        ),
        ast_types.ObjectTypeDefinitionNode(
            name=ast_types.NameNode(value=query_type),
            fields=[],
            interfaces=[],
            directives=[],
        ),
    ])
    return blank_ast
Ejemplo n.º 3
0
    def export_record(self, type_name: str, obj: Record):
        def new_field(name: str, type_):
            return ast.FieldDefinitionNode(
                name=_name(name),
                type=_encode_type(type_),
            )

        fields = [
            new_field(f_name, field)
            for f_name, field in obj.__field_types__.items()
        ]
        return ast.ObjectTypeDefinitionNode(
            name=_name(type_name),
            fields=fields,
        )
Ejemplo n.º 4
0
def _add_edge_field(source_type_node, sink_type_name, source_field_name,
                    sink_field_name, edge_name, direction):
    """Add one direction of the specified edge as a field of the source type.

    Args:
        source_type_node: (Interface/Object)TypeDefinitionNode, where a new field representing
                          one direction of the edge will be added.
        sink_type_name: str, name of the type that the edge leads to
        source_field_name: str, name of the source side field that will be stitched
        sink_field_name: str, name of the sink side field that will be stitched
        edge_name: str, name of the edge that will be used to name the new field
        direction: str, either OUTBOUND_EDGE_DIRECTION or INBOUND_EDGE_DIRECTION ('out'
                   or 'in')

    Returns:
        (Interface/Object)TypeDefinitionNode, updated version of source_type_node.

    Raises:
        - SchemaNameConflictError if the new cross-schema edge name causes a name conflict with
          existing fields, or fields created by previous cross-schema edges
    """
    type_fields = source_type_node.fields

    if direction not in (OUTBOUND_EDGE_DIRECTION, INBOUND_EDGE_DIRECTION):
        raise AssertionError(
            u'Input "direction" must be either "{}" or "{}".'.format(
                OUTBOUND_EDGE_DIRECTION, INBOUND_EDGE_DIRECTION))
    new_edge_field_name = direction + "_" + edge_name

    # Error if new edge causes a field name clash
    if any(field.name.value == new_edge_field_name for field in type_fields):
        raise SchemaNameConflictError(
            u'New field "{}" under type "{}" created by the {}bound field of edge named '
            u'"{}" clashes with an existing field of the same name. Consider changing the '
            u"name of your edge to avoid name conflicts.".format(
                new_edge_field_name, source_type_node.name.value, direction,
                edge_name))

    new_edge_field_node = ast_types.FieldDefinitionNode(
        name=ast_types.NameNode(value=new_edge_field_name),
        arguments=[],
        type=ast_types.ListTypeNode(type=ast_types.NamedTypeNode(
            name=ast_types.NameNode(value=sink_type_name), ), ),
        directives=[
            _build_stitch_directive(source_field_name, sink_field_name),
        ],
    )

    new_type_fields = list(type_fields)
    new_type_fields.append(new_edge_field_node)
    if type(source_type_node) == ast_types.ObjectTypeDefinitionNode:
        new_source_type_node = ast_types.ObjectTypeDefinitionNode(
            description=source_type_node.description,
            name=source_type_node.name,
            directives=source_type_node.directives,
            fields=new_type_fields,
            interfaces=source_type_node.interfaces,
        )
    elif type(source_type_node) == ast_types.InterfaceTypeDefinitionNode:
        new_source_type_node = ast_types.InterfaceTypeDefinitionNode(
            description=source_type_node.description,
            name=source_type_node.name,
            directives=source_type_node.directives,
            fields=new_type_fields,
        )
    else:
        raise AssertionError(
            u'Input "source_type_node" must be of type {} or {}. Received type {}'
            .format(
                ast_types.ObjectTypeDefinitionNode,
                ast_types.InterfaceTypeDefinitionNode,
                type(source_type_node),
            ))
    return new_source_type_node
Ejemplo n.º 5
0
def _accumulate_types(
    merged_schema_ast,
    merged_query_type_name,
    type_name_to_schema_id,
    scalars,
    directives,
    current_schema_id,
    current_ast,
):
    """Add all types and query type fields of current_ast into merged_schema_ast.

    Args:
        merged_schema_ast: DocumentNode.
        merged_query_type_name: str, name of the query type in the merged_schema_ast
        type_name_to_schema_id: Dict[str, str], mapping type name to the id of the schema that
                                the type is from.
        scalars: Set[str], names of all scalars in the merged_schema so far.
        directives: Dict[str, DirectiveDefinitionNode], mapping directive name to definition.
        current_schema_id: str, identifier of the schema being merged
        current_ast: DocumentNode, representing the schema being merged into merged_schema_ast

    Returns:
        tuple (new_merged_schema_ast, type_name_to_schema_id, scalars, directives) with the
        following information:
            new_merged_schema_ast: DocumentNode, updated version of merged_schema_ast with
                                   current_ast incorporated.
            type_name_to_schema_id: Dict[str, str], updated version of type_name_to_schema_id input.
            scalars: Set[str], potentially updated version of scalars input.
            directives: Dict[str, DirectiveDefinitionNode], potentially updated version of
                        directives input.

    Raises:
        - ValueError if the schema identifier is not a nonempty string of alphanumeric
          characters and underscores
        - SchemaStructureError if the schema does not have the expected form; in particular, if
          the AST does not represent a valid schema, if any query type field does not have the
          same name as the type that it queries, if the schema contains type extensions or
          input object definitions, or if the schema contains mutations or subscriptions
        - SchemaNameConflictError if there are conflicts between the names of
          types/interfaces/enums/scalars, or conflicts between the definition of directives
          with the same name
    """
    # Check input schema identifier is a string of alphanumeric characters and underscores
    check_schema_identifier_is_valid(current_schema_id)
    # Check input schema satisfies various structural requirements
    check_ast_schema_is_valid(current_ast)

    current_schema = build_ast_schema(current_ast)
    current_query_type = get_query_type_name(current_schema)

    # Merge current_ast into merged_schema_ast.
    # Concatenate new scalars, new directives, and type definitions other than the query
    # type to definitions list.
    # Raise errors for conflicting scalars, directives, or types.
    new_definitions = current_ast.definitions  # List[Node]
    new_query_type_fields = None  # List[FieldDefinition]

    for new_definition in new_definitions:
        if isinstance(new_definition, ast_types.SchemaDefinitionNode):
            continue
        elif (isinstance(new_definition, ast_types.ObjectTypeDefinitionNode)
              and new_definition.name.value
              == current_query_type):  # query type definition
            new_query_type_fields = new_definition.fields  # List[FieldDefinitionNode]
        elif isinstance(new_definition, ast_types.DirectiveDefinitionNode):
            directives, merged_schema_ast = _process_directive_definition(
                new_definition, directives, merged_schema_ast)
        elif isinstance(new_definition, ast_types.ScalarTypeDefinitionNode):
            scalars, merged_schema_ast = _process_scalar_definition(
                new_definition, scalars, type_name_to_schema_id,
                merged_schema_ast)
        elif isinstance(
                new_definition,
            (
                ast_types.EnumTypeDefinitionNode,
                ast_types.InterfaceTypeDefinitionNode,
                ast_types.ObjectTypeDefinitionNode,
                ast_types.UnionTypeDefinitionNode,
            ),
        ):
            type_name_to_schema_id, merged_schema_ast = _process_generic_type_definition(
                new_definition,
                current_schema_id,
                scalars,
                type_name_to_schema_id,
                merged_schema_ast,
            )
        else:  # All definition types should've been covered
            raise AssertionError(
                u"Unreachable code reached. Missed definition type: "
                u'"{}"'.format(type(new_definition).__name__))

    # Concatenate all query type fields.
    # Since query_type was taken from the schema built from the input AST, the query type
    # should never be not found.
    if new_query_type_fields is None:
        raise AssertionError(
            u'Unreachable code reached. Query type "{}" field definitions '
            u"unexpectedly not found.".format(current_query_type))

    # Note that as field names and type names have been confirmed to match up, and types
    # were merged without name conflicts, query type fields can also be safely merged.
    #
    # Query type is the second entry in the list of definitions of the merged_schema_ast,
    # as guaranteed by _get_basic_schema_ast()
    query_type_index = 1
    new_definitions = list(merged_schema_ast.definitions)
    merged_query_type_definition = new_definitions[query_type_index]
    if merged_query_type_definition.name.value != merged_query_type_name:
        raise AssertionError(
            u"Unreachable code reached. The second definition in the schema is unexpectedly "
            u'not the query type "{}", but is instead "{}".'.format(
                merged_query_type_name,
                merged_query_type_definition.name.value))
    new_fields = list(merged_query_type_definition.fields)
    new_fields.extend(new_query_type_fields)
    new_merged_query_type_definition = ast_types.ObjectTypeDefinitionNode(
        name=merged_query_type_definition.name,
        interfaces=merged_query_type_definition.interfaces,
        fields=new_fields,
        directives=merged_query_type_definition.directives,
    )
    new_definitions[query_type_index] = new_merged_query_type_definition
    new_merged_schema_ast = ast_types.DocumentNode(definitions=new_definitions)
    return new_merged_schema_ast, type_name_to_schema_id, scalars, directives