def get_type_info( # Basic config config_name: str = typer.Argument(None, help=CONFIG_ARG_HELP), graph_name: str = typer.Argument(None, help=GRAPH_ARG_HELP), # Types to query vertex_type_names: List[str] = typer.Option( [], "--vertex", help="Vertex type name to query. Specify * to query all."), edge_type_names: List[str] = typer.Option( [], "--edge", help="Vertex type name to query. Specify * to query all.")): """Get a set of types, either vertices or edges. If no optioans are given, all types are returned.""" conn = get_initialized_tg_connection(config_name=config_name, graph_name=graph_name, require_graph=True) results = {} query_all = (not vertex_type_names) and (not edge_type_names) if vertex_type_names or query_all: vertex_types = resolve_multiple_args(vertex_type_names) if query_all: vertex_types = "*" results.update(conn.getVertexStats(vertex_types)) if edge_type_names or query_all: edge_types = resolve_multiple_args(edge_type_names) if query_all: edge_types = "*" results.update(conn.getEdgeStats(edge_types)) cli.print_to_console(results)
def __get_config_interactive__() -> TgcliConfiguration: """Retrieves a configuration from the user interactively :return: A TgcliConfiguration that corresponds to the input """ cli.print_to_console("Adding a TigerGraph configuration") server_address = cli.get_input_str( "Server Address (ex. https://xyz.i.tgcloud.io)").strip() client_version = cli.get_input_str( "Client Version (3.0.0, 2.6.0, 2.5.2, 2.5.0, 2.4.1, 2.4.0, 2.3.2)" ).strip() restpp_port = cli.get_input_str("REST++ Port", default=DEFAULT_RESTPP_PORT).strip() gs_port = cli.get_input_str("GS Port", default=DEFAULT_GS_PORT).strip() use_auth = cli.get_input_bool("Use Auth (This is usually true)", default=True) server_username = cli.get_input_str("Username", default="tigergraph").strip() server_password = cli.get_input_str("Password", default="tigergraph", hide_input=True) name = cli.get_input_str( "Name (Alphanumeric & Underscore Allowed)").strip() name = __clean_config_name__(name) return TgcliConfiguration(name=name, server=server_address, username=server_username, password=server_password, client_version=client_version, restpp_port=restpp_port, gs_port=gs_port, use_auth=use_auth)
def reinit_dependencies(config_name: str = typer.Argument( ..., help=CONFIG_ARG_HELP), graph_name: str = typer.Option("", "--graph", help=GRAPH_ARG_HELP)): """Force download dependencies for the configuration. If a graph name is specified, a new secret will be generated for the graph. """ _ = get_initialized_tg_connection(config_name=config_name, graph_name=graph_name, clean_init=True) cli.print_to_console("Done")
def add_config(): """Adds a configuration - only supports interactive input for now""" new_config = __get_config_interactive__() curr_configs = get_configs() if curr_configs.get(new_config.name, None): # Make sure that we want to overwrite if not cli.get_input_bool( prompt= f"Configuration {new_config.name} already exists. Do you want to overwrite it?" ): cli.terminate(message="Add configuration cancelled.") # Overwriting a configuration may need a reinitialization of dependencies cli.print_to_console( f"Configuration {new_config.name} already exists. You may need to run " f"tgcli reinit-dependencies to update dependencies to reflect this new configuration." ) curr_configs[new_config.name] = new_config save_configs(curr_configs) cli.print_to_console( f"Configuration {new_config.name} added for server {new_config.server}" )
def run_gsql( config_name: str = typer.Argument(None, help=CONFIG_ARG_HELP), graph_name: str = typer.Argument(None, help=GRAPH_ARG_HELP), inline_command: str = typer.Option(None, "--command", help="Inline GSQL command"), file_command: Path = typer.Option( None, "--file", help="Filepath to load a GSQL command from.", exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True), launch_editor: bool = typer.Option( False, "--editor", help="Launch an interactive editor to load the GSQL command")): """Run a GSQL query against a configuration through an inline command, file, or interactive editor""" conn = get_initialized_tg_connection(config_name=config_name, graph_name=graph_name) options = [] if graph_name: options = ["-g", graph_name] command = None # Get the GSQL command depending on the flag if inline_command: command = inline_command elif file_command: with open(file_command, "r") as cmd_file: command = cmd_file.read() elif launch_editor: command = cli.get_input_from_editor("") command = command.strip() if not command: cli.terminate(message="No command specified.", is_err=True) output = conn.gsql(command, options=options) cli.print_to_console(output)
def delete_config(name: str): """Delete a configuration with the given name.""" try: all_configs = get_configs(raise_on_nonexistent=True) except TgcliConfigurationError as e: # No configuration files found - or error while retrieving the files cli.print_to_console(e.message, is_err=True) cli.print_to_console( "Please add a valid configuration using tgcli config add", is_err=True) return if not all_configs[name]: cli.print_to_console(f"Configuration {name} not found.", is_err=True) return if cli.get_input_bool(f"Are you sure you want to delete {name}?"): # Delete all the associated initialization files delete_dependencies(all_configs[name]) # Delete the dictionary associated with the config del all_configs[name] save_configs(all_configs) cli.print_to_console(f"{name} deleted.")
def load_vertices( # Basic config config_name: str = typer.Argument(None, help=CONFIG_ARG_HELP), graph_name: str = typer.Argument(None, help=GRAPH_ARG_HELP), # Required attributes vertex_type: str = typer.Option(..., "--type", help="Vertex type to map data to.", prompt="Vertex type"), vertex_id_col: str = typer.Option( ..., "--id", help="Column name to set as the ID of the vertex", prompt="Column name for ID"), # No prompt here - must be provided via CLI (string split into chars because of typer processing) attrs: List[str] = typer. Option( [], "--attr", help= "Column name of an vertex attribute, multiple can be specified by using the flag multiple times. " "If no values are provided, all columns will be used.", ), # Data sources csv_filepath: Path = typer.Option( None, "--csv", help="CSV filepath to load vertices from.", exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True), pickle_filepath: Path = typer.Option( None, "--pickle", help="Pickle filepath to load vertices from.", exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True), json_filepath: Path = typer.Option( None, "--json", help="JSON filepath to load vertices from.", exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True)): """Loads a set of vertices from a given datasource.""" conn = get_initialized_tg_connection(config_name=config_name, graph_name=graph_name, require_graph=True) num_upserted: int = 0 vertex_attributes = None df = None if attrs: vertex_attributes = {val: val for val in attrs} if csv_filepath: df = __get_df__(csv_filepath, "csv") elif pickle_filepath: df = __get_df__(pickle_filepath, "pickle") elif json_filepath: df = __get_df__(json_filepath, "json") if df is not None: num_upserted = conn.upsertVertexDataframe(df=df, vertexType=vertex_type, v_id=vertex_id_col, attributes=vertex_attributes) else: cli.terminate( message="No vertices loaded. Please specify a data source.") cli.print_to_console( f"Vertex load success. {num_upserted} vertices added.")
def load_edges( # Basic config config_name: str = typer.Argument(None, help=CONFIG_ARG_HELP), graph_name: str = typer.Argument(None, help=GRAPH_ARG_HELP), # Required items source_vertex_type: str = typer.Option( ..., "--source-type", help="Type name of the source vertex", prompt="Source vertex type"), source_vertex_id_col: str = typer.Option( ..., "--source-id", help="Column name for the source vertex ID", prompt="Source vertex ID column name"), target_vertex_type: str = typer.Option( ..., "--target-type", help="Type name of the target vertex", prompt="Target vertex type"), target_vertex_id_col: str = typer.Option( ..., "--target-id", help="Column name for the target vertex ID", prompt="Target vertex ID column name"), edge_type: str = typer.Option(..., "--edge-type", help="Type name of the edge", prompt="Edge type"), # No prompt here - must be provided via CLI (string split into chars because of typer processing) edge_attrs: List[str] = typer. Option( [], "--edge-attr", help= "Column name of an edge attribute, multiple can be specified by using the flag multiple times. " "If none are provided, all columns except for the source and target vertex ID columns are used.", ), # Data sources csv_filepath: Path = typer.Option( None, "--csv", help="CSV filepath to load vertices from.", exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True), pickle_filepath: Path = typer.Option( None, "--pickle", help="Pickle filepath to load vertices from.", exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True), json_filepath: Path = typer.Option( None, "--json", help="JSON filepath to load vertices from.", exists=True, file_okay=True, dir_okay=False, readable=True, resolve_path=True)): """Loads a set of edges from a given datasource.""" conn = get_initialized_tg_connection(config_name=config_name, graph_name=graph_name, require_graph=True) num_upserted: int = 0 df = None ignore_cols = {source_vertex_id_col, target_vertex_id_col} if csv_filepath: df = __get_df__(csv_filepath, "csv") elif pickle_filepath: df = __get_df__(pickle_filepath, "pickle") elif json_filepath: df = __get_df__(json_filepath, "json") if df is not None: edge_attributes = { val: val for val in df.columns if val not in ignore_cols } if edge_attrs: edge_attributes = {val: val for val in edge_attrs} num_upserted = conn.upsertEdgesDataframe( df=df, sourceVertexType=source_vertex_type, targetVertexType=target_vertex_type, edgeType=edge_type, from_id=source_vertex_id_col, to_id=target_vertex_id_col, attributes=edge_attributes) else: cli.terminate(message="No edges loaded. Please specify a data source.") cli.print_to_console(f"Edge load success. {num_upserted} edges added.")
def version(): cli.print_to_console(__version__)
def get_edges( # Basic config config_name: str = typer.Argument(None, help=CONFIG_ARG_HELP), graph_name: str = typer.Argument(None, help=GRAPH_ARG_HELP), # Required items source_vertex_type: str = typer.Option( ..., "--from-type", help="Type of the source vertex."), source_vertex_id: str = typer.Option(..., "--from-id", help="ID of the source vertex."), # Filter by target target_vertex_id: str = typer.Option(None, "--to-id", help="ID of the target vertex"), target_vertex_type: str = typer. Option( None, "--to-type", help="Type of the target vertex. Required if '--to-id' is specified."), edge_type: str = typer. Option( None, "--edge-type", help= "Type of the edge. Required if '--to-id' and '--to-type' are specified." ), # Generic query params attributes: List[str] = typer. Option( [], '--attr', help= "Attributes to return for each edge, multiple can be specified by using the flag multiple times. " "See https://docs.tigergraph.com/dev/restpp-api/built-in-endpoints#select." ), where: List[str] = typer. Option( [], '--where', help="A condition to match for returned edges, " "multiple can be specified by using the flag multiple times. " "Multiple conditions are joined with AND. " "See https://docs.tigergraph.com/dev/restpp-api/built-in-endpoints#filter. " "For string conditions, the literal can be escaped like so: '--where=gender=\\\"male\\\"'. " "Alternatively, string escapes can be replaced by the URL-encoded string '%22'." ), sort_by_attrs: List[str] = typer. Option( [], '--sort', help= "Attribute name to sort results by, multiple can be specified by using the flag multiple times. " "See https://docs.tigergraph.com/dev/restpp-api/built-in-endpoints#sort." ), limit: int = typer.Option( 10, '--limit', help="Maximum number of results to retrieve."), timeout: int = typer.Option(60, '--timeout', help="Timeout in seconds.")): """Get a set of edges""" conn = get_initialized_tg_connection(config_name=config_name, graph_name=graph_name, require_graph=True) if target_vertex_id and (not target_vertex_type or not edge_type): cli.terminate( message= "Target vertex ID is specified but target vertex type or edge type isn't.", is_err=True) output = conn.getEdges(sourceVertexType=source_vertex_type, sourceVertexId=source_vertex_id, targetVertexType=target_vertex_type, targetVertexId=target_vertex_id, edgeType=edge_type, select=preprocess_list_query(attributes), where=preprocess_list_query(where), sort=preprocess_list_query(sort_by_attrs), limit=limit, timeout=timeout) cli.print_to_console(output)
def get_schema(config_name: str, graph_name: str): """Retrieve the schema for the configuration""" conn = get_initialized_tg_connection(config_name=config_name, graph_name=graph_name) output = conn.getSchema() cli.print_to_console(output)
def get_vertices( # Basic config config_name: str = typer.Argument(None, help=CONFIG_ARG_HELP), graph_name: str = typer.Argument(None, help=GRAPH_ARG_HELP), # Required items vertex_type: str = typer.Option(..., "--type", help="Type of the vertex."), # Query by ID's. If given, ID's take precedence over the generic query vertex_ids: List[str] = typer. Option( [], "--id", help= "ID of the vertex to retrieve, multiple can be specified by using the flag multiple times. If " "this is specified, other query parameters are ignored."), # Generic query params attributes: List[str] = typer. Option( [], '--attr', help= "Attributes to return for each vertex, multiple can be specified by using the flag multiple times. " "See https://docs.tigergraph.com/dev/restpp-api/built-in-endpoints#select ." ), where: List[str] = typer. Option( [], '--where', help="A condition to match for returned vertices, " "multiple can be specified by using the flag multiple times. " "Multiple conditions are joined with AND. " "See https://docs.tigergraph.com/dev/restpp-api/built-in-endpoints#filter . " "For string conditions, the literal can be escaped like so: '--where=gender=\\\"male\\\"'. " "Alternatively, string escapes can be replaced by the URL-encoded string '%22'." ), sort_by_attrs: List[str] = typer. Option( [], '--sort', help= "Attribute name to sort results by, multiple can be specified by using the flag multiple times. " "See https://docs.tigergraph.com/dev/restpp-api/built-in-endpoints#sort." ), limit: int = typer.Option( 10, '--limit', help="Maximum number of results to retrieve."), timeout: int = typer.Option(60, '--timeout', help="Timeout in seconds.")): """Get a set of vertices, either by ID or by query""" conn = get_initialized_tg_connection(config_name=config_name, graph_name=graph_name, require_graph=True) if vertex_ids: # Given ID's give precedence output = conn.getVerticesById(vertex_type, resolve_multiple_args(vertex_ids)) else: output = conn.getVertices(vertex_type, select=preprocess_list_query(attributes), where=preprocess_list_query(where), sort=preprocess_list_query(sort_by_attrs), limit=limit, timeout=timeout) cli.print_to_console(output)
def describe_config(config_name: str, show_sensitive: bool): """Output all the parameters from a TgcliConfiguration, optionally showing sensitive data.""" configs = {} try: configs = get_configs(raise_on_nonexistent=True) except TgcliConfigurationError as e: cli.terminate(message=e.message, is_err=True) config = configs.get(config_name, None) if not config: cli.terminate(message=f"Configuration {config_name} not found.", is_err=True) cli.print_to_console(f"Configuration {config_name}") # Print configuration values cli.print_to_console(f"Server: {config.server}") cli.print_to_console(f"Client Version: {config.client_version}") cli.print_to_console(f"REST++ Port: {config.restpp_port}") cli.print_to_console(f"GS Port: {config.gs_port}") cli.print_to_console(f"Use Auth: {config.use_auth}") cli.print_to_console(f"Username: {config.username}") # Password password = "******" if show_sensitive: password = config.password cli.print_to_console(f"Password: {password}") # Secrets if show_sensitive: for graph_name, secret in config.secrets.items(): cli.print_to_console(f"Secret ({graph_name}): {secret}")