def login(token): """ Login to Prefect Cloud with an api token to use for Cloud communication. \b Options: --token, -t TEXT A Prefect Cloud api token [required] """ client = Client() client.login(api_token=token) # Verify login obtained a valid api token try: client.graphql(query={"query": "hello"}) except AuthorizationError: click.secho( "Error attempting to use Prefect API token {}".format(token), color="RED") return except ClientError: click.secho("Error attempting to communicate with Prefect Cloud", color="RED") return click.echo("Login successful")
def list_tokens(): """ DEPRECATED. Please use API keys instead. List your available Prefect Cloud API tokens. """ click.secho( "WARNING: API tokens are deprecated. Please consider removing your remaining " "tokens and using API keys instead.", fg="yellow", err=True, # Write to stderr in case the user is piping ) client = Client() output = client.graphql(query={"query": {"api_token": {"id", "name"}}}) if not output.get("data", None): click.secho("Unable to list API tokens", fg="red") return tokens = [] for item in output.data.api_token: tokens.append([item.name, item.id]) click.echo( tabulate( tokens, headers=["NAME", "ID"], tablefmt="plain", numalign="left", stralign="left", ))
def revoke_token(id): """ DEPRECATED. Please use API keys instead. Revote a Prefect Cloud API token \b Options: --id, -i TEXT The id of a token to revoke """ check_override_auth_token() client = Client() output = client.graphql( query={ "mutation($input: delete_api_token_input!)": { "delete_api_token(input: $input)": {"success"} } }, variables=dict(input=dict(token_id=id)), ) if not output.get("data", None) or not output.data.delete_api_token.success: click.secho("Unable to revoke token with ID {}".format(id), fg="red") return click.secho("Token successfully revoked", fg="green")
def add(token, config_path): """ Add a new Prefect Cloud auth token to use for Cloud communication. \b Options: --token, -t TEXT A Prefect Cloud auth token [required] --config-path, -c TEXT Path to a Prefect config.toml, defaults to `~/.prefect/config.toml` """ abs_directory = os.path.abspath(os.path.expanduser(config_path)) if not os.path.exists(abs_directory): click.secho("{} does not exist".format(config_path), fg="red") return config = toml.load(abs_directory) if not config.get("cloud"): config["cloud"] = {} config["cloud"]["auth_token"] = token with open(abs_directory, "w") as file: toml.dump(config, file) click.echo("Auth token added to Prefect config") client = Client() client.token = token result = client.graphql(query={"query": "hello"}) if not result.data.hello: click.secho( "Error attempting to use Prefect auth token {}".format(result))
def list_keys(): """ List available Prefect Cloud API keys. """ client = Client() response = client.graphql( query={ "query": { "auth_api_key": { "id": True, "name": True, "expires_at": True, } } }) keys = response.get("data", {}).get("auth_api_key") if keys is None: raise TerminalError( f"Unexpected response from Prefect Cloud: {response}") if not keys: click.secho("You have not created any API keys", fg="yellow") else: click.echo( tabulate( [(key.name, key.id, key.expires_at or "NEVER") for key in keys], headers=["NAME", "ID", "EXPIRES AT"], tablefmt="plain", numalign="left", stralign="left", ))
def list_tokens(): """ List your available Prefect Cloud API tokens. """ check_override_auth_token() client = Client() output = client.graphql(query={"query": {"api_token": {"id", "name"}}}) if not output.get("data", None): click.secho("Unable to list API tokens", fg="red") return tokens = [] for item in output.data.api_token: tokens.append([item.name, item.id]) click.echo( tabulate( tokens, headers=["NAME", "ID"], tablefmt="plain", numalign="left", stralign="left", ) )
def create_token(name, scope): """ Create a Prefect Cloud API token. For more info on API tokens visit https://docs.prefect.io/orchestration/concepts/api.html \b Options: --name, -n TEXT A name to give the generated token --scope, -s TEXT A scope for the token """ check_override_auth_token() client = Client() output = client.graphql( query={ "mutation($input: create_api_token_input!)": { "create_api_token(input: $input)": {"token"} } }, variables=dict(input=dict(name=name, scope=scope)), ) if not output.get("data", None): click.secho("Issue creating API token", fg="red") return click.echo(output.data.create_api_token.token)
def execute( # type: ignore self, storage: "Storage", flow_location: str, **kwargs: Any # type: ignore ) -> None: flow_run_info = None flow_run_id = prefect.context.get("flow_run_id") if self._on_execute: # If an on_execute Callable has been provided, retrieve the flow run parameters # and then allow the Callable a chance to update _provider_kwargs. This allows # better sizing of the cluster resources based on parameters for this Flow run. try: client = Client() flow_run_info = client.get_flow_run_info(flow_run_id) parameters = flow_run_info.parameters or {} # type: ignore self._on_execute(parameters, self._provider_kwargs) except Exception as exc: self.logger.info( "Failed to retrieve flow run info with error: {}".format( repr(exc))) if "image" not in self._provider_kwargs or not self._provider_kwargs.get( "image"): # If image is not specified, use the Flow's image so that dependencies are # identical on all containers: Flow runner, Dask scheduler, and Dask workers flow_id = prefect.context.get("flow_id") try: client = Client() if not flow_id: # We've observed cases where flow_id is None if not flow_run_info: flow_run_info = client.get_flow_run_info(flow_run_id) flow_id = flow_run_info.flow_id flow_info = client.graphql("""query { flow(where: {id: {_eq: "%s"}}) { storage } }""" % flow_id) storage_info = flow_info["data"]["flow"][0]["storage"] image = "{}/{}:{}".format( storage_info["registry_url"], storage_info["image_name"], storage_info["image_tag"], ) self.logger.info( "Using Flow's Docker image for Dask scheduler & workers: {}" .format(image)) self._provider_kwargs["image"] = image except Exception as exc: self.logger.info( "Failed to retrieve flow info with error: {}".format( repr(exc))) self._create_dask_cluster() self.logger.info( "Executing on dynamically created Dask Cluster with scheduler address: {}" .format(self.executor_kwargs["address"])) super().execute(storage, flow_location, **kwargs)
def _query_for_task_runs( where: dict, order_by: dict = None, error_on_empty: bool = True, ) -> List[dict]: """ Query for task run data necessary to initialize `TaskRunView` instances with `TaskRunView.from_task_run_data`. Args: - where (required): The Hasura `where` clause to filter by - order_by (optional): An optional Hasura `order_by` clause to order results by. - error_on_empty (optional): If `True` and no tasks are found, a `ValueError` will be raised. Returns: A list of dicts containing task run data """ client = Client() query_args = {"where": where} if order_by is not None: query_args["order_by"] = order_by query = { "query": { with_args("task_run", query_args): { "id": True, "name": True, "task": { "id": True, "slug": True }, "map_index": True, "serialized_state": True, "flow_run_id": True, } } } result = client.graphql(query) task_runs = result.get("data", {}).get("task_run", None) if task_runs is None: raise ValueError( f"Received bad result while querying for task runs where {where}: " f"{result}") if not task_runs and error_on_empty: raise ValueError( f"No task runs found while querying for task runs where {where}" ) return task_runs
def _query_for_tenants( where: dict, order_by: dict = None, error_on_empty: bool = True, ) -> List[dict]: """ Query for tenant data necessary to initialize `TenantView` instances with `TenantView._from_tenant_data`. Args: - where (required): The Hasura `where` clause to filter by - order_by (optional): An optional Hasura `order_by` clause to order results by - error_on_empty (optional): If `True` and no tenants are found, a `ValueError` will be raised Returns: A list of dicts of tenant information """ client = Client() query_args = {"where": where} if order_by is not None: query_args["order_by"] = order_by tenant_query = { "query": { with_args("tenant", query_args): { "id", "slug", "name", } } } result = client.graphql(tenant_query) tenants = result.get("data", {}).get("tenant", None) if tenants is None: raise ValueError( f"Received bad result while querying for tenants where {where}: " f"{result}") if not tenants: # Empty list if error_on_empty: raise ValueError( f"No results found while querying for tenants where {where!r}" ) return [] # Return a list return tenants
def login(token): """ Log in to Prefect Cloud with an api token to use for Cloud communication. \b Options: --token, -t TEXT A Prefect Cloud api token [required] """ check_override_auth_token() client = Client(api_token=token) # Verify login obtained a valid api token try: output = client.graphql( query={"query": { "user": { "default_membership": "tenant_id" } }}) # Log into default membership success_login = client.login_to_tenant( tenant_id=output.data.user[0].default_membership.tenant_id) if not success_login: raise AuthorizationError except AuthorizationError: click.secho( f"Error attempting to use Prefect API token {token}. " "Please check that you are providing a USER scoped Personal Access Token.\n" "For more information visit the documentation for USER tokens at " "https://docs.prefect.io/orchestration/concepts/tokens.html#user", fg="red", ) return except ClientError: click.secho( "Error attempting to communicate with Prefect Cloud. " "Please check that you are providing a USER scoped Personal Access Token.\n" "For more information visit the documentation for USER tokens at " "https://docs.prefect.io/orchestration/concepts/tokens.html#user", fg="red", ) return # save token client.save_api_token() click.secho("Login successful!", fg="green")
def is_agent_up_prefect_cloud(pytestconfig): """ Check if the agent is up on Prefect Cloud """ # get prefect agent API token prefect_agent_token = pytestconfig.getoption("prefect_agent_token") # Instantiate the prefect client prefect_client = Client(api_token=prefect_agent_token) # query prefect cloud agents query = """ query RunningFlows { agent { labels, last_queried } } """ agents = prefect_client.graphql(query=query)["data"]["agent"] environment_agent_info = [ item for item in agents if f"{ENV}_dataflow_automation" in item["labels"] ] if len(environment_agent_info) == 0: # agent doesn't even appear on prefect cloud yet return ResourceCheckStatus.RETRY else: last_queried_time = environment_agent_info[0]["last_queried"] # date last_queried_time = datetime.strptime(last_queried_time, "%Y-%m-%dT%H:%M:%S.%f%z") difference = datetime.utcnow().replace( tzinfo=None) - last_queried_time.replace(tzinfo=None) # If the agent has been queried recently (meaning it is up) if int(difference.total_seconds()) < 15: return ResourceCheckStatus.FINISHED else: # agent appears on prefect cloud but doesn't seem to be up return ResourceCheckStatus.RETRY
def revoke_key(id): """ Revoke a Prefect Cloud API key. """ client = Client() output = client.graphql( query={ "mutation($input: delete_api_key_input!)": { "delete_api_key(input: $input)": {"success"} } }, variables=dict(input=dict(key_id=id)), ) if not output.get("data", None) or not output.data.delete_api_key.success: raise TerminalError(f"Unable to revoke key {id!r}") click.secho("Key successfully revoked!", fg="green")
def login(token): """ Log in to Prefect Cloud with an api token to use for Cloud communication. \b Options: --token, -t TEXT A Prefect Cloud api token [required] """ check_override_auth_token() client = Client(api_token=token) # Verify login obtained a valid api token try: output = client.graphql( query={"query": { "user": { "default_membership": "tenant_id" } }}) # Log into default membership success_login = client.login_to_tenant( tenant_id=output.data.user[0].default_membership.tenant_id) if not success_login: raise AuthorizationError except AuthorizationError: click.secho( "Error attempting to use Prefect API token {}".format(token), fg="red") return except ClientError: click.secho("Error attempting to communicate with Prefect Cloud", fg="red") return # save token client.save_api_token() click.secho("Login successful!", fg="green")
def create_project_if_not_exists(client: prefect.Client, project_name: str) -> None: """Checks whether a project named "Monitorfish" already exists in Prefect Server. If not, the project is created. Args: client (prefect.Client): Prefect client instance Raises: ValueError: if more than 1 project with the name "Monitorfish" are found. """ r = client.graphql( 'query{project(where: {name: {_eq : "Monitorfish"}}){name}}') projects = r["data"]["project"] if len(projects) == 0: print("Monitorfish project does not exists, it will be created.") client.create_project(project_name) elif len(projects) == 1: print("Monitorfish project already exists. Skipping project creation.") else: raise ValueError( "Several projects with the name 'Monitorfish' were found.")
def create_token(name, scope): """ DEPRECATED. Please use API keys instead. Create a Prefect Cloud API token. For more info on API tokens visit https://docs.prefect.io/orchestration/concepts/api.html \b Options: --name, -n TEXT A name to give the generated token --scope, -s TEXT A scope for the token """ click.secho( "WARNING: API tokens are deprecated. Please use `prefect auth create-key` to " "create an API key instead.", fg="yellow", err=True, # Write to stderr in case the user is piping ) client = Client() output = client.graphql( query={ "mutation($input: create_api_token_input!)": { "create_api_token(input: $input)": {"token"} } }, variables=dict(input=dict(name=name, scope=scope)), ) if not output.get("data", None): click.secho("Issue creating API token", fg="red") return click.echo(output.data.create_api_token.token)
def login(key, token): """ Login to Prefect Cloud Create an API key in the UI then login with it here: $ prefect auth login -k YOUR-KEY You will be switched to the default tenant associated with the key. After login, your available tenants can be seen with `prefect auth list-tenants` and you can change the default tenant on this machine using `prefect auth switch-tenants`. The given key will be stored on disk for later access. Prefect will default to using this key for all interaction with the API but frequently overrides can be passed to individual commands or functions. To remove your key from disk, see `prefect auth logout`. This command has backwards compatibility support for API tokens, which are a deprecated form of authentication with Prefect Cloud """ if not key and not token: raise TerminalError("You must supply an API key or token!") if key and token: raise TerminalError("You cannot supply both an API key and token") abort_on_config_api_key( "To log in with the CLI, remove the config key `prefect.cloud.api_key`" ) # Attempt to treat the input like an API key even if it is passed as a token client = Client(api_key=key or token) try: default_tenant = client.get_default_tenant() except AuthorizationError: if key: # We'll catch an error again later if using a token raise TerminalError("Unauthorized. Invalid Prefect Cloud API key.") except ClientError: raise TerminalError( "Error attempting to communicate with Prefect Cloud.") else: if not default_tenant and key: raise TerminalError( "Failed to find a tenant associated with the given API key!") elif default_tenant: # Successful login if token: click.secho( "WARNING: You logged in with an API key using the `--token` flag " "which is deprecated. Please use `--key` instead.", fg="yellow", ) client.save_auth_to_disk() click.secho("Login successful!", fg="green") return # If there's not a tenant id, we've been given an actual token, fallthrough to # the backwards compatibility token auth # Backwards compatibility for tokens if token: check_override_auth_token() client = Client(api_token=token) # Verify they're not also using an API key if client.api_key: raise TerminalError( "You have already logged in with an API key and cannot use a token." ) click.secho( "WARNING: API tokens are deprecated. Please create an API key and use " "`prefect auth login --key <KEY>` to login instead.", fg="yellow", ) # Verify login obtained a valid api token try: output = client.graphql( query={"query": { "user": { "default_membership": "tenant_id" } }}) # Log into default membership success_login = client.login_to_tenant( tenant_id=output.data.user[0].default_membership.tenant_id) if not success_login: raise AuthorizationError except AuthorizationError: click.secho( "Error attempting to use the given API token. " "Please check that you are providing a USER scoped Personal Access Token.\n" "For more information visit the documentation for USER tokens at " "https://docs.prefect.io/orchestration/concepts/tokens.html#user", fg="red", ) return except ClientError: click.secho( "Error attempting to communicate with Prefect Cloud. " "Please check that you are providing a USER scoped Personal Access Token.\n" "For more information visit the documentation for USER tokens at " "https://docs.prefect.io/orchestration/concepts/tokens.html#user", fg="red", ) return # save token client.save_api_token() click.secho("Login successful!", fg="green")
from os import walk, path from prefect import Client from importlib import import_module from itertools import groupby client = Client() flows = client.graphql('''query { flow { project { name } archived name id } }''').to_dict()['data']['flow'] flows_by_project = { project: { flow['id']: flow['name'] for flow in filter(lambda flow: not flow['archived'], project_flows) } for project, project_flows in groupby(flows, lambda flow: flow['project']['name']) } (root, projects, _) = next(walk(path.dirname(__file__))) for project_name in projects: if project_name in flows_by_project: for old_flow in flows_by_project[project_name].keys(): error = client.graphql(f'''mutation {{ archive_flow(input: {{ flow_id: "{old_flow}" }}) {{ error }} }}''').to_dict()['data']['archive_flow']['error'] print('Archiving existing flow %s.. %s' % (old_flow, error or 'OK'))
def create_key(name, expire, quiet): """ Create a Prefect Cloud API key for authentication with your current user """ # TODO: Add service account associated key creation eventually # Parse the input expiration if expire is not None: try: expires_at = pendulum.parse(expire, strict=False) except pendulum.parsing.exceptions.ParserError as exc: raise TerminalError( f"Failed to parse expiration time. {exc}\n" "Please pass a date in a dateutil parsable format.") if expires_at.diff(abs=False).in_seconds() > 0: raise TerminalError( f"Given expiration time {expire!r} is a time in the past: {expires_at}" ) expire_msg = f" that will expire {expires_at.diff_for_humans()}" else: expires_at = None expire_msg = "" client = Client() # We must retrieve our own user id first since you could be creating a key for a SA if not quiet: click.echo("Retrieving user information...") response = client.graphql({"query": {"auth_info": {"user_id"}}}) user_id = response.get("data", {}).get("auth_info", {}).get("user_id") if not user_id: raise TerminalError( "Failed to retrieve the current user id from Prefect Cloud") # Actually create the key if not quiet: click.echo(f"Creating key{expire_msg}...") response = client.graphql( query={ "mutation($input: create_api_key_input!)": { "create_api_key(input: $input)": {"key"} } }, variables=dict(input=dict( name=name, user_id=user_id, expires_at=expires_at.in_tz("utc").isoformat( ) if expires_at else None, )), ) key = response.get("data", {}).get("create_api_key", {}).get("key") if key is None: raise TerminalError( f"Unexpected response from Prefect Cloud: {response}") if quiet: click.echo(key) else: click.echo( "This is the only time this key will be displayed! Store it somewhere safe." ) click.secho(f"Successfully created key: {key}", fg="green")
def run( self, flow_name: str = None, project_name: str = None, parameters: dict = None, run_config: RunConfig = None, new_flow_context: dict = None, run_name: str = None, idempotency_key: str = None, scheduled_start_time: datetime.datetime = None, ) -> str: """ Run method for the task; responsible for scheduling the specified flow run. Args: - flow_name (str, optional): the name of the flow to schedule; if not provided, this method will use the flow name provided at initialization - project_name (str, optional): the Cloud project in which the flow is located; if not provided, this method will use the project provided at initialization. If running with Prefect Core's server as the backend, this should not be provided. - parameters (dict, optional): the parameters to pass to the flow run being scheduled; if not provided, this method will use the parameters provided at initialization - run_config (RunConfig, optional): a run-config to use for this flow run, overriding any existing flow settings. - new_flow_context (dict, optional): the optional run context for the new flow run - run_name (str, optional): name to be set for the flow run - idempotency_key (str, optional): a unique idempotency key for scheduling the flow run. Duplicate flow runs with the same idempotency key will only create a single flow run. This is useful for ensuring that only one run is created if this task is retried. If not provided, defaults to the active `task_run_id`. - scheduled_start_time (datetime, optional): the time to schedule the execution for; if not provided, defaults to now Returns: - str: the ID of the newly-scheduled flow run Raises: - ValueError: if flow was not provided, cannot be found, or if a project name was not provided while using Cloud as a backend Example: ```python from prefect.tasks.prefect.flow_run import StartFlowRun kickoff_task = StartFlowRun(project_name="Hello, World!", flow_name="My Cloud Flow") ``` """ # verify that flow and project names were passed where necessary if flow_name is None: raise ValueError("Must provide a flow name.") if project_name is None: raise ValueError("Must provide a project name.") where_clause = { "name": { "_eq": flow_name }, "archived": { "_eq": False }, "project": { "name": { "_eq": project_name } }, } # find the flow ID to schedule query = { "query": { with_args( "flow", { "where": where_clause, "order_by": { "version": EnumValue("desc") }, "limit": 1, }, ): {"id"} } } client = Client() flow = client.graphql(query).data.flow # verify that a flow has been returned if not flow: raise ValueError("Flow '{}' not found.".format(flow_name)) # grab the ID for the most recent version flow_id = flow[0].id if idempotency_key is None: idempotency_key = prefect.context.get("task_run_id", None) # providing an idempotency key ensures that retries for this task # will not create additional flow runs flow_run_id = client.create_flow_run( flow_id=flow_id, parameters=parameters, run_config=run_config, idempotency_key=idempotency_key, context=new_flow_context, run_name=run_name, scheduled_start_time=scheduled_start_time, ) self.logger.debug(f"Flow Run {flow_run_id} created.") self.logger.debug( f"Creating link artifact for Flow Run {flow_run_id}.") run_link = client.get_cloud_url("flow-run", flow_run_id, as_user=False) create_link(urlparse(run_link).path) self.logger.info(f"Flow Run: {run_link}") if not self.wait: return flow_run_id while True: time.sleep(self.poll_interval.total_seconds()) flow_run_state = client.get_flow_run_info(flow_run_id).state if flow_run_state.is_finished(): exc = signal_from_state(flow_run_state)( f"{flow_run_id} finished in state {flow_run_state}") raise exc
def execute( # type: ignore self, flow: "Flow", **kwargs: Any # type: ignore ) -> None: """ Execute a flow run on a dask-cloudprovider cluster. Args: - flow (Flow): the Flow object - **kwargs (Any): Unused """ flow_run_info = None flow_run_id = prefect.context.get("flow_run_id") if self._on_execute: # If an on_execute Callable has been provided, retrieve the flow run parameters # and then allow the Callable a chance to update _provider_kwargs. This allows # better sizing of the cluster resources based on parameters for this Flow run. try: client = Client() flow_run_info = client.get_flow_run_info(flow_run_id) parameters = flow_run_info.parameters or {} # type: ignore self._on_execute(parameters, self._provider_kwargs) except Exception as exc: self.logger.info( "Failed to retrieve flow run info with error: {}".format(repr(exc)) ) if "image" not in self._provider_kwargs or not self._provider_kwargs.get( "image" ): # If image is not specified, use the Flow's image so that dependencies are # identical on all containers: Flow runner, Dask scheduler, and Dask workers flow_id = prefect.context.get("flow_id") try: client = Client() if not flow_id: # We've observed cases where flow_id is None if not flow_run_info: flow_run_info = client.get_flow_run_info(flow_run_id) flow_id = flow_run_info.flow_id flow_info = client.graphql( """query { flow(where: {id: {_eq: "%s"}}) { storage } }""" % flow_id ) storage_info = flow_info["data"]["flow"][0]["storage"] image = "{}/{}:{}".format( storage_info["registry_url"], storage_info["image_name"], storage_info["image_tag"], ) self.logger.info( "Using Flow's Docker image for Dask scheduler & workers: {}".format( image ) ) self._provider_kwargs["image"] = image except Exception as exc: self.logger.info( "Failed to retrieve flow info with error: {}".format(repr(exc)) ) self._create_dask_cluster() self.logger.info( "Executing on dynamically created Dask Cluster with scheduler address: {}".format( self.executor_kwargs["address"] ) ) if self.on_start: self.on_start() try: from prefect.engine import get_default_flow_runner_class from prefect.executors import DaskExecutor runner_cls = get_default_flow_runner_class() runner_cls(flow=flow).run(executor=DaskExecutor(**self.executor_kwargs)) except Exception as exc: self.logger.exception( "Unexpected error raised during flow run: {}".format(exc) ) raise finally: if self.on_exit: self.on_exit()
def change_prev_flow_state(number, secret): logger = prefect.context.get("logger") prev_random_number = prefect.context.get("prev_random_number") prev_flow_run_id = prefect.context.get("prev_flow_run_id") logger.info(f"The number retrieved from Context was: {prev_random_number}") logger.info(f"The number submitted to this Task was: {number}") client = Client(api_token=secret) client.login_to_tenant(tenant_slug="kmw-cloud") if number is None: logger.info("Outcome: Your number was None") pass elif number < 50: logger.info(f"Outcome: Your number was {number}, which is < 50") pass elif number < 70: set_flow_run_state = client.graphql(query=""" mutation SetFlowRunStates($flowRunId: UUID!, $state: JSON!) { set_flow_run_states( input: { states: [{ flow_run_id: $flowRunId, state: $state }] } ) { states { id status message } } } """, variables={ "flowRunId": prev_flow_run_id, "state": { "type": "Cancelled" } }) elif number < 90: set_flow_run_state = client.graphql(query=""" mutation SetFlowRunStates($flowRunId: UUID!, $state: JSON!) { set_flow_run_states( input: { states: [{ flow_run_id: $flowRunId, state: $state }] } ) { states { id status message } } } """, variables={ "flowRunId": prev_flow_run_id, "state": { "type": "Finished" } }) else: set_flow_run_state = client.graphql(query=""" mutation SetFlowRunStates($flowRunId: UUID!, $state: JSON!) { set_flow_run_states( input: { states: [{ flow_run_id: $flowRunId, state: $state }] } ) { states { id status message } } } """, variables={ "flowRunId": prev_flow_run_id, "state": { "type": "Skipped" } })
from prefect import Client from prefect.utilities.graphql import with_args c = Client() name = "my_flow" c.graphql({"query": {with_args("flow", {"where": {"name": {"_eq": name}}}): "id"}}) # c.graphql({"query": "'query' {'flow'('where': { 'name': { '_eq': 'ltest' } }) {'id'}}"})