Пример #1
0
 def contain_sensitive_field(self, node: GraphQLNode, type_def) -> bool:
     if isinstance(node, FragmentSpread) or not node.selection_set:
         return False
     fields: Dict[str, GraphQLField] = {}
     if isinstance(type_def, (GraphQLObjectType, GraphQLInterfaceType)):
         fields = type_def.fields
     for child_node in node.selection_set.selections:
         if isinstance(child_node, Field):
             field = fields.get(child_node.name.value)
             if not field:
                 continue
             field_type = get_named_type(field.type)
             if type_def and type_def.name:
                 self.is_sensitive_field(child_node, type_def.name)
             self.contain_sensitive_field(child_node, field_type)
         if isinstance(child_node, FragmentSpread):
             fragment = self.context.get_fragment(child_node.name.value)
             if fragment:
                 fragment_type = self.context.get_schema().get_type(
                     fragment.type_condition.name.value)
                 self.contain_sensitive_field(fragment, fragment_type)
         if isinstance(child_node, InlineFragment):
             inline_fragment_type = type_def
             if child_node.type_condition and child_node.type_condition.name:
                 inline_fragment_type = self.context.get_schema().get_type(
                     child_node.type_condition.name.value)
             self.contain_sensitive_field(child_node, inline_fragment_type)
     return False
Пример #2
0
def get_type_literal(type_: GraphQLType, optional: bool = False) -> str:
    """
    String! => str
    String => Optional[str]
    [Character!]! => ['Character']
    [Character!] => Optional['Character']
    [Character] => Optional[List[Optional['Character']]]
    """
    is_null = False
    if is_non_null_type(type_):
        type_ = cast(GraphQLWrappingType, type_).of_type
    else:
        is_null = True

    if is_wrapping_type(type_):
        type_ = cast(GraphQLWrappingType, type_)
        value = get_type_literal(type_.of_type)
        if is_list_type(type_):
            value = f'List[{value}]'
    else:
        type_ = get_named_type(type_)
        value = SCALAR_MAP.get(type_.name) or type_.name
        value = value if is_leaf_type(type_) else f"'{value}'"
        # value = value if is_leaf_type(type_) or is_interface_type(type_) else f"'{value}'"

    if optional or is_null:
        value = f'Optional[{value}]'

    return value
Пример #3
0
def _get_args(fields, field):
    args = {}
    if field in fields and fields[field].args:
        args = ({
            arg_name: get_named_type(arg.type).name
            for arg_name, arg in fields[field].args.items()
        })

    return args
Пример #4
0
def connection_definitions(
    node_type: Union[GraphQLNamedOutputType,
                     GraphQLNonNull[GraphQLNamedOutputType]],
    name: Optional[str] = None,
    resolve_node: Optional[GraphQLFieldResolver] = None,
    resolve_cursor: Optional[GraphQLFieldResolver] = None,
    edge_fields: Optional[Thunk[GraphQLFieldMap]] = None,
    connection_fields: Optional[Thunk[GraphQLFieldMap]] = None,
) -> GraphQLConnectionDefinitions:
    """Return GraphQLObjectTypes for a connection with the given name.

    The nodes of the returned object types will be of the specified type.
    """
    name = name or get_named_type(node_type).name

    edge_type = GraphQLObjectType(
        name + "Edge",
        description="An edge in a connection.",
        fields=lambda: {
            "node":
            GraphQLField(
                node_type,
                resolve=resolve_node,
                description="The item at the end of the edge",
            ),
            "cursor":
            GraphQLField(
                GraphQLNonNull(GraphQLString),
                resolve=resolve_cursor,
                description="A cursor for use in pagination",
            ),
            **resolve_maybe_thunk(edge_fields or {}),
        },
    )

    connection_type = GraphQLObjectType(
        name + "Connection",
        description="A connection to a list of items.",
        fields=lambda: {
            "pageInfo":
            GraphQLField(
                GraphQLNonNull(page_info_type),
                description="Information to aid in pagination.",
            ),
            "edges":
            GraphQLField(GraphQLList(edge_type),
                         description="A list of edges."),
            **resolve_maybe_thunk(connection_fields or {}),
        },
    )

    return GraphQLConnectionDefinitions(edge_type, connection_type)
