def get_filter_fields(self, typename): selftype = self._gql_inobjtypes[typename] fields = OrderedDict() fields['and'] = GraphQLInputObjectField( GraphQLList(GraphQLNonNull(selftype))) fields['or'] = GraphQLInputObjectField( GraphQLList(GraphQLNonNull(selftype))) fields['not'] = GraphQLInputObjectField(selftype) edb_type = self.edb_schema.get(typename) for name in sorted(edb_type.pointers, key=lambda x: x.name): if name.name == '__type__': continue if name.name in fields: raise GraphQLCoreError( f"{name.name!r} of {typename} clashes with special " "reserved fields required for GraphQL conversion" ) ptr = edb_type.resolve_pointer(self.edb_schema, name) if not isinstance(ptr.target, ScalarType): continue target = self._convert_edb_type(ptr.target) intype = self._gql_inobjtypes.get(f'Filter{target.name}') if intype: fields[name.name] = GraphQLInputObjectField(intype) return fields
def plural_identifying_root_field( arg_name: str, input_type: GraphQLInputType, output_type: GraphQLOutputType, resolve_single_input: Callable[[GraphQLResolveInfo, str], Any], description: Optional[str] = None, ) -> GraphQLField: def resolve(_obj: Any, info: GraphQLResolveInfo, **args: Any) -> List: inputs = args[arg_name] return [resolve_single_input(info, input_) for input_ in inputs] return GraphQLField( GraphQLList(output_type), description=description, args={ arg_name: GraphQLArgument( GraphQLNonNull( GraphQLList( GraphQLNonNull( get_nullable_type(input_type)) # type: ignore ))) }, resolve=resolve, )
def get_type_comparison_fields(graphql_type: Union[GraphQLScalarType, GraphQLList], inputs: Inputs, type_name: str) -> GraphQLInputObjectType: if type_name in inputs: return inputs[type_name] fields = { "_eq": GraphQLInputField(graphql_type), "_neq": GraphQLInputField(graphql_type), "_in": GraphQLInputField(GraphQLList(GraphQLNonNull(graphql_type))), "_nin": GraphQLInputField(GraphQLList(GraphQLNonNull(graphql_type))), "_lt": GraphQLInputField(graphql_type), "_gt": GraphQLInputField(graphql_type), "_gte": GraphQLInputField(graphql_type), "_lte": GraphQLInputField(graphql_type), "_is_null": GraphQLInputField(GraphQLBoolean), } fields_string = { "_like": GraphQLInputField(GraphQLString), "_nlike": GraphQLInputField(GraphQLString), } if graphql_type == GraphQLString: fields.update(fields_string) inputs[type_name] = GraphQLInputObjectType(type_name, fields) return inputs[type_name]
def get_graphql_type_for_annotation(annotation, field_name: str, force_optional: bool = False): # TODO: this might lead to issues with types that have a field value is_field_optional = force_optional if is_generic(annotation): types = getattr(annotation, "__args__", None) if types is None: raise MissingTypesForGenericError(field_name, annotation) graphql_type = copy_annotation_with_types(annotation, *types) elif hasattr(annotation, "graphql_type"): graphql_type = annotation.graphql_type else: annotation_name = getattr(annotation, "_name", None) if annotation_name == "List": list_of_type = get_graphql_type_for_annotation( annotation.__args__[0], field_name) list_type = GraphQLList(list_of_type) return list_type if is_field_optional else GraphQLNonNull( list_type) annotation_origin = getattr(annotation, "__origin__", None) if annotation_origin == AsyncGenerator: # async generators are used in subscription, we only need the yield type # https://docs.python.org/3/library/typing.html#typing.AsyncGenerator return get_graphql_type_for_annotation(annotation.__args__[0], field_name) elif is_union(annotation): types = annotation.__args__ non_none_types = [x for x in types if x != None.__class__] # noqa:E711 # optionals are represented as Union[type, None] if len(non_none_types) == 1: is_field_optional = True graphql_type = get_graphql_type_for_annotation( non_none_types[0], field_name, force_optional=True) else: is_field_optional = None.__class__ in types graphql_type = union(field_name, types).graphql_type else: graphql_type = get_type_for_annotation(annotation) if not graphql_type: raise ValueError(f"Unable to get GraphQL type for {annotation}") if is_field_optional: return graphql_type return GraphQLNonNull(graphql_type)
def _common_subtype(left, right): if left == right: return left elif isinstance(left, GraphQLNonNull) and isinstance( right, GraphQLNonNull): return GraphQLNonNull(_common_subtype(left.of_type, right.of_type)) elif isinstance(left, GraphQLNonNull): return GraphQLNonNull(_common_subtype(left.of_type, right)) elif isinstance(right, GraphQLNonNull): return GraphQLNonNull(_common_subtype(left, right.of_type)) elif isinstance(left, GraphQLList) and isinstance(right, GraphQLList): return GraphQLList(_common_subtype(left.of_type, right.of_type)) elif isinstance(left, GraphQLObjectType) and isinstance( right, GraphQLObjectType): fields = dict((field_name, _common_subfield(left.fields.get(field_name), right.fields.get(field_name))) for field_name in set(left.fields.keys()) | set(right.fields.keys())) return GraphQLObjectType(left.name, fields=fields) elif isinstance(left, GraphQLSchema) and isinstance(right, GraphQLSchema): return GraphQLSchema(query=_common_subtype(left.get_query_type(), right.get_query_type())) else: raise ValueError("Cannot find common subtype")
def make_mutation_args(model: DeclarativeMeta, inputs: Inputs, mutation_type: str) -> GraphQLArgumentMap: args = { "insert": { "objects": GraphQLArgument(GraphQLNonNull(GraphQLList(GraphQLNonNull(get_input_type(model, inputs, "insert_input"))))), "on_conflict": GraphQLArgument(get_input_type(model, inputs, "on_conflict")), }, "insert_one": { "object": GraphQLArgument(get_input_type(model, inputs, "insert_input")), "on_conflict": GraphQLArgument(get_input_type(model, inputs, "on_conflict")), }, "update": { **({"_inc": GraphQLArgument(get_input_type(model, inputs, "inc_input"))} if has_int(model) else {}), "_set": GraphQLArgument(get_input_type(model, inputs, "set_input")), "where": GraphQLArgument(get_input_type(model, inputs, "where")), }, "update_by_pk": { **({"_inc": GraphQLArgument(get_input_type(model, inputs, "inc_input"))} if has_int(model) else {}), "_set": GraphQLArgument(get_input_type(model, inputs, "set_input")), **make_pk_args(model), }, "delete": { "where": GraphQLArgument(get_input_type(model, inputs, "where")), }, "delete_by_pk": { **make_pk_args(model), }, } return args[mutation_type]
def get_fields(self, typename): fields = OrderedDict() if typename == 'Query': for name, gqltype in sorted(self._gql_interfaces.items(), key=lambda x: x[1].name): if name in TOP_LEVEL_TYPES: continue fields[gqltype.name] = GraphQLField( GraphQLList(GraphQLNonNull(gqltype)), args=self._get_query_args(name), ) elif typename == 'Mutation': for name, gqltype in sorted(self._gql_objtypes.items(), key=lambda x: x[1].name): if name in TOP_LEVEL_TYPES: continue gname = self.get_gql_name(name) fields[f'delete_{gname}'] = GraphQLField( GraphQLList(GraphQLNonNull(gqltype)), args=self._get_query_args(name), ) fields[f'insert_{gname}'] = GraphQLField( GraphQLList(GraphQLNonNull(gqltype)), args=self._get_insert_args(name), ) for name, gqltype in sorted(self._gql_interfaces.items(), key=lambda x: x[1].name): if (name in TOP_LEVEL_TYPES or f'Update{name}' not in self._gql_inobjtypes): continue gname = self.get_gql_name(name) args = self._get_update_args(name) if args: fields[f'update_{gname}'] = GraphQLField( GraphQLList(GraphQLNonNull(gqltype)), args=args, ) else: edb_type = self.edb_schema.get(typename) pointers = edb_type.get_pointers(self.edb_schema) for name in sorted(pointers.keys(self.edb_schema)): if name == '__type__': continue ptr = edb_type.getptr(self.edb_schema, name) target = self._get_target(ptr) if target is not None: if ptr.get_target(self.edb_schema).is_object_type(): args = self._get_query_args( ptr.get_target(self.edb_schema).get_name( self.edb_schema)) else: args = None fields[name] = GraphQLField(target, args=args) return fields
def _create_service_field(self): self._service_type = GraphQLObjectType( name="_Service", fields={"sdl": GraphQLField(GraphQLNonNull(GraphQLString))}) self._service_field = GraphQLField( GraphQLNonNull(self._service_type), resolve=lambda _, info: {"sdl": print_schema(self)}, )
def _get_insert_args(self, typename): return { 'data': GraphQLArgument( GraphQLNonNull( GraphQLList( GraphQLNonNull( self._gql_inobjtypes[f'Insert{typename}'])))), }
def _get_entities_field(self, entity_type: GraphQLUnionType) -> GraphQLField: return GraphQLField( GraphQLNonNull(GraphQLList(entity_type)), args={ "representations": GraphQLArgument( GraphQLNonNull(GraphQLList(GraphQLNonNull(self.Any))) ) }, resolve=self.entities_resolver, )
def _translate_annotation_impl(self, ann: types.Annotation) -> GraphQLNamedType: if isinstance(ann, types.AList): return GraphQLNonNull( GraphQLList(self.translate_annotation(ann.of_type))) elif isinstance(ann, types.AOptional): return self.translate_annotation_unwrapped(ann.of_type) elif isinstance(ann, types.AUnion): return GraphQLNonNull(self.map_union(ann)) elif isinstance(ann, types.ANewType): return GraphQLNonNull(self.map_newtype(ann)) assert isinstance(ann, types.AClass) t = ann.t if issubclass(t, Object): return GraphQLNonNull(self.map_type(t)) elif issubclass(t, Interface): return GraphQLNonNull(self.map_interface(t)) elif dataclasses.is_dataclass(t): return GraphQLNonNull(self.map_input(t)) elif issubclass(t, enum.Enum): return GraphQLNonNull(self.map_enum(t)) elif t in BUILTIN_SCALARS: return GraphQLNonNull(BUILTIN_SCALARS[t]) elif t in self.py2gql_types: return GraphQLNonNull(self.py2gql_types[t]) raise SchemaError(f"""Cannot translate {t}. Suggestions: - Did you forget to inherit Object? - Did you forget to add its scalar mapper to the `scalars` list?""")
def make_insert_args(model: DeclarativeMeta, inputs: Inputs) -> GraphQLArgumentMap: return { "objects": GraphQLArgument( GraphQLNonNull( GraphQLList( GraphQLNonNull(get_insert_input_type(model, inputs))))), "on_conflict": GraphQLArgument(ON_CONFLICT_INPUT), }
def get_filter_fields(self, typename, nested=False): selftype = self._gql_inobjtypes[typename] fields = OrderedDict() if not nested: fields['and'] = GraphQLInputObjectField( GraphQLList(GraphQLNonNull(selftype))) fields['or'] = GraphQLInputObjectField( GraphQLList(GraphQLNonNull(selftype))) fields['not'] = GraphQLInputObjectField(selftype) edb_type = self.edb_schema.get(typename) pointers = edb_type.get_pointers(self.edb_schema) names = sorted(pointers.keys(self.edb_schema)) for name in names: if name == '__type__': continue if name in fields: raise g_errors.GraphQLCoreError( f"{name!r} of {typename} clashes with special " "reserved fields required for GraphQL conversion" ) ptr = edb_type.getptr(self.edb_schema, name) edb_target = ptr.get_target(self.edb_schema) if edb_target.is_object_type(): t_name = edb_target.get_name(self.edb_schema) gql_name = self.get_input_name( 'NestedFilter', self.get_gql_name(t_name)) intype = self._gql_inobjtypes.get(gql_name) if intype is None: # construct a nested insert type intype = GraphQLInputObjectType( name=gql_name, fields=partial(self.get_filter_fields, t_name, True), ) self._gql_inobjtypes[gql_name] = intype elif not edb_target.is_scalar(): continue else: target = self._convert_edb_type(edb_target) if target is None: # don't expose this continue intype = self._gql_inobjtypes.get(f'Filter{target.name}') if intype: fields[name] = GraphQLInputObjectField(intype) return fields
def get_base_comparison_fields(graphql_type: Union[GraphQLScalarType, GraphQLList]) -> Dict[str, GraphQLInputField]: return { "_eq": GraphQLInputField(graphql_type), "_neq": GraphQLInputField(graphql_type), "_in": GraphQLInputField(GraphQLList(GraphQLNonNull(graphql_type))), "_nin": GraphQLInputField(GraphQLList(GraphQLNonNull(graphql_type))), "_lt": GraphQLInputField(graphql_type), "_gt": GraphQLInputField(graphql_type), "_gte": GraphQLInputField(graphql_type), "_lte": GraphQLInputField(graphql_type), "_is_null": GraphQLInputField(GraphQLBoolean), }
def test_deserialize_lists(self) -> None: # Non-collection with self.assertRaises(GraphQLInvalidArgumentError): deserialize_argument("numbers", GraphQLList(GraphQLInt), 1) # Tuple with self.assertRaises(GraphQLInvalidArgumentError): deserialize_argument("numbers", GraphQLList(GraphQLInt), (1, 2)) # Second element is of unexpected kind. with self.assertRaises(GraphQLInvalidArgumentError): deserialize_argument("numbers", GraphQLList(GraphQLInt), (1, 1.2, 3)) # Second element is "unparseable". with self.assertRaises(GraphQLInvalidArgumentError): deserialize_argument("numbers", GraphQLList(GraphQLInt), (1, "asda", 3)) # Basic self.assertEqual( [1.2, 2.3], deserialize_argument("numbers", GraphQLList(GraphQLFloat), [1.2, 2.3]) ) # With empty list self.assertEqual([], deserialize_argument("numbers", GraphQLList(GraphQLFloat), [])) # With list with one element self.assertEqual([1.2], deserialize_argument("numbers", GraphQLList(GraphQLFloat), [1.2])) # With outer null wrapper. self.assertEqual( [1.2, 2.3], deserialize_argument("numbers", GraphQLNonNull(GraphQLList(GraphQLFloat)), [1.2, 2.3]), ) # With inner null wrapper. self.assertEqual( [1.2, 2.3], deserialize_argument("numbers", GraphQLList(GraphQLNonNull(GraphQLFloat)), [1.2, 2.3]), ) # With outer and inner null wrapper. self.assertEqual( [1.2, 2.3], deserialize_argument( "numbers", GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLFloat))), [1.2, 2.3] ), ) # With custom scalar type self.assertEqual( [datetime.date(2014, 2, 5)], deserialize_argument("dates", GraphQLList(GraphQLDate), ["2014-02-05"]), )
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)
def node_definitions( fetch_by_id: Callable[[str, GraphQLResolveInfo], Any], type_resolver: Optional[GraphQLTypeResolver] = None, ) -> GraphQLNodeDefinitions: """ Given a function to map from an ID to an underlying object, and a function to map from an underlying object to the concrete GraphQLObjectType it corresponds to, constructs a `Node` interface that objects can implement, and a field object to be used as a `node` root field. If the type_resolver is omitted, object resolution on the interface will be handled with the `is_type_of` method on object types, as with any GraphQL interface without a provided `resolve_type` method. """ node_interface = GraphQLInterfaceType( "Node", description="An object with an ID", fields=lambda: { "id": GraphQLField(GraphQLNonNull(GraphQLID), description="The id of the object.") }, resolve_type=type_resolver, ) # noinspection PyShadowingBuiltins node_field = GraphQLField( node_interface, description="Fetches an object given its ID", args={ "id": GraphQLArgument(GraphQLNonNull(GraphQLID), description="The ID of an object") }, resolve=lambda _obj, info, id: fetch_by_id(id, info), ) nodes_field = GraphQLField( GraphQLNonNull(GraphQLList(node_interface)), description="Fetches objects given their IDs", args={ "ids": GraphQLArgument( GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLID))), description="The IDs of objects", ) }, resolve=lambda _obj, info, ids: [fetch_by_id(id_, info) for id_ in ids], ) return GraphQLNodeDefinitions(node_interface, node_field, nodes_field)
def build_mutation_response_type(model: DeclarativeMeta, objects: Objects) -> GraphQLObjectType: type_name = get_field_name(model, "mutation_response") object_type = objects[get_table_name(model)] fields = { "affected_rows": GraphQLField(GraphQLNonNull(GraphQLInt)), "returning": GraphQLField(GraphQLNonNull(GraphQLList(GraphQLNonNull(object_type)))), } return GraphQLObjectType(type_name, fields)
def _get_insert_args(self, typename): # The data can only be a specific non-interface type, if no # such type exists, skip it as we cannot accept unambiguous # data input. It's still possible to just select some existing # data. intype = self._gql_inobjtypes.get(f'Insert{typename}') if intype is None: return {} return { 'data': GraphQLArgument( GraphQLNonNull(GraphQLList(GraphQLNonNull(intype)))), }
def test_non_null_types_pass_validation(self): type_and_value = [ (GraphQLString, 'abc'), # sanity check (GraphQLNonNull(GraphQLString), 'abc'), (GraphQLList(GraphQLString), ['a', 'b', 'c']), # sanity check (GraphQLList(GraphQLNonNull(GraphQLString)), ['a', 'b', 'c']), (GraphQLNonNull(GraphQLList(GraphQLString)), ['a', 'b', 'c']), (GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLString))), ['a', 'b', 'c']), ] for graphql_type, value in type_and_value: validate_argument_type(graphql_type, value)
def test_non_null_types_pass_validation(self) -> None: type_and_value: List[Tuple[QueryArgumentGraphQLType, Any]] = [ (GraphQLString, "abc"), # sanity check (GraphQLNonNull(GraphQLString), "abc"), (GraphQLList(GraphQLString), ["a", "b", "c"]), # sanity check (GraphQLList(GraphQLNonNull(GraphQLString)), ["a", "b", "c"]), (GraphQLNonNull(GraphQLList(GraphQLString)), ["a", "b", "c"]), (GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLString))), ["a", "b", "c"]), ] arbitrary_argument_name = "arbitrary_name" for graphql_type, value in type_and_value: validate_argument_type(arbitrary_argument_name, graphql_type, value)
def _get_target(self, ptr): edb_target = ptr.get_target(self.edb_schema) target = self._convert_edb_type(edb_target) if target is not None: # figure out any additional wrappers due to cardinality # and required flags if not ptr.singular(self.edb_schema): target = GraphQLList(GraphQLNonNull(target)) if ptr.get_required(self.edb_schema): target = GraphQLNonNull(target) return target
def get_args(): return { "old": GraphQLArgument( type=GraphQLNonNull(GraphQLString), description="Value of old character to replace", ), "new": GraphQLArgument( type=GraphQLNonNull(GraphQLString), description="Value of new character to replace", ), "count": GraphQLArgument( type=GraphQLInt, description="Value to returned str lenght" ), }
def get_conflict_type(model: DeclarativeMeta, inputs: Inputs) -> GraphQLInputObjectType: type_name = get_field_name(model, "on_conflict") if type_name in inputs: return inputs[type_name] fields = { "constraint": GraphQLInputField(GraphQLNonNull(get_constraint_enum(model))), "update_columns": GraphQLInputField(GraphQLNonNull(GraphQLList(GraphQLNonNull(get_update_column_enums(model))))), "where": GraphQLInputField(get_input_type(model, inputs, "where")), } input_type = GraphQLInputObjectType(type_name, fields) inputs[type_name] = input_type return input_type
def test_when_arg_is_missing_then_is_not_subtype(self): assert not is_subtype( GraphQLObjectType("Object", { "value": GraphQLField( type=GraphQLNonNull(GraphQLInt), args={}, ) }), GraphQLObjectType("Object", { "value": GraphQLField( type=GraphQLNonNull(GraphQLInt), args={"id": GraphQLArgument(type=GraphQLInt)}, ) }), )
def test_types_within_non_null_types_are_merged(self): self._assert_merge( [ GraphQLNonNull(GraphQLObjectType("Object", fields={ "id": GraphQLField(type=GraphQLInt), })), GraphQLNonNull(GraphQLObjectType("Object", fields={ "name": GraphQLField(type=GraphQLString), })), ], is_non_null(is_object_type(fields=has_entries({ "id": is_field(type=is_int), "name": is_field(type=is_string), }))), )
def relay_connection_type(node_type): base_name = node_type.name Edge = GraphQLObjectType( name=base_name + 'Edge', fields={ 'cursor': GraphQLField(type=GraphQLNonNull(GraphQLString)), 'node': GraphQLField(type=node_type), }) Connection = GraphQLObjectType( name=base_name + 'Connection', fields={ 'pageInfo': GraphQLField(type=GraphQLNonNull(PageInfoType)), 'edges': GraphQLField(type=GraphQLList(Edge)), }) return Connection
def test_when_field_has_extra_non_null_args_then_is_not_subtype(self): assert not is_subtype( GraphQLObjectType("Object", { "value": GraphQLField( type=GraphQLNonNull(GraphQLInt), args={"id": GraphQLArgument(type=GraphQLNonNull(GraphQLInt))}, ) }), GraphQLObjectType("Object", { "value": GraphQLField( type=GraphQLNonNull(GraphQLInt), args={}, ) }), )
def _get_target(self, ptr): edb_target = ptr.target target = self._convert_edb_type(edb_target) if target: # figure out any additional wrappers due to cardinality # and required flags if ptr.cardinality in {s_pointers.PointerCardinality.OneToMany, s_pointers.PointerCardinality.ManyToMany}: target = GraphQLList(GraphQLNonNull(target)) if ptr.required: target = GraphQLNonNull(target) return target
def test_when_field_has_arg_of_supertype_then_is_subtype(self): assert is_subtype( GraphQLObjectType("Object", { "value": GraphQLField( type=GraphQLNonNull(GraphQLInt), args={"id": GraphQLArgument(type=GraphQLInt)}, ) }), GraphQLObjectType("Object", { "value": GraphQLField( type=GraphQLNonNull(GraphQLInt), args={"id": GraphQLArgument(type=GraphQLNonNull(GraphQLInt))}, ) }), )