From f9742f0e071d4679d58223ac390782199423bb8c Mon Sep 17 00:00:00 2001 From: "K. Allagbe" Date: Thu, 21 Dec 2023 17:01:46 -0500 Subject: [PATCH] issue #21: implement versioning logic --- app/{blueprints => api}/__init__.py | 0 app/api/common/__init__.py | 0 app/{ => api/common}/ailab_db/__init__.py | 0 app/{ => api/common}/finesse_data/__init__.py | 0 app/api/v1/__init__.py | 13 ++++ app/api/v1/blueprints/__init__.py | 0 app/{ => api/v1}/blueprints/monitor.py | 0 app/{ => api/v1}/blueprints/search.py | 4 +- app/api/v2/__init__.py | 13 ++++ app/api/v2/blueprints/__init__.py | 0 app/api/v2/blueprints/monitor.py | 8 +++ app/api/v2/blueprints/search.py | 65 +++++++++++++++++++ app/app_creator.py | 9 +-- 13 files changed, 106 insertions(+), 6 deletions(-) rename app/{blueprints => api}/__init__.py (100%) create mode 100644 app/api/common/__init__.py rename app/{ => api/common}/ailab_db/__init__.py (100%) rename app/{ => api/common}/finesse_data/__init__.py (100%) create mode 100644 app/api/v1/__init__.py create mode 100644 app/api/v1/blueprints/__init__.py rename app/{ => api/v1}/blueprints/monitor.py (100%) rename app/{ => api/v1}/blueprints/search.py (94%) create mode 100644 app/api/v2/__init__.py create mode 100644 app/api/v2/blueprints/__init__.py create mode 100644 app/api/v2/blueprints/monitor.py create mode 100644 app/api/v2/blueprints/search.py diff --git a/app/blueprints/__init__.py b/app/api/__init__.py similarity index 100% rename from app/blueprints/__init__.py rename to app/api/__init__.py diff --git a/app/api/common/__init__.py b/app/api/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/ailab_db/__init__.py b/app/api/common/ailab_db/__init__.py similarity index 100% rename from app/ailab_db/__init__.py rename to app/api/common/ailab_db/__init__.py diff --git a/app/finesse_data/__init__.py b/app/api/common/finesse_data/__init__.py similarity index 100% rename from app/finesse_data/__init__.py rename to app/api/common/finesse_data/__init__.py diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py new file mode 100644 index 0000000..18f933a --- /dev/null +++ b/app/api/v1/__init__.py @@ -0,0 +1,13 @@ +from flask import Blueprint + +from .blueprints.monitor import monitor_blueprint +from .blueprints.search import search_blueprint + +VERSION = "1" + + +def get_blueprint(): + bp = Blueprint(VERSION, __name__) + bp.register_blueprint(monitor_blueprint, url_prefix="/health", strict_slashes=False) + bp.register_blueprint(search_blueprint, url_prefix="/search", strict_slashes=False) + return bp diff --git a/app/api/v1/blueprints/__init__.py b/app/api/v1/blueprints/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/blueprints/monitor.py b/app/api/v1/blueprints/monitor.py similarity index 100% rename from app/blueprints/monitor.py rename to app/api/v1/blueprints/monitor.py diff --git a/app/blueprints/search.py b/app/api/v1/blueprints/search.py similarity index 94% rename from app/blueprints/search.py rename to app/api/v1/blueprints/search.py index 5fd4daf..ba79b7d 100644 --- a/app/blueprints/search.py +++ b/app/api/v1/blueprints/search.py @@ -3,8 +3,8 @@ from flask import Blueprint, current_app, jsonify, request from index_search import AzureIndexSearchQueryError, search -from app.ailab_db import DBError, ailab_db_search -from app.finesse_data import FinesseDataFetchException, fetch_data +from app.api.common.ailab_db import DBError, ailab_db_search +from app.api.common.finesse_data import FinesseDataFetchException, fetch_data from app.utils import sanitize search_blueprint = Blueprint("finesse", __name__) diff --git a/app/api/v2/__init__.py b/app/api/v2/__init__.py new file mode 100644 index 0000000..03585d2 --- /dev/null +++ b/app/api/v2/__init__.py @@ -0,0 +1,13 @@ +from flask import Blueprint + +from .blueprints.monitor import monitor_blueprint +from .blueprints.search import search_blueprint + +VERSION = "2" + + +def get_blueprint(): + bp = Blueprint(VERSION, __name__) + bp.register_blueprint(monitor_blueprint, url_prefix="/health", strict_slashes=False) + bp.register_blueprint(search_blueprint, url_prefix="/search", strict_slashes=False) + return bp diff --git a/app/api/v2/blueprints/__init__.py b/app/api/v2/blueprints/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/v2/blueprints/monitor.py b/app/api/v2/blueprints/monitor.py new file mode 100644 index 0000000..4241e03 --- /dev/null +++ b/app/api/v2/blueprints/monitor.py @@ -0,0 +1,8 @@ +from flask import Blueprint + +monitor_blueprint = Blueprint("monitor", __name__) + + +@monitor_blueprint.route("", methods=["GET"]) +def health(): + return "ok", 200 diff --git a/app/api/v2/blueprints/search.py b/app/api/v2/blueprints/search.py new file mode 100644 index 0000000..ba79b7d --- /dev/null +++ b/app/api/v2/blueprints/search.py @@ -0,0 +1,65 @@ +from functools import wraps + +from flask import Blueprint, current_app, jsonify, request +from index_search import AzureIndexSearchQueryError, search + +from app.api.common.ailab_db import DBError, ailab_db_search +from app.api.common.finesse_data import FinesseDataFetchException, fetch_data +from app.utils import sanitize + +search_blueprint = Blueprint("finesse", __name__) + + +def require_non_empty_query(f): + @wraps(f) + def decorated_function(*args, **kwargs): + query = request.json.get("query") + if not query: + return jsonify({"message": current_app.config["ERROR_EMPTY_QUERY"]}), 400 + return f(*args, **kwargs) + + return decorated_function + + +@search_blueprint.route("/azure", methods=["POST"]) +@require_non_empty_query +def search_azure(): + query = request.json["query"] + query = sanitize(query, current_app.config["SANITIZE_PATTERN"]) + try: + results = search(query, current_app.config["AZURE_CONFIG"]) + return jsonify(results) + except AzureIndexSearchQueryError: + return jsonify({"error": current_app.config["ERROR_AZURE_FAILED"]}), 500 + except Exception: + return jsonify({"error": current_app.config["ERROR_UNEXPECTED"]}), 500 + + +@search_blueprint.route("/static", methods=["POST"]) +@require_non_empty_query +def search_static(): + finesse_data_url = current_app.config["FINESSE_DATA_URL"] + query = request.json["query"] + query = sanitize(query, current_app.config["SANITIZE_PATTERN"]) + match_threshold = current_app.config["FUZZY_MATCH_THRESHOLD"] + try: + data = fetch_data(finesse_data_url, query, match_threshold) + return jsonify(data) + except FinesseDataFetchException: + return jsonify({"error": current_app.config["ERROR_FINESSE_DATA_FAILED"]}), 500 + except Exception: + return jsonify({"error": current_app.config["ERROR_UNEXPECTED"]}), 500 + + +@search_blueprint.route("/ailab", methods=["POST"]) +@require_non_empty_query +def search_ailab_db(): + query = request.json["query"] + query = sanitize(query, current_app.config["SANITIZE_PATTERN"]) + try: + results = ailab_db_search(query) + return jsonify(results) + except DBError: + return jsonify({"error": current_app.config["ERROR_AILAB_FAILED"]}), 500 + except Exception: + return jsonify({"error": current_app.config["ERROR_UNEXPECTED"]}), 500 diff --git a/app/app_creator.py b/app/app_creator.py index d14d4b8..b64d6c2 100644 --- a/app/app_creator.py +++ b/app/app_creator.py @@ -9,12 +9,13 @@ def create_app(config: Config): CORS(app) app.config.from_object(config) - from .blueprints.monitor import monitor_blueprint - from .blueprints.search import search_blueprint + from .api import v1, v2 app.register_blueprint( - monitor_blueprint, url_prefix="/health", strict_slashes=False + v1.get_blueprint(), url_prefix=f"/api/v{v1.VERSION}", strict_slashes=False + ) + app.register_blueprint( + v2.get_blueprint(), url_prefix=f"/api/v{v2.VERSION}", strict_slashes=False ) - app.register_blueprint(search_blueprint, url_prefix="/search", strict_slashes=False) return app