Пример #5
0
    async def resolve(self, _next, _resource, _info, **kwargs):
        mock_header = self.get_mocks_from_headers(_info.context)
        mock_objects = self.get_mocks(extra=mock_header)
        if not mock_objects:
            return await self.run_next(_next, _resource, _info, **kwargs)

        field_name = _info.field_name
        return_type = _info.return_type
        named_type = get_named_type(return_type)
        parent_type_name = _info.parent_type.name

        resolver_is_mocked = (
            self.mock_all or
            mock_objects.has_named_type(named_type.name) or
            mock_objects.has_parent_field(parent_type_name, field_name)
        )

        if not resolver_is_mocked:
            return await self.run_next(_next, _resource, _info, **kwargs)

        if mock_objects.has_parent_field(parent_type_name, field_name):
            return mock_objects.get_parent_field(parent_type_name, field_name)

        # Check if we previously resolved a mock object.
        field_value = (
            _resource.get(field_name)
            if isinstance(_resource, dict)
            else getattr(_resource, field_name, None)
        )

        if field_value:
            return field_value

        # Finally check the return_type for mocks

        def resolve_return_type(schema_type: GraphQLType):
            # Special case for list types return a random length list of type.
            if isinstance(schema_type, GraphQLList):
                list_length = mock_objects.get(LIST_LENGTH_KEY, 3)
                return [resolve_return_type(schema_type.of_type) for x in range(list_length)]

            named_type_name = get_named_type(schema_type).name
            if named_type_name in mock_objects:
                return mock_objects.get(named_type_name)

            # If we reached this point this is a custom type that has not
            # explicitly overridden in the mock_objects. Just return a dict
            # with a `__typename` set to the type name to assist in resolving
            # Unions and Interfaces.
            return SuperDict({'__typename': named_type_name})

        return resolve_return_type(return_type)
Пример #6
0
def is_there_field_annotations(_type):
    """
    Check if a given type contains fields that does not start with '_' and are of enum or scalar type
    These fields should be annotations
    :param type:
    :return boolean:
    """
    for field_name, field_type in _type.fields.items():
        if field_name.startswith('_') or field_name == 'id':
            continue
        if is_enum_or_scalar(get_named_type(field_type.type)):
            return True
    return False
Пример #7
0
def generate(input_file, output_file):
    # load schema
    with open(input_file, 'r') as f:
        schema_string = f.read()
    schema = build_schema(schema_string)

    data = {'types': [], 'interfaces': [], 'unions': []}

    # get list of types
    for type_name, _type in schema.type_map.items():
        if is_interface_type(_type):
            t = {'name': type_name, 'possible_types': []}
            for possible_type in schema.get_possible_types(_type):
                t['possible_types'].append(possible_type.name)
            data['interfaces'].append(t)

        if is_union_type(_type):
            t = {'name': type_name, 'possible_types': []}
            for possible_type in schema.get_possible_types(_type):
                t['possible_types'].append(possible_type.name)
            data['unions'].append(t)

        if is_schema_defined_object_type(_type):
            t = {'name': type_name, 'fields': []}
            # add object fields
            for field_name, field_type in _type.fields.items():
                inner_field_type = get_named_type(field_type.type)

                if is_schema_defined_object_type(inner_field_type) or \
                        is_interface_type(inner_field_type) or \
                        is_union_type(inner_field_type):
                    t['fields'].append(field_name)

            sort_before_rendering(t)
            data['types'].append(t)

    # sort
    data['types'].sort(key=lambda x: x['name'])
    data['interfaces'].sort()
    data['unions'].sort()

    # apply template
    template = Template(filename=f'resources/resolver.template')
    if output_file is None:
        print(template.render(data=data))
    else:
        with open(output_file, 'w') as f:
            updated_schema_string = template.render(data=data)
            api_schema = build_schema(schema_string)
            assert_valid_schema(api_schema)
            f.write(updated_schema_string)
Пример #8
0
        def resolve_return_type(schema_type: GraphQLType):
            # Special case for list types return a random length list of type.
            if isinstance(schema_type, GraphQLList):
                list_length = mock_objects.get(LIST_LENGTH_KEY, 3)
                return [resolve_return_type(schema_type.of_type) for x in range(list_length)]

            named_type_name = get_named_type(schema_type).name
            if named_type_name in mock_objects:
                return mock_objects.get(named_type_name)

            # If we reached this point this is a custom type that has not
            # explicitly overridden in the mock_objects. Just return a dict
            # with a `__typename` set to the type name to assist in resolving
            # Unions and Interfaces.
            return SuperDict({'__typename': named_type_name})
