def build(self) -> GraphQLSchema: query = self.translate_annotation_unwrapped( types.AClass(None, self.query, origin=None)) mutation = self.translate_annotation_unwrapped( types.AClass(None, self.mutation, origin=None)) if self.mutation else None # Interface implementations may not have been explicitly referenced in # the schema. But their interface must have been--so we want to # traverse all interfaces, find their subclasses and explicitly supply # them to the schema. # # To traverse all instances, we hackily construct a temporary schema, # then check self.type_map to see what the schema found. tmp_schema = GraphQLSchema( query=query, mutation=mutation, ) extra_types = [] for interface in list(self.type_map): if isinstance(interface, type) and issubclass( interface, Interface): for impl in interface.__subclasses__(): ann = types.AClass(None, impl, origin=None) extra_types.append( self.translate_annotation_unwrapped(ann)) return GraphQLSchema(query=query, mutation=mutation, types=extra_types)
def test_when_query_is_not_subtype_then_schema_is_not_subtype(self): assert not is_subtype( GraphQLSchema( query=GraphQLObjectType("Object", fields={ "id": GraphQLField(type=GraphQLInt), }), ), GraphQLSchema( query=GraphQLObjectType("Object", fields={ "id": GraphQLField(type=GraphQLInt), "name": GraphQLField(type=GraphQLString), }), ), )
def test_accepts_type_definition_with_sync_subscribe_function(): SubscriptionType = GraphQLObjectType( name='Subscription', fields=OrderedDict([ ('importantEmail', GraphQLField( EmailEventType, resolver=lambda *_: AsyncObservable.from_iterable([None]))), ])) test_schema = GraphQLSchema(query=QueryType, subscription=SubscriptionType) stream = AsyncSingleStream() send_important_email, subscription = create_subscription( stream, test_schema) email = Email( from_='*****@*****.**', subject='Alright', message='Tests are good', unread=True, ) l = [] fut1 = asyncio.ensure_future(p_subscribe(l, stream)) fut2 = asyncio.ensure_future(send_important_email(email)) asyncio.get_event_loop().run_until_complete(asyncio.gather(fut1, fut2)) assert l # [0].data == {'importantEmail': None}
def test_accepts_multiple_subscription_fields_defined_in_schema(): SubscriptionTypeMultiple = GraphQLObjectType( name='Subscription', fields=OrderedDict([ ('importantEmail', GraphQLField(EmailEventType)), ('nonImportantEmail', GraphQLField(EmailEventType)), ])) test_schema = GraphQLSchema(query=QueryType, subscription=SubscriptionTypeMultiple) stream = AsyncSingleStream() send_important_email, subscription = create_subscription( stream, test_schema) email = Email( from_='*****@*****.**', subject='Alright', message='Tests are good', unread=True, ) l = [] fut1 = asyncio.ensure_future(p_subscribe(l, stream)) fut2 = asyncio.ensure_future(send_important_email(email)) asyncio.get_event_loop().run_until_complete(asyncio.gather(fut1, fut2)) assert l[0][0] == email
def email_schema_with_resolvers(resolve_fn=None): # type: (Callable) -> GraphQLSchema def default_resolver(root, info): # type: (Any, ResolveInfo) -> Union[Observable, Subject] func = getattr(root, "importantEmail", None) if func: func = get_unbound_function(func) return func() return Observable.empty() return GraphQLSchema( query=QueryType, subscription=GraphQLObjectType( name="Subscription", fields=OrderedDict( [ ( "importantEmail", GraphQLField( EmailEventType, resolver=resolve_fn or default_resolver ), ) ] ), ), )
def test_when_mutation_is_missing_then_is_not_subtype_of_schema_with_mutation(self): assert not is_subtype( GraphQLSchema( query=GraphQLObjectType("Object", fields={ "id": GraphQLField(type=GraphQLInt), }), ), GraphQLSchema( query=GraphQLObjectType("Object", fields={ "id": GraphQLField(type=GraphQLInt), }), mutation=GraphQLObjectType("Mutation", fields={ "id": GraphQLField(type=GraphQLInt), }), ), )
def test_accepts_type_definition_with_sync_subscribe_function(): # type: () -> None SubscriptionType = GraphQLObjectType( name="Subscription", fields=OrderedDict([( "importantEmail", GraphQLField(EmailEventType, resolver=lambda *_: Observable.from_([None])), )]), ) test_schema = GraphQLSchema(query=QueryType, subscription=SubscriptionType) stream = Subject() send_important_email, subscription = create_subscription( stream, test_schema) email = Email( from_="*****@*****.**", subject="Alright", message="Tests are good", unread=True, ) inbox = [] subscription.subscribe(inbox.append) send_important_email(email) assert len(inbox) == 1 assert inbox[0].data == {"importantEmail": None}
def __init__( self, # TODO: can we make sure we only allow to pass something that has been decorated? query: Type, mutation: Optional[Type] = None, subscription: Optional[Type] = None, directives=(), types=(), extensions: Sequence[Type[Extension]] = (), ): self.extensions = extensions self.type_map: Dict[str, ConcreteType] = {} query_type = get_object_type(query, self.type_map) mutation_type = get_object_type(mutation, self.type_map) if mutation else None subscription_type = (get_object_type(subscription, self.type_map) if subscription else None) self.middleware: List[Middleware] = [DirectivesMiddleware(directives)] directives = [ get_directive_type(directive, self.type_map) for directive in directives ] self._schema = GraphQLSchema( query=query_type, mutation=mutation_type, subscription=subscription_type if subscription else None, directives=specified_directives + directives, types=[get_object_type(type, self.type_map) for type in types], ) self.query = self.type_map[query_type.name]
def build_schema(base: DeclarativeMeta, enable_subscription: bool = False) -> GraphQLSchema: """ Args: base: enable_subscription: Returns: :class:`graphql:graphql.type.GraphQLSchema` """ queries: GraphQLFieldMap = {} mutations: GraphQLFieldMap = {} objects: Objects = {} inputs: Inputs = {} for model in base.__subclasses__(): build_queries(model, objects, queries, inputs) build_mutations(model, objects, mutations, inputs) return GraphQLSchema( GraphQLObjectType("Query", queries), GraphQLObjectType("Mutation", mutations), GraphQLObjectType("Subscription", {}) if enable_subscription else None, )
def test_accepts_type_definition_with_sync_subscribe_function(): SubscriptionType = GraphQLObjectType( name='Subscription', fields=OrderedDict([ ('importantEmail', GraphQLField(EmailEventType, resolver=lambda *_: Observable.from_([None]))), ])) test_schema = GraphQLSchema(query=QueryType, subscription=SubscriptionType) stream = Subject() send_important_email, subscription = create_subscription( stream, test_schema) email = Email( from_='*****@*****.**', subject='Alright', message='Tests are good', unread=True, ) l = [] subscription.subscribe(l.append) send_important_email(email) assert l # [0].data == {'importantEmail': None}
def update_schema_scalar(schema: GraphQLSchema, name: str, scalar: GraphQLScalarType): """Update the scalar in a schema with the scalar provided. :param schema: the GraphQL schema :param name: the name of the custom scalar type in the schema :param scalar: a provided scalar type This can be used to update the default Custom Scalar implementation when the schema has been provided from a text file or from introspection. """ if not isinstance(scalar, GraphQLScalarType): raise TypeError("Scalars should be instances of GraphQLScalarType.") schema_scalar = schema.get_type(name) if schema_scalar is None: raise KeyError(f"Scalar '{name}' not found in schema.") if not isinstance(schema_scalar, GraphQLScalarType): raise TypeError(f'The type "{name}" is not a GraphQLScalarType,' f" it is a {type(schema_scalar)}") # Update the conversion methods # Using setattr because mypy has a false positive # https://github.com/python/mypy/issues/2427 setattr(schema_scalar, "serialize", scalar.serialize) setattr(schema_scalar, "parse_value", scalar.parse_value) setattr(schema_scalar, "parse_literal", scalar.parse_literal)
def test_accepts_multiple_subscription_fields_defined_in_schema(): # type: () -> None SubscriptionTypeMultiple = GraphQLObjectType( name="Subscription", fields=OrderedDict([ ("importantEmail", GraphQLField(EmailEventType)), ("nonImportantEmail", GraphQLField(EmailEventType)), ]), ) test_schema = GraphQLSchema(query=QueryType, subscription=SubscriptionTypeMultiple) stream = Subject() send_important_email, subscription = create_subscription( stream, test_schema) email = Email( from_="*****@*****.**", subject="Alright", message="Tests are good", unread=True, ) inbox = [] stream.subscribe(inbox.append) send_important_email(email) assert len(inbox) == 1 assert inbox[0][0] == email
def __init__(self, edb_schema): '''Create a graphql schema based on edgedb schema.''' self.edb_schema = edb_schema # extract and sort modules to have a consistent type ordering self.modules = { m.name for m in self.edb_schema.get_modules() } - {'schema', 'graphql'} self.modules = list(self.modules) self.modules.sort() self._gql_interfaces = {} self._gql_objtypes = {} self._gql_inobjtypes = {} self._gql_ordertypes = {} self._define_types() query = self._gql_objtypes['Query'] = GraphQLObjectType( name='Query', fields=self.get_fields('Query'), ) # get a sorted list of types relevant for the Schema types = [ objt for name, objt in itertools.chain(self._gql_objtypes.items(), self._gql_inobjtypes.items()) # the Query is included separately if name != 'Query' ] types = sorted(types, key=lambda x: x.name) self._gql_schema = GraphQLSchema(query=query, types=types)
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 executor(root, mutation=None): if mutation is None: mutation_type = None else: mutation_type = _nullable(mutation.to_graphql_type()) default_schema = GraphQLSchema( query=_nullable(root.to_graphql_type()), mutation=mutation_type, ) def execute(query, variables=None, context=None, schema=None): if schema is None: schema = default_schema elif not is_subtype(default_schema, schema): raise ValueError( "schema argument must be superschema of main schema") return _execute( schema=schema, root=root, mutation=mutation, query=query, variables=variables, context=context, ) return execute
def test_non_nullable_list(self): PersonType = GraphQLObjectType( "Person", lambda: {"name": GraphQLField(GraphQLString)} ) schema = GraphQLSchema( query=GraphQLObjectType( name="RootQueryType", fields={ "people": GraphQLField( GraphQLList(GraphQLNonNull(PersonType)), resolve=lambda obj, info: {"name": "eran"}, ) }, ) ) query = """ query GetPeople { people { name } } """ parser = QueryParser(schema) dataclass_renderer = DataclassesRenderer(schema) parsed = parser.parse(query) rendered = dataclass_renderer.render(parsed) m = self.load_module(rendered) mock_client = MagicMock() mock_client.call = MagicMock( return_value=""" { "data": { "people": [ { "name": "eran" }, { "name": "eran1" } ] } } """ ) result = m.GetPeople.execute(mock_client) assert result assert isinstance(result, m.GetPeople.GetPeopleData) assert len(result.people) == 2 assert result.people[0].name == "eran" assert result.people[1].name == "eran1"
def generate_schema(self) -> GraphQLSchema: """ Finalizes the GraphQL schema by generating the GraphQLSchema :return: The finalized GraphQL schema """ query = GraphQLObjectType("Query", self.gql_queries) mutation = GraphQLObjectType("Mutation", self.gql_mutations) return GraphQLSchema(query, mutation)
def schema(): Query = GraphQLObjectType('Query', fields={ 'allInts': GraphQLField(GraphQLList(GraphQLInt), resolver=resolve_all_ints) }) return GraphQLSchema(Query)
def compile_schema(self, schema: Schema) -> GraphQLSchema: return GraphQLSchema( query=self._compile_object_or_none(schema.query), mutation=self._compile_object_or_none(schema.mutation), subscription=self._compile_object_or_none(schema.subscription), directives=schema.directives, types=self._compile_schema_types(schema.types), )
def test_batches_correctly(executor): Business = GraphQLObjectType( 'Business', lambda: { 'id': GraphQLField(GraphQLID, resolver=lambda root, info, **args: root), }) Query = GraphQLObjectType( 'Query', lambda: { 'getBusiness': GraphQLField(Business, args={ 'id': GraphQLArgument(GraphQLNonNull(GraphQLID)), }, resolver=lambda root, info, **args: info.context. business_data_loader.load(args.get('id'))), }) schema = GraphQLSchema(query=Query) doc = ''' { business1: getBusiness(id: "1") { id } business2: getBusiness(id: "2") { id } } ''' doc_ast = parse(doc) load_calls = [] class BusinessDataLoader(DataLoader): def batch_load_fn(self, keys): load_calls.append(keys) return Promise.resolve(keys) class Context(object): business_data_loader = BusinessDataLoader() result = execute(schema, doc_ast, None, context_value=Context(), executor=executor) assert not result.errors assert result.data == { 'business1': { 'id': '1' }, 'business2': { 'id': '2' }, } assert load_calls == [['1', '2']]
def test_batches_correctly(executor): # type: (SyncExecutor) -> None Business = GraphQLObjectType( "Business", lambda: { "id": GraphQLField(GraphQLID, resolver=lambda root, info, **args: root) }, ) Query = GraphQLObjectType( "Query", lambda: { "getBusiness": GraphQLField( Business, args={"id": GraphQLArgument(GraphQLNonNull(GraphQLID))}, resolver=lambda root, info, **args: info.context. business_data_loader.load(args.get("id")), ) }, ) schema = GraphQLSchema(query=Query) doc = """ { business1: getBusiness(id: "1") { id } business2: getBusiness(id: "2") { id } } """ doc_ast = parse(doc) load_calls = [] class BusinessDataLoader(DataLoader): def batch_load_fn(self, keys): # type: (List[str]) -> Promise load_calls.append(keys) return Promise.resolve(keys) class Context(object): business_data_loader = BusinessDataLoader() result = execute(schema, doc_ast, None, context_value=Context(), executor=executor) assert not result.errors assert result.data == {"business1": {"id": "1"}, "business2": {"id": "2"}} assert load_calls == [["1", "2"]]
def _bind_schema(self, schema: GraphQLSchema, solutions: List[Solution]) -> GraphQLSchema: solutions_map = {solution.type: solution for solution in solutions} custom_types = [type_ for type_ in schema.to_kwargs()['types'] if not type_.name.startswith('__')] for type_ in custom_types: if isinstance(type_, GraphQLObjectType): solution = solutions_map[type_.name] self.logger.debug(f' Binding solution: {solution} ') solution_type = schema.get_type(solution.type) assert solution_type fields = getattr(solution_type, 'fields', []) for name, field in fields.items(): field.resolve = solution.resolve(name) return schema
def schema(): Query = GraphQLObjectType('Query', fields={ 'allContainers': GraphQLField( GraphQLList(ContainerType), resolver=resolve_all_containers) }) return GraphQLSchema(Query)
def _make_executable(self, schema: GraphQLSchema): for type_name, fields in self.registry.items(): object_type = schema.get_type(type_name) for field_name, resolver_fn in fields.items(): field_definition = object_type.fields.get(field_name) if not field_definition: raise Exception(f'Invalid field {type_name}.{field_name}') field_definition.resolve = resolver_fn
def test_fast_introspection_validate_schema(self) -> None: execution_result = try_fast_introspection(GraphQLSchema(), introspection_query) self.assertIsNotNone(execution_result) if execution_result is not None: self.assertIsNone(execution_result.data) self.assertEqual( execution_result.errors, [GraphQLError("Query root type must be provided.")])
def resolve(self, next, root, info, **kwargs): # Block introspection in PROD to save time unless the param forceIntrospection=true is passed block_introspection = True or not R.prop_or(False, 'forceIntrospection', info.context.GET) and settings.PROD if block_introspection and info.field_name.lower() in ['__schema', '_introspection']: query = GraphQLObjectType( "Query", lambda: {"Introspection": GraphQLField(GraphQLString, resolver=lambda *_: "Disabled")} ) info.schema = GraphQLSchema(query=query) return next(root, info, **kwargs) return next(root, info, **kwargs)
def total(): Query = GraphQLObjectType('Query', fields={ 'allInts': GraphQLField(GraphQLList(GraphQLInt), resolver=resolve_all_ints) }) hello_schema = GraphQLSchema(Query) source = Source('{ allInts }') ast = parse(source) return partial(execute, hello_schema, ast)
def test_schema_queries_are_merged(self): self._assert_merge( [ GraphQLSchema( query=GraphQLObjectType("Object", fields={ "id": GraphQLField(type=GraphQLInt), }), ), GraphQLSchema( query=GraphQLObjectType("Object", fields={ "name": GraphQLField(type=GraphQLString), }), ), ], is_schema( query=is_object_type(fields=has_entries({ "id": is_field(type=is_int), "name": is_field(type=is_string), })), ), )
def __init__(self, edb_schema): '''Create a graphql schema based on edgedb schema.''' self.edb_schema = edb_schema # extract and sort modules to have a consistent type ordering self.modules = { m.get_name(self.edb_schema) for m in self.edb_schema.get_objects(type=s_mod.Module) } - HIDDEN_MODULES self.modules = list(self.modules) self.modules.sort() self._gql_interfaces = {} self._gql_objtypes = {} self._gql_inobjtypes = {} self._gql_ordertypes = {} self._gql_enums = {} self._define_types() query = self._gql_objtypes['Query'] = GraphQLObjectType( name='Query', fields=self.get_fields('Query'), ) # If a database only has abstract types and scalars, no # mutations will be possible (such as in a blank database), # but we would still want the reflection to work without # error, even if all that can be discovered through GraphQL # then is the schema. fields = self.get_fields('Mutation') if fields: mutation = self._gql_objtypes['Mutation'] = GraphQLObjectType( name='Mutation', fields=fields, ) else: mutation = None # get a sorted list of types relevant for the Schema types = [ objt for name, objt in itertools.chain( self._gql_objtypes.items(), self._gql_inobjtypes.items()) # the Query is included separately if name not in TOP_LEVEL_TYPES ] types = sorted(types, key=lambda x: x.name) self._gql_schema = GraphQLSchema(query=query, mutation=mutation, types=types) # this map is used for GQL -> EQL translator needs self._type_map = {}
def total(): Query = GraphQLObjectType('Query', fields={ 'allContainers': GraphQLField( GraphQLList(ContainerType), resolver=resolve_all_containers) }) hello_schema = GraphQLSchema(Query) source = Source('{ allContainers { x } }') ast = parse(source) result = partial(execute, hello_schema, ast)
def test_unicode_error_message(): ast = parse('query Example { unicode }') def resolver(context, *_): raise Exception(u'UNIÇODÉ!') Type = GraphQLObjectType( 'Type', { 'unicode': GraphQLField(GraphQLString, resolver=resolver), }) result = execute(GraphQLSchema(Type), ast) assert isinstance(result.errors[0], GraphQLLocatedError)