async def test_generate_multiple_objects(self): graphql = await hasura.insert( "flow", objects=[{ "a": 1 }, { "b": "2" }], selection_set={"returning": {"id"}}, run_mutation=False, ) expected_query = """ insert: insert_flow(objects: $insert_objects) { returning { id } } """ assert parse_graphql( graphql["query"]) == dedent(expected_query).strip() assert graphql["variables"] == [ Variable( name="insert_objects", type="[flow_insert_input!]!", value=[{ "a": 1 }, { "b": "2" }], ) ]
def graphql(self, query: Any, raise_on_error: bool = True, **variables: Union[bool, dict, str, int]) -> GraphQLResult: """ Convenience function for running queries against the Prefect GraphQL API Args: - query (Any): A representation of a graphql query to be executed. It will be parsed by prefect.utilities.graphql.parse_graphql(). - raise_on_error (bool): if True, a `ClientError` will be raised if the GraphQL returns any `errors`. - **variables (kwarg): Variables to be filled into a query with the key being equivalent to the variables that are accepted by the query Returns: - dict: Data returned from the GraphQL query Raises: - ClientError if there are errors raised by the GraphQL mutation """ result = self.post( path="", query=parse_graphql(query), variables=json.dumps(variables), server=self.graphql_server, ) if raise_on_error and "errors" in result: raise ClientError(result["errors"]) else: return as_nested_dict(result, GraphQLResult) # type: ignore
def test_parse_graphql_dedents_and_strips(): query = """ hi there """ assert parse_graphql(query) == "hi\n there"
async def test_on_conflict_string(self): graphql = await hasura_client.insert( "tenant", objects=[], on_conflict="{ constraint: pk1 }", run_mutation=False ) expected_query = """ insert: insert_tenant(objects: $insert_objects, on_conflict: { constraint: pk1 }) { affected_rows } """ assert parse_graphql(graphql["query"]) == dedent(expected_query).strip()
async def test_generate_selection_set(self): graphql = await hasura_client.insert( "tenant", objects=[], selection_set="affected_rows", run_mutation=False ) expected_query = """ insert: insert_tenant(objects: $insert_objects) { affected_rows } """ assert parse_graphql(graphql["query"]) == dedent(expected_query).strip()
async def test_generate_gql_delete_tenants(self): graphql = await hasura_client.delete("tenant", where={}, run_mutation=False) expected_query = """ delete: delete_tenant(where: $delete_where) { affected_rows } """ assert parse_graphql(graphql["query"]) == dedent(expected_query).strip() assert graphql["variables"] == [ Variable(name="delete_where", type="tenant_bool_exp!", value={}) ]
async def test_generate_gql_insert_tenants(self): graphql = await hasura_client.insert("tenant", objects=[], run_mutation=False) expected_query = """ insert: insert_tenant(objects: $insert_objects) { affected_rows } """ expected_defs = "$insert_objects: [tenant_insert_input!]!" assert parse_graphql(graphql["query"]) == dedent(expected_query).strip() assert graphql["variables"] == [ Variable(name="insert_objects", type="[tenant_insert_input!]!", value=[]) ]
async def test_generate_gql_delete_tenants_selection_set(self): graphql = await hasura_client.delete( "tenant", where={}, selection_set={"returning": {"id"}}, run_mutation=False ) expected_query = """ delete: delete_tenant(where: $delete_where) { returning { id } } """ assert parse_graphql(graphql["query"]) == dedent(expected_query).strip()
async def test_generate_gql_update_tenants_increment(self): graphql = await hasura_client.update( "tenant", where={}, increment={"age": 1}, run_mutation=False ) expected_query = """ update: update_tenant(where: $update_where, _inc: $update_inc) { affected_rows } """ assert parse_graphql(graphql["query"]) == dedent(expected_query).strip() assert graphql["variables"] == [ Variable(name="update_where", type="tenant_bool_exp!", value={}), Variable(name="update_inc", type="tenant_inc_input", value={"age": 1}), ]
async def test_alias(self): graphql = await hasura.insert("flow", objects=[], alias="x", run_mutation=False) expected_query = """ x: insert_flow(objects: $x_objects) { affected_rows } """ assert parse_graphql( graphql["query"]) == dedent(expected_query).strip() assert graphql["variables"] == [ Variable(name="x_objects", type="[flow_insert_input!]!", value=[]) ]
async def test_alias(self): graphql = await hasura.delete("flow", where={}, alias="x", run_mutation=False) expected_query = """ x: delete_flow(where: $x_where) { affected_rows } """ assert parse_graphql( graphql["query"]) == dedent(expected_query).strip() assert graphql["variables"] == [ Variable(name="x_where", type="flow_bool_exp!", value={}) ]
async def test_generate_selection_set_returning(self): graphql = await hasura_client.insert( "tenant", objects=[], selection_set={"returning": {"id"}}, run_mutation=False, ) expected_query = """ insert: insert_tenant(objects: $insert_objects) { returning { id } } """ assert parse_graphql(graphql["query"]) == dedent(expected_query).strip()
async def test_generate_gql_update_tenants_selection_set(self): graphql = await hasura_client.update( "tenant", where={}, set={"name": "x"}, selection_set={"returning": {"id"}}, run_mutation=False, ) expected_query = """ update: update_tenant(where: $update_where, _set: $update_set) { returning { id } } """ assert parse_graphql(graphql["query"]) == dedent(expected_query).strip()
def graphql( self, query: Any, raise_on_error: bool = True, headers: Dict[str, str] = None, variables: Dict[str, JSONLike] = None, token: str = None, ) -> GraphQLResult: """ Convenience function for running queries against the Prefect GraphQL API Args: - query (Any): A representation of a graphql query to be executed. It will be parsed by prefect.utilities.graphql.parse_graphql(). - raise_on_error (bool): if True, a `ClientError` will be raised if the GraphQL returns any `errors`. - headers (dict): any additional headers that should be passed as part of the request - variables (dict): Variables to be filled into a query with the key being equivalent to the variables that are accepted by the query - token (str): an auth token. If not supplied, the `client.access_token` is used. Returns: - dict: Data returned from the GraphQL query Raises: - ClientError if there are errors raised by the GraphQL mutation """ result = self.post( path="", server=self.api_server, headers=headers, params=dict(query=parse_graphql(query), variables=json.dumps(variables)), token=token, ) if raise_on_error and "errors" in result: if "UNAUTHENTICATED" in str(result["errors"]): raise AuthorizationError(result["errors"]) elif "Malformed Authorization header" in str(result["errors"]): raise AuthorizationError(result["errors"]) raise ClientError(result["errors"]) else: return GraphQLResult(result) # type: ignore
async def test_on_conflict(self): graphql = await hasura_client.insert( "tenant", objects=[], on_conflict={"constraint": "pk1"}, run_mutation=False ) expected_query = """ insert: insert_tenant(objects: $insert_objects, on_conflict: $insert_on_conflict) { affected_rows } """ assert parse_graphql(graphql["query"]) == dedent(expected_query).strip() assert graphql["variables"] == [ Variable(name="insert_objects", type="[tenant_insert_input!]!", value=[]), Variable( name="insert_on_conflict", type="tenant_on_conflict", value={"constraint": "pk1"}, ), ]
async def test_generate_gql_update_flows_set(self): graphql = await hasura.update("flow", where={}, set={"name": "x"}, run_mutation=False) expected_query = """ update: update_flow(where: $update_where, _set: $update_set) { affected_rows } """ assert parse_graphql( graphql["query"]) == dedent(expected_query).strip() assert graphql["variables"] == [ Variable(name="update_where", type="flow_bool_exp!", value={}), Variable(name="update_set", type="flow_set_input", value={"name": "x"}), ]
async def test_generate_gql_update_tenants_delete_elem(self): graphql = await hasura_client.update("tenant", where={}, delete_elem={"settings": 2}, run_mutation=False) expected_query = """ update: update_tenant(where: $update_where, _delete_elem: $update_delete_elem) { affected_rows } """ assert parse_graphql( graphql["query"]) == dedent(expected_query).strip() assert graphql["variables"] == [ Variable(name="update_where", type="tenant_bool_exp!", value={}), Variable( name="update_delete_elem", type="tenant_delete_elem_input", value={"settings": 2}, ), ]
async def test_alias(self): graphql = await hasura_client.update( "tenant", where={}, alias="x", set={"name": "x"}, increment={"age": 1}, run_mutation=False, ) expected_query = """ x: update_tenant(where: $x_where, _set: $x_set, _inc: $x_inc) { affected_rows } """ assert parse_graphql(graphql["query"]) == dedent(expected_query).strip() assert graphql["variables"] == [ Variable(name="x_where", type="tenant_bool_exp!", value={}), Variable(name="x_set", type="tenant_set_input", value={"name": "x"}), Variable(name="x_inc", type="tenant_inc_input", value={"age": 1}), ]
def graphql( self, query: Any, raise_on_error: bool = True, headers: Dict[str, str] = None, variables: Dict[str, JSONLike] = None, ) -> GraphQLResult: """ Convenience function for running queries against the Prefect GraphQL API Args: - query (Any): A representation of a graphql query to be executed. It will be parsed by prefect.utilities.graphql.parse_graphql(). - raise_on_error (bool): if True, a `ClientError` will be raised if the GraphQL returns any `errors`. - headers (dict): any additional headers that should be passed as part of the request - variables (dict): Variables to be filled into a query with the key being equivalent to the variables that are accepted by the query Returns: - dict: Data returned from the GraphQL query Raises: - ClientError if there are errors raised by the GraphQL mutation """ result = self.post( path="", server=self.graphql_server, headers=headers, params=dict(query=parse_graphql(query), variables=json.dumps(variables)), ) if raise_on_error and "errors" in result: raise ClientError(result["errors"]) else: return as_nested_dict(result, GraphQLResult) # type: ignore
async def execute( self, query: Union[str, Dict[str, Any]], variables: Dict[str, Any] = None, headers: dict = None, raise_on_error: bool = True, as_box=True, ) -> dict: """ Args: - query (Union[str, dict]): either a GraphQL query string or objects that are compatible with prefect.utilities.graphql.parse_graphql(). - variables (dict): GraphQL variables - headers (dict): Headers to include with the GraphQL request - raise_on_error (bool): if True, a `ValueError` is raised whenever the GraphQL result contains an `errors` field. - as_box (bool): if True, a `box.Box` object is returned, which behaves like a dict but allows "dot" access in addition to key access. Returns: - dict: a dictionary of GraphQL info. If `as_box` is True, it will be a Box (dict subclass) Raises: - GraphQLSyntaxError: if the provided query is not a valid GraphQL query - ValueError: if `raise_on_error=True` and there are any errors during execution. """ if not isinstance(query, str): query = parse_graphql(query) # validate query if prefect_server.config.debug: ariadne.gql(query) # timeout of 30 seconds response = await httpx.post( self.url, json=dict(query=query, variables=variables or {}), headers=headers or self.headers, timeout=30, ) try: result = response.json() except json.decoder.JSONDecodeError as exc: self.logger.error("JSON Decode Error on {}".format( response.content.decode())) self.logger.error(exc) raise exc if raise_on_error and "errors" in result: if prefect_server.config.debug: self.log_query_debug_info( query=query, variables=variables or {}, errors=result["errors"], headers=headers or self.headers, ) raise ValueError(result["errors"]) # convert to box if as_box: result = Box(result) return result
def verify(query, expected): assert parse_graphql(query) == dedent(expected).strip()