Пример #9
0
def map_schema_types(schema, ignore=['Query', 'Mutation']):
    """Iterate over a graphQl schema to get the schema types and its relations

    The process ignores the root types like Query and Mutation and the Scalar types"""
    objects = dict()
    enums = dict()
    for type_name, type_obj in schema.type_map.items():
        # Iterating over all the items of the schema
        # Ignore internal types and root types
        if (type_name[:2] != '__') and (type_name not in ignore):
            # ENUM types
            if is_enum_type(type_obj):
                enums[type_name] = []  # values
            # Graphql Types
            elif is_object_type(type_obj):
                #Create the type obj
                obj = _get_object(objects, type_name)

                # Iterate over all fields of the graphql type
                for field_name, field_obj in type_obj.fields.items():
                    field = get_named_type(
                        field_obj.type)  # Returns a scalar or an object
                    # setting non null
                    nullable = True
                    if contains_non_null_type(field_obj.type):
                        nullable = False

                    if is_scalar_type(field):
                        obj['field'][field_name] = {
                            'type': field.name,
                            'nullable': nullable
                        }
                    elif is_object_type(field):
                        # if it's a list of types, that means that I'm the parent in a one to many relationship
                        list_ = False
                        if contains_list_type(field_obj.type):
                            list_ = True
                        obj['relationship'][field_name] = {
                            'type': field.name,
                            'nullable': nullable,
                            'list': list_
                        }

    return {'objects': objects, 'enums': enums}
Пример #10
0
    def compute_node_cost(self,
                          node,
                          type_definition,
                          parent_multiplier=None,
                          parent_complexity=None):
        if not parent_multiplier:
            parent_multiplier = []

        if not node.selection_set:
            return 0

        fields = {}
        if isinstance(type_definition, GraphQLObjectType) or isinstance(
                type_definition, GraphQLInterfaceType):
            fields = type_definition.fields

        total_cost = 0
        fragment_costs = []
        variables = {}  # TODO get variables from operation
        selections = node.selection_set.selections
        for selection in selections:
            self.operation_multipliers = [*parent_multiplier]
            node_cost = 0

            if selection.kind == 'field':
                # Calculate cost for FieldNode
                field: GraphQLField = fields.get(selection.name.value)
                if not field:
                    break

                field_type = get_named_type(field.type)
                field_args = get_argument_values(field, selection, variables)

                use_field_type_complexity = False
                cost_is_computed = False
                if field.ast_node and field.ast_node.directives:
                    directive_args: Union[
                        Tuple[int, List,
                              bool], None] = self.get_args_from_directives(
                                  directives=field.ast_node.directives,
                                  field_args=field_args)

                    override_complexity = directive_args[-1]
                    if not override_complexity and isinstance(
                            field_type, GraphQLObjectType):
                        use_field_type_complexity = True
                        parent_complexity, _, _ = self.get_args_from_directives(
                            directives=field_type.ast_node.directives,
                            field_args=field_args)

                    node_cost = self.compute_cost(directive_args)

                    if directive_args:
                        cost_is_computed = True

                if field_type and field_type.ast_node and \
                        field_type.ast_node.directives and \
                        isinstance(field_type, GraphQLObjectType) and \
                        (not cost_is_computed or use_field_type_complexity):
                    directive_args = self.get_args_from_directives(
                        directives=field_type.ast_node.directives,
                        field_args=field_args)
                    node_cost = self.compute_cost(directive_args)

                child_cost = self.compute_node_cost(
                    node=selection,
                    type_definition=field_type,
                    parent_multiplier=self.operation_multipliers,
                    parent_complexity=parent_complexity) or 0
                node_cost += child_cost

            elif selection.kind == 'fragment_spread':
                fragment = self.context.get_fragment(selection.name.value)
                fragment_type = fragment and self.context.schema.get_type(
                    fragment.type_condition.name.value)
                fragment_node_cost = self.compute_node_cost(fragment, fragment_type, self.operation_multipliers) \
                    if fragment \
                    else 0
                fragment_costs.append(fragment_node_cost)
                node_cost = 0

            elif selection.kind == 'inline_fragment':
                inline_fragment_type = self.context.schema.get_type(selection.type_condition.name.value) \
                    if selection.type_condition and selection.type_condition.name \
                    else type_definition
                fragment_node_cost = self.compute_node_cost(selection, inline_fragment_type, self.operation_multipliers) \
                    if selection \
                    else 0

                fragment_costs.append(fragment_node_cost)
                node_cost = 0

            else:
                node_cost = self.compute_node_cost(
                    node=selection, type_definition=type_definition)

            total_cost += max(node_cost, 0)

        if fragment_costs:
            return total_cost + max(fragment_costs)

        return total_cost
