From f9060f9b13a644d24ff0f630fa2827fd1a0af40a Mon Sep 17 00:00:00 2001 From: Roi Lipman Date: Thu, 14 Nov 2024 14:23:39 +0200 Subject: [PATCH] simplify flask setup --- code_graph/app.py | 526 +++++++++++++++++++++++----------------------- 1 file changed, 259 insertions(+), 267 deletions(-) diff --git a/code_graph/app.py b/code_graph/app.py index a041e05..60aae90 100644 --- a/code_graph/app.py +++ b/code_graph/app.py @@ -12,6 +12,8 @@ # Load environment variables from .env file load_dotenv() +app = Flask(__name__) + # Configure the logger import logging logging.basicConfig(level=logging.DEBUG, @@ -33,293 +35,283 @@ def decorated_function(*args, **kwargs): return f(*args, **kwargs) return decorated_function -def create_app(): - app = Flask(__name__) - - @app.route('/graph_entities', methods=['GET']) - @token_required # Apply token authentication decorator - def graph_entities(): - """ - Endpoint to fetch sub-graph entities from a given repository. - The repository is specified via the 'repo' query parameter. - - Returns: - - 200: Successfully returns the sub-graph. - - 400: Missing or invalid 'repo' parameter. - - 500: Internal server error or database connection issue. - """ - - # Access the 'repo' parameter from the GET request - repo = request.args.get('repo') - - if not repo: - logging.error("Missing 'repo' parameter in request.") - return jsonify({"status": "Missing 'repo' parameter"}), 400 - - if not graph_exists(repo): - logging.error(f"Missing project {repo}") - return jsonify({"status": f"Missing project {repo}"}), 400 - - try: - # Initialize the graph with the provided repo and credentials - g = Graph(repo) - - # Retrieve a sub-graph of up to 100 entities - sub_graph = g.get_sub_graph(100) - - logging.info(f"Successfully retrieved sub-graph for repo: {repo}") - response = { - 'status': 'success', - 'entities': sub_graph - } - - return jsonify(response), 200 - - except Exception as e: - logging.error(f"Error retrieving sub-graph for repo '{repo}': {e}") - return jsonify({"status": "Internal server error"}), 500 - - - @app.route('/get_neighbors', methods=['GET']) - @token_required # Apply token authentication decorator - def get_neighbors(): - """ - Endpoint to get neighbors of a specific node in the graph. - Expects 'repo' and 'node_id' as query parameters. - - Returns: - JSON response containing neighbors or error messages. - """ - - # Get query parameters - repo = request.args.get('repo') - node_id = request.args.get('node_id') - - # Validate 'repo' parameter - if not repo: - logging.error("Repository name is missing in the request.") - return jsonify({"status": "Repository name is required."}), 400 - - # Validate 'node_id' parameter - if not node_id: - logging.error("Node ID is missing in the request.") - return jsonify({"status": "Node ID is required."}), 400 - - # Validate repo exists - if not graph_exists(repo): - logging.error(f"Missing project {repo}") - return jsonify({"status": f"Missing project {repo}"}), 400 - - # Try converting node_id to an integer - try: - node_id = int(node_id) - except ValueError: - logging.error(f"Invalid node ID: {node_id}. It must be an integer.") - return jsonify({"status": "Invalid node ID. It must be an integer."}), 400 - - # Initialize the graph with the provided repository - g = Graph(repo) - - # Fetch the neighbors of the specified node - neighbors = g.get_neighbors(node_id) - - # Log and return the neighbors - logging.info(f"Successfully retrieved neighbors for node ID {node_id} in repo '{repo}'.") - - response = { - 'status': 'success', - 'neighbors': neighbors - } - - return jsonify(response), 200 - - @app.route('/auto_complete', methods=['POST']) - @token_required # Apply token authentication decorator - def auto_complete(): - """ - Endpoint to process auto-completion requests for a repository based on a prefix. - - Returns: - JSON response with auto-completion suggestions or an error message. - """ - - # Get JSON data from the request - data = request.get_json() - - # Validate that 'repo' is provided - repo = data.get('repo') - if repo is None: - return jsonify({'status': f'Missing mandatory parameter "repo"'}), 400 - - # Validate that 'prefix' is provided - prefix = data.get('prefix') - if prefix is None: - return jsonify({'status': f'Missing mandatory parameter "prefix"'}), 400 - - # Validate repo exists - if not graph_exists(repo): - return jsonify({'status': f'Missing project {repo}'}), 400 - - # Fetch auto-completion results - completions = prefix_search(repo, prefix) - - # Create a success response - response = { - 'status': 'success', - 'completions': completions - } - - return jsonify(response), 200 - - - @app.route('/list_repos', methods=['GET']) - @token_required # Apply token authentication decorator - def list_repos(): - """ - Endpoint to list all available repositories. - - Returns: - JSON response with a list of repositories or an error message. - """ - - # Fetch list of repositories - repos = get_repos() - - # Create a success response with the list of repositories - response = { - 'status': 'success', - 'repositories': repos - } - - return jsonify(response), 200 +@app.route('/graph_entities', methods=['GET']) +@token_required # Apply token authentication decorator +def graph_entities(): + """ + Endpoint to fetch sub-graph entities from a given repository. + The repository is specified via the 'repo' query parameter. - @app.route('/repo_info', methods=['POST']) - @token_required # Apply token authentication decorator - def repo_info(): - """ - Endpoint to retrieve information about a specific repository. + Returns: + - 200: Successfully returns the sub-graph. + - 400: Missing or invalid 'repo' parameter. + - 500: Internal server error or database connection issue. + """ - Expected JSON payload: - { - "repo": - } + # Access the 'repo' parameter from the GET request + repo = request.args.get('repo') - Returns: - JSON: A response containing the status and graph statistics (node and edge counts). - - 'status': 'success' if successful, or an error message. - - 'info': A dictionary with the node and edge counts if the request is successful. - """ + if not repo: + logging.error("Missing 'repo' parameter in request.") + return jsonify({"status": "Missing 'repo' parameter"}), 400 - # Get JSON data from the request - data = request.get_json() + if not graph_exists(repo): + logging.error(f"Missing project {repo}") + return jsonify({"status": f"Missing project {repo}"}), 400 - # Validate the 'repo' parameter - repo = data.get('repo') - if repo is None: - return jsonify({'status': f'Missing mandatory parameter "repo"'}), 400 - - # Initialize the graph with the provided repository name + try: + # Initialize the graph with the provided repo and credentials g = Graph(repo) - # Retrieve statistics from the graph - stats = g.stats() - info = get_repo_info(repo) - - if stats is None or info is None: - return jsonify({'status': f'Missing repository "{repo}"'}), 400 - - stats |= info + # Retrieve a sub-graph of up to 100 entities + sub_graph = g.get_sub_graph(100) - # Create a response + logging.info(f"Successfully retrieved sub-graph for repo: {repo}") response = { 'status': 'success', - 'info': stats + 'entities': sub_graph } return jsonify(response), 200 - @app.route('/find_paths', methods=['POST']) - @token_required # Apply token authentication decorator - def find_paths(): - """ - Finds all paths between a source node (src) and a destination node (dest) in the graph. - The graph is associated with the repository (repo) provided in the request. - - Request Body (JSON): - - repo (str): Name of the repository. - - src (int): ID of the source node. - - dest (int): ID of the destination node. - - Returns: - A JSON response with: - - status (str): Status of the request ("success" or "error"). - - paths (list): List of paths between the source and destination nodes. - """ - - # Get JSON data from the request - data = request.get_json() - - # Validate 'repo' parameter - repo = data.get('repo') - if repo is None: - return jsonify({'status': f'Missing mandatory parameter "repo"'}), 400 - - # Validate 'src' parameter - src = data.get('src') - if src is None: - return jsonify({'status': f'Missing mandatory parameter "src"'}), 400 - if not isinstance(src, int): - return jsonify({'status': "src node id must be int"}), 400 - - # Validate 'dest' parameter - dest = data.get('dest') - if dest is None: - return jsonify({'status': f'Missing mandatory parameter "dest"'}), 400 - if not isinstance(dest, int): - return jsonify({'status': "dest node id must be int"}), 400 - - if not graph_exists(repo): - logging.error(f"Missing project {repo}") - return jsonify({"status": f"Missing project {repo}"}), 400 - - # Initialize graph with provided repo and credentials - g = Graph(repo) - - # Find paths between the source and destination nodes - paths = g.find_paths(src, dest) + except Exception as e: + logging.error(f"Error retrieving sub-graph for repo '{repo}': {e}") + return jsonify({"status": "Internal server error"}), 500 - # Create and return a successful response - response = { 'status': 'success', 'paths': paths } - return jsonify(response), 200 +@app.route('/get_neighbors', methods=['GET']) +@token_required # Apply token authentication decorator +def get_neighbors(): + """ + Endpoint to get neighbors of a specific node in the graph. + Expects 'repo' and 'node_id' as query parameters. - @app.route('/chat', methods=['POST']) - @token_required # Apply token authentication decorator - def chat(): - # Get JSON data from the request - data = request.get_json() + Returns: + JSON response containing neighbors or error messages. + """ - # Validate 'repo' parameter - repo = data.get('repo') - if repo is None: - return jsonify({'status': f'Missing mandatory parameter "repo"'}), 400 + # Get query parameters + repo = request.args.get('repo') + node_id = request.args.get('node_id') - # Get optional 'label' and 'relation' parameters - msg = data.get('msg') - if msg is None: - return jsonify({'status': f'Missing mandatory parameter "msg"'}), 400 - - answer = ask(repo, msg) - - # Create and return a successful response - response = { 'status': 'success', 'response': answer } - - return jsonify(response), 200 + # Validate 'repo' parameter + if not repo: + logging.error("Repository name is missing in the request.") + return jsonify({"status": "Repository name is required."}), 400 + # Validate 'node_id' parameter + if not node_id: + logging.error("Node ID is missing in the request.") + return jsonify({"status": "Node ID is required."}), 400 + + # Validate repo exists + if not graph_exists(repo): + logging.error(f"Missing project {repo}") + return jsonify({"status": f"Missing project {repo}"}), 400 + + # Try converting node_id to an integer + try: + node_id = int(node_id) + except ValueError: + logging.error(f"Invalid node ID: {node_id}. It must be an integer.") + return jsonify({"status": "Invalid node ID. It must be an integer."}), 400 + + # Initialize the graph with the provided repository + g = Graph(repo) + + # Fetch the neighbors of the specified node + neighbors = g.get_neighbors(node_id) + + # Log and return the neighbors + logging.info(f"Successfully retrieved neighbors for node ID {node_id} in repo '{repo}'.") + + response = { + 'status': 'success', + 'neighbors': neighbors + } + + return jsonify(response), 200 + +@app.route('/auto_complete', methods=['POST']) +@token_required # Apply token authentication decorator +def auto_complete(): + """ + Endpoint to process auto-completion requests for a repository based on a prefix. + + Returns: + JSON response with auto-completion suggestions or an error message. + """ + + # Get JSON data from the request + data = request.get_json() + + # Validate that 'repo' is provided + repo = data.get('repo') + if repo is None: + return jsonify({'status': f'Missing mandatory parameter "repo"'}), 400 + + # Validate that 'prefix' is provided + prefix = data.get('prefix') + if prefix is None: + return jsonify({'status': f'Missing mandatory parameter "prefix"'}), 400 + + # Validate repo exists + if not graph_exists(repo): + return jsonify({'status': f'Missing project {repo}'}), 400 + + # Fetch auto-completion results + completions = prefix_search(repo, prefix) + + # Create a success response + response = { + 'status': 'success', + 'completions': completions + } + + return jsonify(response), 200 + + +@app.route('/list_repos', methods=['GET']) +@token_required # Apply token authentication decorator +def list_repos(): + """ + Endpoint to list all available repositories. + + Returns: + JSON response with a list of repositories or an error message. + """ + + # Fetch list of repositories + repos = get_repos() + + # Create a success response with the list of repositories + response = { + 'status': 'success', + 'repositories': repos + } + + return jsonify(response), 200 + +@app.route('/repo_info', methods=['POST']) +@token_required # Apply token authentication decorator +def repo_info(): + """ + Endpoint to retrieve information about a specific repository. + + Expected JSON payload: + { + "repo": + } - return app + Returns: + JSON: A response containing the status and graph statistics (node and edge counts). + - 'status': 'success' if successful, or an error message. + - 'info': A dictionary with the node and edge counts if the request is successful. + """ + + # Get JSON data from the request + data = request.get_json() + + # Validate the 'repo' parameter + repo = data.get('repo') + if repo is None: + return jsonify({'status': f'Missing mandatory parameter "repo"'}), 400 + + # Initialize the graph with the provided repository name + g = Graph(repo) + + # Retrieve statistics from the graph + stats = g.stats() + info = get_repo_info(repo) + + if stats is None or info is None: + return jsonify({'status': f'Missing repository "{repo}"'}), 400 + + stats |= info + + # Create a response + response = { + 'status': 'success', + 'info': stats + } + + return jsonify(response), 200 + +@app.route('/find_paths', methods=['POST']) +@token_required # Apply token authentication decorator +def find_paths(): + """ + Finds all paths between a source node (src) and a destination node (dest) in the graph. + The graph is associated with the repository (repo) provided in the request. + + Request Body (JSON): + - repo (str): Name of the repository. + - src (int): ID of the source node. + - dest (int): ID of the destination node. + + Returns: + A JSON response with: + - status (str): Status of the request ("success" or "error"). + - paths (list): List of paths between the source and destination nodes. + """ + + # Get JSON data from the request + data = request.get_json() + + # Validate 'repo' parameter + repo = data.get('repo') + if repo is None: + return jsonify({'status': f'Missing mandatory parameter "repo"'}), 400 + + # Validate 'src' parameter + src = data.get('src') + if src is None: + return jsonify({'status': f'Missing mandatory parameter "src"'}), 400 + if not isinstance(src, int): + return jsonify({'status': "src node id must be int"}), 400 + + # Validate 'dest' parameter + dest = data.get('dest') + if dest is None: + return jsonify({'status': f'Missing mandatory parameter "dest"'}), 400 + if not isinstance(dest, int): + return jsonify({'status': "dest node id must be int"}), 400 + + if not graph_exists(repo): + logging.error(f"Missing project {repo}") + return jsonify({"status": f"Missing project {repo}"}), 400 + + # Initialize graph with provided repo and credentials + g = Graph(repo) + + # Find paths between the source and destination nodes + paths = g.find_paths(src, dest) + + # Create and return a successful response + response = { 'status': 'success', 'paths': paths } + + return jsonify(response), 200 + +@app.route('/chat', methods=['POST']) +@token_required # Apply token authentication decorator +def chat(): + # Get JSON data from the request + data = request.get_json() + + # Validate 'repo' parameter + repo = data.get('repo') + if repo is None: + return jsonify({'status': f'Missing mandatory parameter "repo"'}), 400 + + # Get optional 'label' and 'relation' parameters + msg = data.get('msg') + if msg is None: + return jsonify({'status': f'Missing mandatory parameter "msg"'}), 400 -if __name__ == '__main__': - app = create_app() - app.run(debug=True, port=80) + answer = ask(repo, msg) + + # Create and return a successful response + response = { 'status': 'success', 'response': answer } + + return jsonify(response), 200