def test_value_error_is_raised_if_source_decorator_was_used_without_argument(): async def source(*_): yield "test" # pragma: no cover subscription = SubscriptionType() with pytest.raises(ValueError): subscription.source(source)
def test_attempt_bind_subscription_to_undefined_field_raises_error(schema): async def source(*_): yield "test" # pragma: no cover subscription = SubscriptionType() subscription.set_source("fake", source) with pytest.raises(ValueError): subscription.bind_to_schema(schema)
def test_field_source_can_be_set_using_decorator(schema): async def source(*_): yield "test" # pragma: no cover subscription = SubscriptionType() subscription.source("message")(source) subscription.bind_to_schema(schema) field = schema.type_map.get("Subscription").fields["message"] assert field.subscribe is source
def start(self): query = QueryType() query.set_field('echo', self._echo_resolver()) mutation = MutationType() mutation.set_field('createTable', self._create_table_resolver()) mutation.set_field('addPlayer', self._add_player_resolver()) mutation.set_field('takeAction', self._take_action_resolver()) mutation.set_field('startGame', self._start_game_resolver()) mutation.set_field('stopGame', self._stop_game_resolver()) mutation.set_field('reset', self._reset_resolver()) subscription = SubscriptionType() subscription.set_field('subscribe', self._subscribe_resolver()) subscription.set_source('subscribe', self._subscribe_generator()) resolvers = [query, mutation, subscription] executable_schema = make_executable_schema(self._schema_str, resolvers) self._app = GraphQL(executable_schema, debug=True)
import os import os.path import pathlib import asyncio from graphql import parse from ariadne import MutationType, SubscriptionType, load_schema_from_path from nomine.models import Namer from nomine.models.folder_entry import FolderEntry, FILE_TYPE, DIRECTORY_TYPE mutation = MutationType() subscription = SubscriptionType() bindables = [mutation, subscription] schema_file = load_schema_from_path( pathlib.Path(__file__).parent.joinpath("schema.graphql").absolute()) schema = parse(schema_file) queue = asyncio.Queue() def get_or_create_entry(session, folder_id, dir_entry): if dir_entry.is_file(): entry_type = FILE_TYPE elif dir_entry.is_dir(): entry_type = DIRECTORY_TYPE else: return existing = (session.query(FolderEntry).filter( FolderEntry.path == dir_entry.path).first()) if existing:
def subscriptions(): subscription = SubscriptionType() subscription.set_source("ping", ping_generator) subscription.set_source("resolverError", ping_generator) subscription.set_field("resolverError", resolve_error) subscription.set_source("sourceError", error_generator) subscription.set_source("testContext", test_context_generator) subscription.set_source("testRoot", test_root_generator) return subscription
pathlib.Path(__file__).parent.absolute().joinpath("graphql", "schema.graphql") ) type_defs = load_schema_from_path("saltapi/graphql/schema.graphql") datetime_scalar = ScalarType("Datetime") datetime_scalar.set_serializer(scalars.serialize_datetime) datetime_scalar.set_value_parser(scalars.parse_datetime) proposal_code_scalar = ScalarType("ProposalCode") proposal_code_scalar.set_serializer(scalars.serialize_proposal_code) proposal_code_scalar.set_value_parser(scalars.parse_proposal_code) mutation = MutationType() mutation.set_field("submitProposal", resolvers.resolve_submit_proposal) subscription = SubscriptionType() subscription.set_field("submissionProgress", resolvers.resolve_submission_progress) subscription.set_source("submissionProgress", resolvers.submission_progress_generator) schema = make_executable_schema( type_defs, datetime_scalar, proposal_code_scalar, mutation, subscription, directives={"permittedFor": PermittedForDirective}, ) # non-GraphQL routes
def subscriptions(): subs = SubscriptionType() subs.source("ping")(ping) return subs
class GraphAPI: query: QueryType = QueryType() subscription = SubscriptionType() mutations = MutationType() def __init__(self, core: "Core"): self.core = core self.type_defs = load_schema_from_path(f"{core.location}/api/schema/") # self.add_query("spotifyProgress: String!", self.type_defs) self.query.set_field(CORE_VERSION, lambda *_: core.version) self.query.set_field(PLUGIN_VERSION, lambda *_: VERSION) self.query.set_field(PLUGINS, lambda *_: list(core.plugins.keys())) self.query.set_field(VALUE, self.get_value) self.query.set_field(AVAILABLE_COMPONENTS, lambda *_: list(core.registry.components.keys())) self.query.set_field( AVAILABLE_FORMATTER, lambda *_: list(map(lambda x: x.gql(), core.io.formatter)), ) self.query.set_field(COMMIT_ID, lambda *_: os.getenv("COMMIT", "unknown")) self.query.set_field(ENTITY, self.get_entity) self.query.set_field(ENTITIES, self.get_entities) self.subscription.set_field(ENTITY, self.entity_subscription) self.subscription.set_source(ENTITY, self.entity_subscription_source) self.subscription.set_field(VALUE, self.value_subscription) self.subscription.set_source(VALUE, self.value_subscription_source) self.subscription.set_field(EVENT, self.event_subscription) self.subscription.set_source(EVENT, self.event_subscription_source) self.mutations.set_field(SET_COMPONENT, self.set_mutation) self.mutations.set_field(ACTIVATE_SCENE, self.activate_scene) def add_query(self, query_def: str, resolver: Callable): query_definition: str = re.findall("(?=type Query)([\s\S]*?)(?<=})", self.type_defs)[0] self.type_defs = self.type_defs.replace(query_definition, "") lines = query_definition.split("\n") lines.insert(1, query_def) query_definition = "\n".join(lines) name = query_def.split("(")[0].split(":")[0].strip() self.query.set_field(name, resolver) self.type_defs += f"\n{query_definition}" def add_mutation(self, mutation_def: str, handler: Callable): mutations: str = re.findall("(?=type Mutation)([\s\S]*?)(?<=})", self.type_defs)[0] self.type_defs = self.type_defs.replace(mutations, "") lines = mutations.split("\n") lines.insert(1, mutation_def) mutations = "\n".join(lines) name = mutation_def.split("(")[0].split(":")[0].strip() self.mutations.set_field(name, handler) self.type_defs += f"\n{mutations}" async def setup(self): schema = make_executable_schema(self.type_defs, self.query, self.subscription, self.mutations) app = CORSMiddleware( GraphQL(schema), allow_origins=["*"], allow_methods=("GET", "POST", "OPTIONS"), ) conf = Config() conf.bind = ["0.0.0.0:8006"] conf.loglevel = "fatal" # to suppress lifespan error LOGGER.info("starting GQL API") try: # also to suppress lifespan error await serve(app, conf) except Exception as e: print(e) # ----------- QUERIES ----------- def get_entity(self, _, __, name: str): entity = self.core.registry.get_entities()[name] return entity.gql() def get_entities(self, *_): entities = self.core.registry.get_entities() return map(lambda e: e.gql(), entities.values()) def get_value(self, *_, key=""): v = self.core.storage.get_value(key) return v if v in BASE_TYPES else default_encoder(v) # ----------- SUBSCRIPTIONS ----------- async def entity_subscription_source(self, *_, name=""): async for entity in self.core.registry.state_queue.subscribe(): if entity.name != name: continue yield entity.gql() @staticmethod def entity_subscription(entity, *_, name=""): return entity async def value_subscription_source(self, *_, key=""): value = self.core.storage.get_value(key) if type(value) not in BASE_TYPES: value = default_encoder(value) yield value async for value in self.core.storage.subscribe(key): if type(value) not in BASE_TYPES: value = default_encoder(value) yield value @staticmethod def value_subscription(value, *_, key=""): return value @staticmethod def event_subscription(value, *_): return value async def event_subscription_source(self, *_): async for event in self.core.bus.event_stream.subscribe( ): # type: Event yield event.gql() # ----------- MUTATIONS ----------- def set_mutation(self, _, info, entity, component, target): self.core.registry.call_method(entity, component, "set", target, context=Context.admin(external=True)) return True def activate_scene(self, _, info, scene): self.core.registry.activate_scene(scene) return True
from ariadne.executable_schema import make_executable_schema from graphql.pyutils import EventEmitter, EventEmitterAsyncIterator from starlette.applications import Starlette from tortoise.exceptions import DoesNotExist from .models import Post, User, close_connections, init_db APP = Starlette() SCHEMA = load_schema_from_path(os.path.join("example_site", "schema.graphql")) MUTATION = ObjectType("Mutation") PUBSUB = EventEmitter() QUERY = ObjectType("Query") POST = ObjectType("Post") SUBSCRIPTION = SubscriptionType() @QUERY.field("users") async def get_all_users(_root, _info): """return list of all users""" results = await User.all() return results @QUERY.field("posts") async def get_all_posts(_root, _info): """return a list of all posts""" results = await Post.all() return results
from ariadne import SubscriptionType from aiostream import stream import rpc_pb2 as ln from context import LND, PUBSUB from models import Invoice from classes.user import User from helpers.mixins import LoggerMixin _sub_logger = LoggerMixin() INVOICE = SubscriptionType() @INVOICE.source("invoice") async def r_invoice_gen(user: User, *_): # create new new pub sub client for streaming locally paid invoices local_stream = PUBSUB.add_client(user.username) # create stream for remotely paid invoices remote_stream = await LND.stub.SubscribeInvoices(ln.InvoiceSubscription()) global_stream = stream.merge(local_stream, remote_stream) async with global_stream.stream() as streamer: async for response in streamer: try: # check if response if from lnd # external payment or pubsub - local payment if isinstance(response, Invoice): # invoice model received from pubsub client # yield and default resolver will retrieve requested fields