Пример #11
0
    def compute_node_cost(self, node: CostAwareNode, type_def, parent_multipliers=None):
        if parent_multipliers is None:
            parent_multipliers = []
        if isinstance(node, FragmentSpread) or not node.selection_set:
            return 0
        fields: GraphQLFieldMap = {}
        if isinstance(type_def, (GraphQLObjectType, GraphQLInterfaceType)):
            fields = type_def.fields
        total = 0
        for child_node in node.selection_set.selections:
            self.operation_multipliers = parent_multipliers[:]
            node_cost = self.default_cost
            if isinstance(child_node, Field):
                field = fields.get(child_node.name.value)
                if not field:
                    continue
                field_type = get_named_type(field.type)
                try:
                    field_args: Dict[str, Any] = get_argument_values(
                        field.args,
                        child_node.arguments,
                        self.variables,
                    )
                except Exception as e:
                    report_error(self.context, e)
                    field_args = {}

                if not self.cost_map:
                    return 0

                cost_map_args = (
                    self.get_args_from_cost_map(child_node, type_def.name, field_args)
                    if type_def and type_def.name
                    else None
                )
                if cost_map_args is not None:
                    try:
                        node_cost = self.compute_cost(**cost_map_args)
                    except (TypeError, ValueError) as e:
                        report_error(self.context, e)

                child_cost = self.compute_node_cost(
                    child_node, field_type, self.operation_multipliers
                )
                node_cost += child_cost
            if isinstance(child_node, FragmentSpread):
                fragment = self.context.get_fragment(child_node.name.value)
                if fragment:
                    fragment_type = self.context.get_schema().get_type(
                        fragment.type_condition.name.value
                    )
                    node_cost = self.compute_node_cost(fragment, fragment_type)
            if isinstance(child_node, InlineFragment):
                inline_fragment_type = type_def
                if child_node.type_condition and child_node.type_condition.name:
                    inline_fragment_type = self.context.get_schema().get_type(
                        child_node.type_condition.name.value
                    )
                node_cost = self.compute_node_cost(child_node, inline_fragment_type)
            total += node_cost
        return total
