diff --git a/pyproject.toml b/pyproject.toml index 233d3649..dac7e43a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "OpenGeodeWeb-Back" -version = "5.8.3" +version = "5.8.4-rc.2" dynamic = ["dependencies"] authors = [ { name="Geode-solutions", email="team-web@geode-solutions.com" }, diff --git a/requirements.in b/requirements.in index cba0f877..a9f7d46d 100644 --- a/requirements.in +++ b/requirements.in @@ -9,7 +9,7 @@ geode-simplex geode-explicit geode-implicit geode-common -jsonschema +fastjsonschema Flask[async] Flask-Cors werkzeug \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f60c72a2..4260add6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,6 +14,8 @@ blinker==1.9.0 # via flask click==8.2.1 # via flask +fastjsonschema==2.21.1 +# via -r requirements.in flask[async]==3.1.1 # via # -r requirements.in @@ -60,10 +62,6 @@ itsdangerous==2.2.0 # via flask jinja2==3.1.6 # via flask -jsonschema==4.24.0 - # via -r requirements.in -jsonschema-specifications==2025.4.1 - # via jsonschema markupsafe==3.0.2 # via # flask @@ -120,4 +118,4 @@ werkzeug==3.1.3 # via # -r requirements.in # flask - # flask-cors + # flask-cors \ No newline at end of file diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index 9daf75aa..5e64ac70 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -241,61 +241,16 @@ def geode_objects_and_output_extensions(): methods=save_viewable_file_json["methods"], ) def save_viewable_file(): + utils_functions.validate_request(flask.request, save_viewable_file_json) UPLOAD_FOLDER = flask.current_app.config["UPLOAD_FOLDER"] DATA_FOLDER_PATH = flask.current_app.config["DATA_FOLDER_PATH"] - utils_functions.validate_request(flask.request, save_viewable_file_json) - secure_filename = werkzeug.utils.secure_filename(flask.request.json["filename"]) file_path = os.path.abspath(os.path.join(UPLOAD_FOLDER, secure_filename)) data = geode_functions.load(flask.request.json["input_geode_object"], file_path) - generated_id = str(uuid.uuid4()).replace("-", "") - - name = data.name() - native_extension = data.native_extension() - - absolute_native_file_path = os.path.join( - UPLOAD_FOLDER, generated_id + "." + native_extension - ) - - saved_viewable_file_path = geode_functions.save_viewable( - flask.request.json["input_geode_object"], data, DATA_FOLDER_PATH, generated_id - ) - - saved_light_viewable_file_path = geode_functions.save_light_viewable( - flask.request.json["input_geode_object"], - data, - DATA_FOLDER_PATH, - "light_" + generated_id, - ) - - f = open(saved_light_viewable_file_path, "rb") - binary_light_viewable = f.read() - f.close() - - geode_functions.save( - flask.request.json["input_geode_object"], - data, - DATA_FOLDER_PATH, - generated_id + "." + native_extension, - ) - os.remove(os.path.join(UPLOAD_FOLDER, secure_filename)) - object_type = geode_functions.get_object_type( - flask.request.json["input_geode_object"] - ) - - native_file_name = os.path.basename(absolute_native_file_path) - viewable_file_name = os.path.basename(saved_viewable_file_path) - return flask.make_response( - { - "name": name, - "native_file_name": native_file_name, - "viewable_file_name": viewable_file_name, - "id": generated_id, - "object_type": object_type, - "binary_light_viewable": str(binary_light_viewable, "utf-8"), - }, - 200, + response_data = utils_functions.create_response_with_binary_light_viewable( + flask.request.json["input_geode_object"], data, DATA_FOLDER_PATH ) + return flask.jsonify(response_data), 200 with open(os.path.join(schemas, "create_point.json"), "r") as file: @@ -315,29 +270,10 @@ def create_point(): builder = geode_functions.create_builder("PointSet3D", PointSet3D) builder.create_point(opengeode.Point3D([x, y, z])) builder.set_name(title) - name = PointSet3D.name() - generated_id = str(uuid.uuid4()).replace("-", "") - object_type = geode_functions.get_object_type("PointSet3D") - saved_native_file_path = geode_functions.save( - "PointSet3D", PointSet3D, DATA_FOLDER_PATH, generated_id + ".og_pts3d" - ) - saved_viewable_file_path = geode_functions.save_viewable( - "PointSet3D", PointSet3D, DATA_FOLDER_PATH, generated_id - ) - - native_file_name = os.path.basename(saved_native_file_path[0]) - viewable_file_name = os.path.basename(saved_viewable_file_path) - return flask.make_response( - { - "viewable_file_name": os.path.basename(saved_viewable_file_path), - "id": generated_id, - "name": name, - "native_file_name": native_file_name, - "viewable_file_name": viewable_file_name, - "object_type": object_type, - "geode_object": "PointSet3D", - }, + utils_functions.create_response_with_binary_light_viewable( + "PointSet3D", PointSet3D, DATA_FOLDER_PATH + ), 200, ) diff --git a/src/opengeodeweb_back/test_utils.py b/src/opengeodeweb_back/test_utils.py index 0d4c6176..0888a4ad 100644 --- a/src/opengeodeweb_back/test_utils.py +++ b/src/opengeodeweb_back/test_utils.py @@ -12,14 +12,13 @@ def test_route_wrong_params(client, route, get_full_data): response = client.post(route, json=json) assert response.status_code == 400 error_description = response.json["description"] - assert error_description == f"Validation error: '{key}' is a required property" + assert "data must contain" in error_description + assert f"'{key}'" in error_description json = get_full_data() json["dumb_key"] = "dumb_value" response = client.post(route, json=json) assert response.status_code == 400 error_description = response.json["description"] - assert ( - error_description - == "Validation error: Additional properties are not allowed ('dumb_key' was unexpected)" - ) + assert "data must not contain" in error_description + assert "'dumb_key'" in error_description diff --git a/src/opengeodeweb_back/utils_functions.py b/src/opengeodeweb_back/utils_functions.py index 183d40de..4d9ba0a2 100644 --- a/src/opengeodeweb_back/utils_functions.py +++ b/src/opengeodeweb_back/utils_functions.py @@ -2,15 +2,16 @@ import os import threading import time +import uuid import zipfile # Third party imports import flask -from jsonschema import validate -from jsonschema.exceptions import ValidationError +import fastjsonschema import importlib.metadata as metadata # Local application imports +from . import geode_functions def increment_request_counter(current_app): @@ -82,9 +83,11 @@ def validate_request(request, schema): json_data = {} try: - validate(instance=json_data, schema=schema) - except ValidationError as e: - flask.abort(400, f"Validation error: {e.message}") + validate = fastjsonschema.compile(schema) + validate(json_data) + except fastjsonschema.JsonSchemaException as e: + error_msg = str(e) + flask.abort(400, error_msg) def set_interval(func, sec, args=None): @@ -138,3 +141,49 @@ def handle_exception(e): ) response.content_type = "application/json" return response + + +def save_native_viewable_binary_file_names(geode_object, data, folder_absolute_path): + generated_id = str(uuid.uuid4()).replace("-", "") + saved_native_file_path = geode_functions.save( + geode_object, + data, + folder_absolute_path, + generated_id + "." + data.native_extension(), + ) + saved_viewable_file_path = geode_functions.save_viewable( + geode_object, data, folder_absolute_path, generated_id + ) + saved_light_viewable_file_path = geode_functions.save_light_viewable( + geode_object, data, folder_absolute_path, "light_" + generated_id + ) + f = open(saved_light_viewable_file_path, "rb") + binary_light_viewable = f.read() + f.close() + return { + "native_file_name": os.path.basename(saved_native_file_path[0]), + "viewable_file_name": os.path.basename(saved_viewable_file_path[0]), + "binary_light_viewable": str(binary_light_viewable, "utf-8"), + } + + +def create_response_with_binary_light_viewable( + geode_object, data, folder_absolute_path +): + generated_id = str(uuid.uuid4()).replace("-", "") + name = data.name() + object_type = geode_functions.get_object_type(geode_object) + + native_file_name, viewable_file_name, binary_light_viewable = ( + save_native_viewable_binary_file_names(geode_object, data, folder_absolute_path) + ) + + return { + "name": name, + "native_file_name": native_file_name, + "viewable_file_name": viewable_file_name, + "id": generated_id, + "object_type": object_type, + "binary_light_viewable": binary_light_viewable, + "geode_object": geode_object, + }