def flow_display( flow_id: str = typer.Argument("", show_default=False), flow_definition: str = typer.Option( "", help= ("JSON or YAML representation of the Flow to display. May be provided as a filename " "or a raw string representing a JSON object or YAML definition."), callback=input_validator, show_default=False, ), output_format: ImageOutputFormat = typer.Option( ImageOutputFormat.json, "--format", "-f", help="Output display format.", case_sensitive=False, show_default=True, ), flows_endpoint: str = flows_env_var_option, ): """ Visualize a local or deployed Flow defintion. If providing a Flows's ID, You must have either created the Flow or be present in the Flow's "flow_viewers" list to view it. """ if not flow_definition and not flow_id: raise typer.BadParameter( "Either FLOW_ID or --flow_definition should be set.") if flow_definition and flow_id: raise typer.BadParameter( "Only one of FLOW_ID or --flow_definition should be set.") fc = create_flows_client(CLIENT_ID, flows_endpoint) rr = RequestRunner( functools.partial(fc.get_flow, flow_id), format=output_format, verbose=False, watch=False, ) if flow_id: result = rr.run() if result.is_api_error: rr.format = (output_format if output_format in { ImageOutputFormat.json, ImageOutputFormat.yaml } else ImageOutputFormat.json) rr.render(result) raise typer.Exit(1) else: flow_dict = result.data["definition"] else: flow_dict = process_input(flow_definition) if output_format in {ImageOutputFormat.json, ImageOutputFormat.yaml}: rr.render_as_result(flow_dict) else: output_format.visualize(flow_dict)
def flow_lint( definition: str = typer.Option( ..., help= ("JSON or YAML representation of the Flow to deploy. May be provided as a filename " "or a raw string."), prompt=True, callback=input_validator_callback, ), validate: bool = typer.Option( True, help= ("(EXPERIMENTAL) Perform rudimentary validation of the flow definition." ), case_sensitive=False, show_default=True, ), output_format: FlowDisplayFormat = typer.Option( FlowDisplayFormat.json, "--format", "-f", help="Output display format.", case_sensitive=False, show_default=True, ), input_format: InputFormat = typer.Option( InputFormat.json, "--input", "-i", help="Input format.", case_sensitive=False, show_default=True, ), ): """ Parse and validate a Flow definition by providing visual output. """ flow_dict = process_input(definition, input_format) try: if validate: validate_flow_definition(flow_dict) except FlowValidationError as fve: typer.secho(str(fve), fg=typer.colors.RED) raise typer.Exit(code=1) graph = graphviz_format(flow_dict) if output_format is FlowDisplayFormat.json: format_and_echo(flow_dict) elif output_format is FlowDisplayFormat.yaml: format_and_echo(flow_dict, yaml.dump) elif output_format is FlowDisplayFormat.graphviz: typer.echo(graph.source) else: graph.render("flows-output/graph", view=True, cleanup=True)
def flow_lint(definition: str = typer.Option( ..., help= ("JSON or YAML representation of the Flow to deploy. May be provided as a filename " "or a raw string."), prompt=True, callback=input_validator, ), ): """ Parse and validate a Flow definition by providing visual output. """ flow_dict = process_input(definition) try: validate_flow_definition(flow_dict) except FlowValidationError as fve: typer.secho(str(fve), fg=typer.colors.RED) raise typer.Exit(code=1) typer.secho("No issues found in the Flow definition.", fg=typer.colors.GREEN)
def flow_deploy( title: str = typer.Option(..., help="The Flow's title.", prompt=True), definition: str = typer.Option( ..., help= ("JSON or YAML representation of the Flow to deploy. May be provided as a filename " "or a raw string representing a JSON object or YAML definition."), prompt=True, callback=input_validator_callback, ), subtitle: str = typer.Option( None, help="A subtitle for the Flow providing additional, brief description.", ), description: str = typer.Option( None, help="A long form description of the Flow's purpose or usage."), input_schema: str = typer.Option( None, help= ("A JSON or YAML representation of a JSON Schema which will be used to " "validate the input to the deployed Flow when it is run. " "If not provided, no validation will be performed on Flow input. " "May be provided as a filename or a raw string."), callback=input_validator_callback, ), keywords: List[str] = typer.Option( None, "--keyword", help= "A keyword which may categorize or help discover the Flow. [repeatable]", ), visible_to: List[str] = typer.Option( None, help=( "A principal which may see the existence of the deployed Flow. The " 'special value of "public" may be used to control which users can ' "discover this flow. [repeatable]"), callback=principal_or_public_validator, ), administered_by: List[str] = typer.Option( None, help="A principal which may update the deployed Flow. [repeatable]", callback=principal_validator, ), runnable_by: List[str] = typer.Option( None, help= ("A principal which may run an instance of the deployed Flow. The special " 'value of "all_authenticated_users" may be used to control which users ' "can invoke this flow. [repeatable]"), callback=principal_or_all_authenticated_users_validator, ), validate: bool = typer.Option( True, help= ("(EXPERIMENTAL) Perform rudimentary validation of the flow definition." ), case_sensitive=False, show_default=True, ), flows_endpoint: str = typer.Option( PROD_FLOWS_BASE_URL, hidden=True, callback=flows_endpoint_envvar_callback, ), verbose: bool = verbosity_option, input_format: InputFormat = typer.Option( InputFormat.json, "--input", "-i", help="Input format.", case_sensitive=False, show_default=True, ), ): """ Deploy a new Flow. """ fc = create_flows_client(CLIENT_ID, flows_endpoint) flow_dict = process_input(definition, input_format) input_schema_dict = process_input(input_schema, input_format, " for input schema") result = fc.deploy_flow( flow_dict, title, subtitle, description, keywords, visible_to, runnable_by, administered_by, input_schema_dict, validate_definition=validate, ) # Match up output format with input format if input_format is InputFormat.json: format_and_echo(result, json.dumps, verbose=verbose) elif input_format is InputFormat.yaml: format_and_echo(result, yaml.dump, verbose=verbose)
def action_run( action_url: str = typer.Option( ..., help="The url at which the target Action Provider is located.", prompt=True, callback=url_validator_callback, ), action_scope: str = typer.Option( None, help="The scope this Action Provider uses to authenticate requests.", callback=url_validator_callback, ), body: str = typer.Option( ..., "--body", "-b", help= ("The body to supply to the Action Provider. Can be a filename or raw " "JSON string."), prompt=True, callback=input_validator_callback, ), request_id: str = typer.Option( None, help=( "An identifier to associate with this Action invocation request"), ), manage_by: List[str] = typer.Option( None, help= "A principal which may change the execution of the Action. [repeatable]", callback=principal_validator, ), monitor_by: List[str] = typer.Option( None, help="A principal which may view the state of the Action. [repeatable]", callback=principal_validator, ), verbose: bool = verbosity_option, output_format: ActionOutputFormat = typer.Option( ActionOutputFormat.json, "--format", "-f", help="Output display format.", case_sensitive=False, show_default=True, ), input_format: InputFormat = typer.Option( InputFormat.json, "--input", "-i", help="Input format.", case_sensitive=False, show_default=True, ), ): """ Launch an Action. """ ac = create_action_client(action_url, action_scope) if ac: parsed_body = process_input(body, input_format) result = ac.run(parsed_body, request_id, manage_by, monitor_by) format_and_echo(result, output_format.get_dumper(), verbose=verbose) return None
def flow_deploy( title: str = typer.Option(..., help="The Flow's title.", prompt=True), definition: str = typer.Option( ..., help= ("JSON or YAML representation of the Flow to deploy. May be provided as a filename " "or a raw string representing a JSON object or YAML definition."), prompt=True, callback=input_validator, ), subtitle: str = typer.Option( None, help="A subtitle for the Flow providing additional, brief description.", ), description: str = typer.Option( None, help="A long form description of the Flow's purpose or usage."), input_schema: str = typer.Option( None, help= ("A JSON or YAML representation of a JSON Schema which will be used to " "validate the input to the deployed Flow when it is run. " "If not provided, no validation will be performed on Flow input. " "May be provided as a filename or a raw string."), callback=input_validator, ), keywords: List[str] = typer.Option( None, "--keyword", help= "A keyword which may categorize or help discover the Flow. [repeatable]", ), flow_viewer: List[str] = typer.Option( None, help=("A principal which may view this Flow. " + _principal_description + " The special value of 'public' may be used to " "indicate that any user can view this Flow. [repeatable]"), callback=principal_or_public_validator, hidden=False, ), # viewer and visible_to are aliases for the full flow_viewer viewer: List[str] = typer.Option( None, callback=principal_or_public_validator, hidden=True, ), visible_to: List[str] = typer.Option( None, callback=principal_or_public_validator, hidden=True, ), flow_starter: List[str] = typer.Option( None, help= ("A principal which may run an instance of the deployed Flow. " + _principal_description + "The special value of " "'all_authenticated_users' may be used to indicate that any authenticated user " "can invoke this flow. [repeatable]"), callback=principal_or_all_authenticated_users_validator, ), # starter and runnable_by are aliases for the full flow_starter starter: List[str] = typer.Option( None, callback=principal_or_all_authenticated_users_validator, hidden=True), runnable_by: List[str] = typer.Option( None, callback=principal_or_all_authenticated_users_validator, hidden=True), flow_administrator: List[str] = typer.Option( None, help=("A principal which may update the deployed Flow. " + _principal_description + "[repeatable]"), callback=principal_validator, ), # administrator and administered_by are aliases for the full flow_administrator administrator: List[str] = typer.Option(None, callback=principal_validator, hidden=True), administered_by: List[str] = typer.Option(None, callback=principal_validator, hidden=True), subscription_id: Optional[str] = typer.Option( None, help= "The Id of the Globus Subscription which will be used to make this flow managed.", ), validate: bool = typer.Option( True, help= ("(EXPERIMENTAL) Perform rudimentary validation of the flow definition." ), case_sensitive=False, show_default=True, ), flows_endpoint: str = flows_env_var_option, verbose: bool = verbosity_option, output_format: OutputFormat = output_format_option, dry_run: bool = typer.Option( False, "--dry-run", help=( "Do a dry run of deploying the flow to test your definition without" " actually making changes."), ), ): """ Deploy a new Flow. """ fc = create_flows_client(CLIENT_ID, flows_endpoint) flow_dict = process_input(definition) input_schema_dict = process_input(input_schema) method = functools.partial( fc.deploy_flow, flow_dict, title, subtitle, description, keywords, visible_to, runnable_by, administered_by, subscription_id, input_schema_dict, validate_definition=validate, dry_run=dry_run, ) RequestRunner(method, format=output_format, verbose=verbose).run_and_render()
def flow_run( flow_id: str = typer.Argument(...), flow_input: str = typer.Option( ..., help= ("JSON or YAML formatted input to the Flow. May be provided as a filename " "or a raw string."), callback=flow_input_validator, ), flow_scope: str = typer.Option( None, help="The scope this Flow uses to authenticate requests.", callback=url_validator_callback, ), run_manager: List[str] = typer.Option( None, help="A principal which may change the execution of the Flow instance. " + _principal_description + " [repeatable]", callback=principal_validator, ), manage_by: List[str] = typer.Option(None, callback=principal_validator, hidden=True), run_monitor: List[str] = typer.Option( None, help= "A principal which may monitor the execution of the Flow instance. " + _principal_description + " [repeatable]", callback=principal_validator, ), monitor_by: List[str] = typer.Option(None, callback=principal_validator, hidden=True), flows_endpoint: str = flows_env_var_option, verbose: bool = verbosity_option, output_format: OutputFormat = output_format_option, label: str = typer.Option( ..., "--label", "-l", help="Label to mark this run.", ), watch: bool = typer.Option( False, "--watch", "-w", help= "Continuously poll this Action until it reaches a completed state.", show_default=True, ), dry_run: bool = typer.Option( False, "--dry-run", help= ("Do a dry run with your input to this flow to test the input without" " actually running anything."), ), ): """ Run an instance of a Flow. The argument provides the initial state of the Flow. You must be in the Flow's "flow_starters" list. """ fc = create_flows_client(CLIENT_ID, flows_endpoint) flow_input_dict = process_input(flow_input) method = functools.partial( fc.run_flow, flow_id, flow_scope, flow_input_dict, run_monitors=run_monitor, run_managers=run_manager, label=label, dry_run=dry_run, monitor_by=monitor_by, manage_by=manage_by, ) with live_content: result = RequestRunner( method, format=output_format, verbose=verbose, watch=watch, run_once=True, ).run_and_render() if not result.is_api_error and watch: action_id = result.data.get("action_id") method = functools.partial(fc.flow_action_status, flow_id, flow_scope, action_id) RequestRunner( method, format=output_format, verbose=verbose, watch=watch, run_once=False, ).run_and_render()
def flow_update( flow_id: str = typer.Argument(...), title: str = typer.Option(None, help="The Flow's title."), definition: str = typer.Option( None, help= ("JSON or YAML representation of the Flow to update. May be provided as a filename " "or a raw string."), callback=input_validator, ), subtitle: str = typer.Option( None, help="A subtitle for the Flow providing additional, brief description.", ), description: str = typer.Option( None, help="A long form description of the Flow's purpose or usage."), input_schema: str = typer.Option( None, help= ("A JSON or YAML representation of a JSON Schema which will be used to " "validate the input to the deployed Flow when it is run. " "If not provided, no validation will be performed on Flow input. " "May be provided as a filename or a raw string."), callback=input_validator, ), keywords: List[str] = typer.Option( None, "--keyword", help= "A keyword which may categorize or help discover the Flow. [repeatable]", ), flow_viewer: List[str] = typer.Option( None, help="A principal which may view this Flow. " + _principal_description + "The special value of 'public' may be used to " "indicate that any user can view this Flow. [repeatable]", callback=principal_or_public_validator, ), viewer: List[str] = typer.Option(None, callback=principal_or_public_validator, hidden=True), visible_to: List[str] = typer.Option( None, callback=principal_or_public_validator, hidden=True), flow_starter: List[str] = typer.Option( None, help="A principal which may run an instance of the deployed Flow. " + _principal_description + " The special value of " "'all_authenticated_users' may be used to indicate that any " "authenticated user can invoke this flow. [repeatable]", callback=principal_or_all_authenticated_users_validator, ), starter: List[str] = typer.Option( None, callback=principal_or_all_authenticated_users_validator, hidden=True), runnable_by: List[str] = typer.Option( None, callback=principal_or_all_authenticated_users_validator, hidden=True), flow_administrator: List[str] = typer.Option( None, help="A principal which may update the deployed Flow. " + _principal_description + "[repeatable]", callback=principal_validator, ), administrator: List[str] = typer.Option(None, callback=principal_validator, hidden=True), administered_by: List[str] = typer.Option(None, callback=principal_validator, hidden=True), assume_ownership: bool = typer.Option( False, "--assume-ownership", help= "Assume the ownership of the Flow. This can only be performed by user's " "in the flow_administrators role.", ), subscription_id: Optional[str] = typer.Option( None, help= "The Globus Subscription which will be used to make this flow managed.", ), validate: bool = typer.Option( True, help= ("(EXPERIMENTAL) Perform rudimentary validation of the flow definition." ), case_sensitive=False, show_default=True, ), flows_endpoint: str = flows_env_var_option, verbose: bool = verbosity_option, output_format: OutputFormat = output_format_option, ): """ Update a Flow. """ fc = create_flows_client(CLIENT_ID, flows_endpoint) flow_dict = process_input(definition) input_schema_dict = process_input(input_schema) method = functools.partial( fc.update_flow, flow_id, flow_dict, title, subtitle, description, keywords, flow_viewer, flow_starter, flow_administrator, subscription_id, input_schema_dict, validate_definition=validate, visible_to=visible_to, runnable_by=runnable_by, administered_by=administered_by, ) RequestRunner(method, format=output_format, verbose=verbose).run_and_render()
def action_run( action_url: str = typer.Option( ..., help="The url at which the target Action Provider is located.", prompt=True, callback=url_validator_callback, ), action_scope: str = typer.Option( None, help="The scope this Action Provider uses to authenticate requests.", callback=url_validator_callback, ), body: str = typer.Option( ..., "--body", "-b", help= ("The body to supply to the Action Provider. Can be a filename or raw " "JSON string."), prompt=True, callback=input_validator, ), request_id: str = typer.Option( None, help=( "An identifier to associate with this Action invocation request"), ), manage_by: List[str] = typer.Option( None, help= "A principal which may change the execution of the Action. The principal " "is the user's or group's UUID prefixed with either 'urn:globus:groups:id:' " "or 'urn:globus:auth:identity:' [repeatable]", callback=principal_validator, ), monitor_by: List[str] = typer.Option( None, help="A principal which may view the state of the Action. The principal " "is the user's or group's UUID prefixed with either 'urn:globus:groups:id:' " "or 'urn:globus:auth:identity:' [repeatable]", callback=principal_validator, ), verbose: bool = verbosity_option, output_format: OutputFormat = output_format_option, label: str = typer.Option( None, "--label", "-l", help="Optional label to mark this execution of the action.", ), watch: bool = typer.Option( False, "--watch", "-w", help= "Continuously poll this Action until it reaches a completed state.", show_default=True, ), ): """ Launch an Action. """ parsed_body = process_input(body) ac = create_action_client(action_url, action_scope) method = functools.partial(ac.run, parsed_body, request_id, manage_by, monitor_by, label=label) with live_content: result = RequestRunner( method, format=output_format, verbose=verbose, watch=watch, run_once=True, ).run_and_render() if not result.is_api_error and watch: action_id = result.data.get("action_id") method = functools.partial(ac.status, action_id) RequestRunner( method, format=output_format, verbose=verbose, watch=watch, run_once=False, ).run_and_render()