Пример #12
0
def generate(input_file, output_dir, config: dict):
    # load schema
    with open(input_file, 'r') as f:
        schema_string = f.read()
    schema = build_schema(schema_string)

    data = {
        'types': [],
        'types_by_key': [],
        'interfaces': [],
        'unions': [],
        'typeDelete': [],
        'edge_types_to_delete': [],
        'edge_types_to_update': [],
        'edge_objects': []
    }

    # get list of types
    for type_name, _type in schema.type_map.items():
        if is_union_type(_type):
            data['unions'].append(type_name)
            continue
        if is_interface_type(_type):
            data['interfaces'].append(type_name)
        if is_union_type(_type):
            data['unions'].append(type_name)
        if is_edge_type(_type):
            if config.get('generation').get('query_edge_by_id'):
                data['edge_objects'].append(type_name)
        if is_schema_defined_object_type(_type):
            t = {
                'Name': type_name,
                'name': camelCase(type_name),
                'fields': [],
                'edgeFields': [],
                'edgeFieldEndpoints': [],
                'DateTime': [],
                'hasKeyDirective': f'_KeyFor{type_name}'
                in schema.type_map.keys()
            }
            # add object fields
            for field_name, field_type in _type.fields.items():
                inner_field_type = get_named_type(field_type.type)
                if field_name.startswith('_incoming') or field_name.startswith(
                        '_outgoing'):
                    t['edgeFields'].append(field_name)
                elif is_schema_defined_object_type(
                        inner_field_type) or is_interface_type(
                            inner_field_type) or is_union_type(
                                inner_field_type):
                    t['fields'].append(field_name)
                if inner_field_type.name == 'DateTime':
                    t['DateTime'].append(field_name)
                if field_name[0] == '_':
                    continue
                if is_schema_defined_object_type(
                        inner_field_type) or is_interface_type(
                            inner_field_type) or is_union_type(
                                inner_field_type):
                    t['edgeFieldEndpoints'].append(
                        (pascalCase(field_name), inner_field_type))

                    if config.get('generation').get('delete_edge_objects'):
                        data['edge_types_to_delete'].append(
                            (f'{pascalCase(field_name)}EdgeFrom{type_name}',
                             type_name))

                    if config.get('generation').get('update_edge_objects'):
                        if is_there_field_annotations(schema.type_map[
                                f'_{pascalCase(field_name)}EdgeFrom{type_name}']
                                                      ):
                            data['edge_types_to_update'].append((
                                f'{pascalCase(field_name)}EdgeFrom{type_name}',
                                type_name))

            sort_before_rendering(t)
            data['types'].append(t)

            if config.get('generation').get('delete_objects'):
                data['typeDelete'].append(type_name)

    # sort
    data['types'].sort(key=lambda x: x['name'])
    data['interfaces'].sort()
    data['unions'].sort()
    data['typeDelete'].sort()
    data['edge_types_to_delete'].sort()
    data['edge_types_to_update'].sort()
    data['edge_objects'].sort()

    # apply template
    template = Template(filename=f'resources/resolver.template')
    if output_dir is None:
        print(template.render(data=data))
    else:
        with open(f'{output_dir}/resolvers.js', 'w') as f:
            updated_schema_string = template.render(data=data)
            api_schema = build_schema(schema_string)
            assert_valid_schema(api_schema)
            f.write(updated_schema_string)
Пример #13
0
def complete_field(
    context: 'QueryPlanningContext',
    scope: Scope[GraphQLCompositeType],
    parent_group: 'FetchGroup',
    path: ResponsePath,
    fields: FieldSet,
) -> Field:
    field_node = fields[0].field_node
    field_def = fields[0].field_def
    return_type = get_named_type(field_def.type)

    if not is_composite_type(return_type):
        # FIXME: We should look at all field nodes to make sure we take directives
        # into account (or remove directives for the time being).
        return Field(scope=scope, field_node=field_node, field_def=field_def)
    else:
        # For composite types, we need to recurse.

        field_path = add_path(path, get_response_name(field_node), field_def.type)

        sub_group = FetchGroup(parent_group.service_name)
        sub_group.merge_at = field_path

        sub_group.provided_fields = context.get_provided_fields(
            field_def, parent_group.service_name
        )

        # For abstract types, we always need to request `__typename`
        if is_abstract_type(return_type):
            sub_group.fields.append(
                Field(
                    scope=context.new_scope(cast(GraphQLCompositeType, return_type), scope),
                    field_node=typename_field,
                    field_def=GraphQLField(TypeNameMetaFieldDef, name='__typename'),
                )
            )

        sub_fields = collect_subfields(context, cast(GraphQLCompositeType, return_type), fields)
        # debug.group(() => `Splitting collected sub-fields (${debugPrintFields(subfields)})`);
        split_subfields(context, field_path, sub_fields, sub_group)
        # debug.groupEnd();

        parent_group.other_dependent_groups.extend(sub_group.dependent_groups)

        selection_set = selection_set_from_field_set(
            sub_group.fields, cast(GraphQLCompositeType, return_type)
        )

        if context.auto_fragmentization and len(sub_group.fields) > 2:
            internal_fragment = get_internal_fragment(
                selection_set, cast(GraphQLCompositeType, return_type), context
            )
            definition = internal_fragment.definition
            selection_set = internal_fragment.selection_set
            parent_group.internal_fragments.add(definition)

        # "Hoist" internalFragments of the subGroup into the parentGroup so all
        # fragments can be included in the final request for the root FetchGroup
        parent_group.internal_fragments.update(sub_group.internal_fragments)

        new_field_node = copy(field_node)
        new_field_node.selection_set = selection_set
        return Field(scope=scope, field_node=new_field_node, field_def=field_def)