From 57608405ab331fc4c18890321825dc5b9b7f2f4b Mon Sep 17 00:00:00 2001 From: JulienChampagnol Date: Fri, 30 Jan 2026 10:58:13 +0100 Subject: [PATCH 1/6] test --- .../routes/blueprint_routes.py | 136 +++++++++--------- .../routes/schemas/__init__.py | 1 - .../routes/schemas/import_extension.json | 10 -- .../routes/schemas/import_extension.py | 10 -- 4 files changed, 68 insertions(+), 89 deletions(-) delete mode 100644 src/opengeodeweb_back/routes/schemas/import_extension.json delete mode 100644 src/opengeodeweb_back/routes/schemas/import_extension.py diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index 88ab7689..5df6a828 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -558,71 +558,71 @@ def import_project() -> flask.Response: return flask.make_response({"snapshot": snapshot}, 200) -@routes.route( - schemas_dict["import_extension"]["route"], - methods=schemas_dict["import_extension"]["methods"], -) -def import_extension() -> flask.Response: - """Import a .vext extension file and extract its contents.""" - utils_functions.validate_request(flask.request, schemas_dict["import_extension"]) - - if "file" not in flask.request.files: - flask.abort(400, "No .vext file provided under 'file'") - - vext_file = flask.request.files["file"] - assert vext_file.filename is not None - filename = werkzeug.utils.secure_filename(os.path.basename(vext_file.filename)) - - if not filename.lower().endswith(".vext"): - flask.abort(400, "Uploaded file must be a .vext") - - # Create extensions directory in the data folder - extensions_folder = flask.current_app.config["EXTENSIONS_FOLDER_PATH"] - os.makedirs(extensions_folder, exist_ok=True) - - extension_name = ( - filename.rsplit("-", 1)[0] if "-" in filename else filename.replace(".vext", "") - ) - extension_path = os.path.join(extensions_folder, extension_name) - - # Remove existing extension if present - if os.path.exists(extension_path): - shutil.rmtree(extension_path) - - os.makedirs(extension_path, exist_ok=True) - - # Extract the .vext file - vext_file.stream.seek(0) - with zipfile.ZipFile(vext_file.stream) as zip_archive: - zip_archive.extractall(extension_path) - - # Look for the backend executable and frontend JS - backend_executable = None - frontend_file = None - - for file in os.listdir(extension_path): - file_path = os.path.join(extension_path, file) - if os.path.isfile(file_path): - if file.endswith(".es.js"): - frontend_file = file_path - elif not file.endswith(".js") and not file.endswith(".css"): - backend_executable = file_path - os.chmod(backend_executable, 0o755) - - if not frontend_file: - flask.abort(400, "Invalid .vext file: missing frontend JavaScript") - if not backend_executable: - flask.abort(400, "Invalid .vext file: missing backend executable") - - assert frontend_file is not None - with open(frontend_file, "r", encoding="utf-8") as f: - frontend_content = f.read() - - return flask.make_response( - { - "extension_name": extension_name, - "frontend_content": frontend_content, - "backend_path": backend_executable, - }, - 200, - ) +# @routes.route( +# schemas_dict["import_extension"]["route"], +# methods=schemas_dict["import_extension"]["methods"], +# ) +# def import_extension() -> flask.Response: +# """Import a .vext extension file and extract its contents.""" +# utils_functions.validate_request(flask.request, schemas_dict["import_extension"]) + +# if "file" not in flask.request.files: +# flask.abort(400, "No .vext file provided under 'file'") + +# vext_file = flask.request.files["file"] +# assert vext_file.filename is not None +# filename = werkzeug.utils.secure_filename(os.path.basename(vext_file.filename)) + +# if not filename.lower().endswith(".vext"): +# flask.abort(400, "Uploaded file must be a .vext") + +# # Create extensions directory in the data folder +# extensions_folder = flask.current_app.config["EXTENSIONS_FOLDER_PATH"] +# os.makedirs(extensions_folder, exist_ok=True) + +# extension_name = ( +# filename.rsplit("-", 1)[0] if "-" in filename else filename.replace(".vext", "") +# ) +# extension_path = os.path.join(extensions_folder, extension_name) + +# # Remove existing extension if present +# if os.path.exists(extension_path): +# shutil.rmtree(extension_path) + +# os.makedirs(extension_path, exist_ok=True) + +# # Extract the .vext file +# vext_file.stream.seek(0) +# with zipfile.ZipFile(vext_file.stream) as zip_archive: +# zip_archive.extractall(extension_path) + +# # Look for the backend executable and frontend JS +# backend_executable = None +# frontend_file = None + +# for file in os.listdir(extension_path): +# file_path = os.path.join(extension_path, file) +# if os.path.isfile(file_path): +# if file.endswith(".es.js"): +# frontend_file = file_path +# elif not file.endswith(".js") and not file.endswith(".css"): +# backend_executable = file_path +# os.chmod(backend_executable, 0o755) + +# if not frontend_file: +# flask.abort(400, "Invalid .vext file: missing frontend JavaScript") +# if not backend_executable: +# flask.abort(400, "Invalid .vext file: missing backend executable") + +# assert frontend_file is not None +# with open(frontend_file, "r", encoding="utf-8") as f: +# frontend_content = f.read() + +# return flask.make_response( +# { +# "extension_name": extension_name, +# "frontend_content": frontend_content, +# "backend_path": backend_executable, +# }, +# 200, +# ) diff --git a/src/opengeodeweb_back/routes/schemas/__init__.py b/src/opengeodeweb_back/routes/schemas/__init__.py index 763f0a24..344a09fc 100644 --- a/src/opengeodeweb_back/routes/schemas/__init__.py +++ b/src/opengeodeweb_back/routes/schemas/__init__.py @@ -9,7 +9,6 @@ from .kill import * from .inspect_file import * from .import_project import * -from .import_extension import * from .geographic_coordinate_systems import * from .geode_objects_and_output_extensions import * from .export_project import * diff --git a/src/opengeodeweb_back/routes/schemas/import_extension.json b/src/opengeodeweb_back/routes/schemas/import_extension.json deleted file mode 100644 index 4d80f553..00000000 --- a/src/opengeodeweb_back/routes/schemas/import_extension.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "route": "/import_extension", - "methods": [ - "POST" - ], - "type": "object", - "properties": {}, - "required": [], - "additionalProperties": false -} diff --git a/src/opengeodeweb_back/routes/schemas/import_extension.py b/src/opengeodeweb_back/routes/schemas/import_extension.py deleted file mode 100644 index 256cb5fa..00000000 --- a/src/opengeodeweb_back/routes/schemas/import_extension.py +++ /dev/null @@ -1,10 +0,0 @@ -from dataclasses_json import DataClassJsonMixin -from dataclasses import dataclass - - -@dataclass -class ImportExtension(DataClassJsonMixin): - def __post_init__(self) -> None: - print(self, flush=True) - - pass From 8420abd6a02ab3438120a3dbe6e27bb3fdb55310 Mon Sep 17 00:00:00 2001 From: JulienChampagnol Date: Wed, 11 Feb 2026 09:25:26 +0100 Subject: [PATCH 2/6] revert blueprint --- .../routes/blueprint_routes.py | 143 +++++++++--------- .../routes/schemas/import_extension.json | 10 ++ .../routes/schemas/import_extension.py | 10 ++ 3 files changed, 95 insertions(+), 68 deletions(-) create mode 100644 src/opengeodeweb_back/routes/schemas/import_extension.json create mode 100644 src/opengeodeweb_back/routes/schemas/import_extension.py diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index ad1b9d31..935ceda0 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -560,71 +560,78 @@ def import_project() -> flask.Response: return flask.make_response({"snapshot": snapshot}, 200) -# @routes.route( -# schemas_dict["import_extension"]["route"], -# methods=schemas_dict["import_extension"]["methods"], -# ) -# def import_extension() -> flask.Response: -# """Import a .vext extension file and extract its contents.""" -# utils_functions.validate_request(flask.request, schemas_dict["import_extension"]) - -# if "file" not in flask.request.files: -# flask.abort(400, "No .vext file provided under 'file'") - -# vext_file = flask.request.files["file"] -# assert vext_file.filename is not None -# filename = werkzeug.utils.secure_filename(os.path.basename(vext_file.filename)) - -# if not filename.lower().endswith(".vext"): -# flask.abort(400, "Uploaded file must be a .vext") - -# # Create extensions directory in the data folder -# extensions_folder = flask.current_app.config["EXTENSIONS_FOLDER_PATH"] -# os.makedirs(extensions_folder, exist_ok=True) - -# extension_name = ( -# filename.rsplit("-", 1)[0] if "-" in filename else filename.replace(".vext", "") -# ) -# extension_path = os.path.join(extensions_folder, extension_name) - -# # Remove existing extension if present -# if os.path.exists(extension_path): -# shutil.rmtree(extension_path) - -# os.makedirs(extension_path, exist_ok=True) - -# # Extract the .vext file -# vext_file.stream.seek(0) -# with zipfile.ZipFile(vext_file.stream) as zip_archive: -# zip_archive.extractall(extension_path) - -# # Look for the backend executable and frontend JS -# backend_executable = None -# frontend_file = None - -# for file in os.listdir(extension_path): -# file_path = os.path.join(extension_path, file) -# if os.path.isfile(file_path): -# if file.endswith(".es.js"): -# frontend_file = file_path -# elif not file.endswith(".js") and not file.endswith(".css"): -# backend_executable = file_path -# os.chmod(backend_executable, 0o755) - -# if not frontend_file: -# flask.abort(400, "Invalid .vext file: missing frontend JavaScript") -# if not backend_executable: -# flask.abort(400, "Invalid .vext file: missing backend executable") - -# assert frontend_file is not None -# with open(frontend_file, "r", encoding="utf-8") as f: -# frontend_content = f.read() - -# return flask.make_response( -# { -# "extension_name": extension_name, -# "frontend_content": frontend_content, -# "backend_path": backend_executable, -# }, -# 200, -# ) +@routes.route( + schemas_dict["import_extension"]["route"], + methods=schemas_dict["import_extension"]["methods"], +) +def import_extension() -> flask.Response: + """Import a .vext extension file and extract its contents.""" + utils_functions.validate_request(flask.request, schemas_dict["import_extension"]) + + if "file" not in flask.request.files: + flask.abort(400, "No .vext file provided under 'file'") + + vext_file = flask.request.files["file"] + assert vext_file.filename is not None + filename = werkzeug.utils.secure_filename(os.path.basename(vext_file.filename)) + + if not filename.lower().endswith(".vext"): + flask.abort(400, "Uploaded file must be a .vext") + + + # Create extensions directory in the data folder + extensions_folder = flask.current_app.config["EXTENSIONS_FOLDER_PATH"] + os.makedirs(extensions_folder, exist_ok=True) + + archive_path = os.path.join(extensions_folder, filename) + + # Remove existing extension if present + unziped_extension_path = os.path.join(archive_path.replace(".vext", "")) + if os.path.exists(unziped_extension_path): + shutil.rmtree(unziped_extension_path) + + os.makedirs(unziped_extension_path, exist_ok=True) + + # Extract the .vext file + vext_file.stream.seek(0) + with zipfile.ZipFile(vext_file.stream) as zip_archive: + zip_archive.extractall(unziped_extension_path) + + metadata_path = os.path.join(unziped_extension_path, "metadata.json") + if not os.path.isfile(metadata_path): + flask.abort(400, "Invalid .vext file: missing metadata.json") + with open(metadata_path, "r", encoding="utf-8") as f: + metadata = flask.json.load(f) + + + # Look for the backend executable and frontend JS + backend_executable = None + frontend_file = None + + for file in os.listdir(unziped_extension_path): + file_path = os.path.join(unziped_extension_path, file) + if os.path.isfile(file_path): + if file.endswith(".es.js"): + frontend_file = file_path + elif not file.endswith(".js") and not file.endswith(".css"): + backend_executable = file_path + os.chmod(backend_executable, 0o755) + + if not frontend_file: + flask.abort(400, "Invalid .vext file: missing frontend JavaScript") + if not backend_executable: + flask.abort(400, "Invalid .vext file: missing backend executable") + + assert frontend_file is not None + with open(frontend_file, "r", encoding="utf-8") as f: + frontend_content = f.read() + + return flask.make_response( + { + "extension_name": metadata["name"], + "extension_version": metadata["version"], + "frontend_content": frontend_content, + "backend_path": metadata["microservice_executable"], + }, + 200, + ) diff --git a/src/opengeodeweb_back/routes/schemas/import_extension.json b/src/opengeodeweb_back/routes/schemas/import_extension.json new file mode 100644 index 00000000..4d80f553 --- /dev/null +++ b/src/opengeodeweb_back/routes/schemas/import_extension.json @@ -0,0 +1,10 @@ +{ + "route": "/import_extension", + "methods": [ + "POST" + ], + "type": "object", + "properties": {}, + "required": [], + "additionalProperties": false +} diff --git a/src/opengeodeweb_back/routes/schemas/import_extension.py b/src/opengeodeweb_back/routes/schemas/import_extension.py new file mode 100644 index 00000000..256cb5fa --- /dev/null +++ b/src/opengeodeweb_back/routes/schemas/import_extension.py @@ -0,0 +1,10 @@ +from dataclasses_json import DataClassJsonMixin +from dataclasses import dataclass + + +@dataclass +class ImportExtension(DataClassJsonMixin): + def __post_init__(self) -> None: + print(self, flush=True) + + pass From 96b70237e612c6a3aa42ab9697ae7c4b934f4e99 Mon Sep 17 00:00:00 2001 From: JulienChampagnol Date: Thu, 12 Feb 2026 09:05:51 +0100 Subject: [PATCH 3/6] test remove import_extension [skip ci] --- .../routes/blueprint_routes.py | 77 ------------------- .../routes/schemas/import_extension.json | 10 --- .../routes/schemas/import_extension.py | 10 --- 3 files changed, 97 deletions(-) delete mode 100644 src/opengeodeweb_back/routes/schemas/import_extension.json delete mode 100644 src/opengeodeweb_back/routes/schemas/import_extension.py diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index 935ceda0..ded43e55 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -558,80 +558,3 @@ def import_project() -> flask.Response: except KeyError: snapshot = {} return flask.make_response({"snapshot": snapshot}, 200) - - -@routes.route( - schemas_dict["import_extension"]["route"], - methods=schemas_dict["import_extension"]["methods"], -) -def import_extension() -> flask.Response: - """Import a .vext extension file and extract its contents.""" - utils_functions.validate_request(flask.request, schemas_dict["import_extension"]) - - if "file" not in flask.request.files: - flask.abort(400, "No .vext file provided under 'file'") - - vext_file = flask.request.files["file"] - assert vext_file.filename is not None - filename = werkzeug.utils.secure_filename(os.path.basename(vext_file.filename)) - - if not filename.lower().endswith(".vext"): - flask.abort(400, "Uploaded file must be a .vext") - - - # Create extensions directory in the data folder - extensions_folder = flask.current_app.config["EXTENSIONS_FOLDER_PATH"] - os.makedirs(extensions_folder, exist_ok=True) - - archive_path = os.path.join(extensions_folder, filename) - - # Remove existing extension if present - unziped_extension_path = os.path.join(archive_path.replace(".vext", "")) - if os.path.exists(unziped_extension_path): - shutil.rmtree(unziped_extension_path) - - os.makedirs(unziped_extension_path, exist_ok=True) - - # Extract the .vext file - vext_file.stream.seek(0) - with zipfile.ZipFile(vext_file.stream) as zip_archive: - zip_archive.extractall(unziped_extension_path) - - metadata_path = os.path.join(unziped_extension_path, "metadata.json") - if not os.path.isfile(metadata_path): - flask.abort(400, "Invalid .vext file: missing metadata.json") - with open(metadata_path, "r", encoding="utf-8") as f: - metadata = flask.json.load(f) - - - # Look for the backend executable and frontend JS - backend_executable = None - frontend_file = None - - for file in os.listdir(unziped_extension_path): - file_path = os.path.join(unziped_extension_path, file) - if os.path.isfile(file_path): - if file.endswith(".es.js"): - frontend_file = file_path - elif not file.endswith(".js") and not file.endswith(".css"): - backend_executable = file_path - os.chmod(backend_executable, 0o755) - - if not frontend_file: - flask.abort(400, "Invalid .vext file: missing frontend JavaScript") - if not backend_executable: - flask.abort(400, "Invalid .vext file: missing backend executable") - - assert frontend_file is not None - with open(frontend_file, "r", encoding="utf-8") as f: - frontend_content = f.read() - - return flask.make_response( - { - "extension_name": metadata["name"], - "extension_version": metadata["version"], - "frontend_content": frontend_content, - "backend_path": metadata["microservice_executable"], - }, - 200, - ) diff --git a/src/opengeodeweb_back/routes/schemas/import_extension.json b/src/opengeodeweb_back/routes/schemas/import_extension.json deleted file mode 100644 index 4d80f553..00000000 --- a/src/opengeodeweb_back/routes/schemas/import_extension.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "route": "/import_extension", - "methods": [ - "POST" - ], - "type": "object", - "properties": {}, - "required": [], - "additionalProperties": false -} diff --git a/src/opengeodeweb_back/routes/schemas/import_extension.py b/src/opengeodeweb_back/routes/schemas/import_extension.py deleted file mode 100644 index 256cb5fa..00000000 --- a/src/opengeodeweb_back/routes/schemas/import_extension.py +++ /dev/null @@ -1,10 +0,0 @@ -from dataclasses_json import DataClassJsonMixin -from dataclasses import dataclass - - -@dataclass -class ImportExtension(DataClassJsonMixin): - def __post_init__(self) -> None: - print(self, flush=True) - - pass From 8a07a7f5d2ff508a9fb0cecd7a52fa9d162da441 Mon Sep 17 00:00:00 2001 From: JulienChampagnol Date: Fri, 6 Mar 2026 09:47:12 +0100 Subject: [PATCH 4/6] remove import_extension tests & schemas --- opengeodeweb_back_schemas.json | 170 ++++++++------------------------- tests/test_models_routes.py | 82 ---------------- 2 files changed, 38 insertions(+), 214 deletions(-) diff --git a/opengeodeweb_back_schemas.json b/opengeodeweb_back_schemas.json index a8a9bd9d..d5ba1a76 100644 --- a/opengeodeweb_back_schemas.json +++ b/opengeodeweb_back_schemas.json @@ -4,9 +4,7 @@ "point": { "$id": "opengeodeweb_back/create/point", "route": "/point", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "name": { @@ -23,12 +21,7 @@ "type": "number" } }, - "required": [ - "name", - "x", - "y", - "z" - ], + "required": ["name", "x", "y", "z"], "additionalProperties": false } }, @@ -36,9 +29,7 @@ "model_components": { "$id": "opengeodeweb_back/models/model_components", "route": "/model_components", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "id": { @@ -46,18 +37,14 @@ "minLength": 1 } }, - "required": [ - "id" - ], + "required": ["id"], "additionalProperties": false } }, "vertex_attribute_names": { "$id": "opengeodeweb_back/vertex_attribute_names", "route": "/vertex_attribute_names", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "id": { @@ -65,18 +52,13 @@ "minLength": 1 } }, - "required": [ - "id" - ], + "required": ["id"], "additionalProperties": false }, "upload_file": { "$id": "opengeodeweb_back/upload_file", "route": "/upload_file", - "methods": [ - "OPTIONS", - "PUT" - ], + "methods": ["OPTIONS", "PUT"], "type": "object", "properties": { "filename": { @@ -89,9 +71,7 @@ "texture_coordinates": { "$id": "opengeodeweb_back/texture_coordinates", "route": "/texture_coordinates", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "id": { @@ -99,17 +79,13 @@ "minLength": 1 } }, - "required": [ - "id" - ], + "required": ["id"], "additionalProperties": false }, "save_viewable_file": { "$id": "opengeodeweb_back/save_viewable_file", "route": "/save_viewable_file", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "geode_object_type": { @@ -121,18 +97,13 @@ "minLength": 1 } }, - "required": [ - "geode_object_type", - "filename" - ], + "required": ["geode_object_type", "filename"], "additionalProperties": false }, "polyhedron_attribute_names": { "$id": "opengeodeweb_back/polyhedron_attribute_names", "route": "/polyhedron_attribute_names", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "id": { @@ -140,17 +111,13 @@ "minLength": 1 } }, - "required": [ - "id" - ], + "required": ["id"], "additionalProperties": false }, "polygon_attribute_names": { "$id": "opengeodeweb_back/polygon_attribute_names", "route": "/polygon_attribute_names", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "id": { @@ -158,17 +125,13 @@ "minLength": 1 } }, - "required": [ - "id" - ], + "required": ["id"], "additionalProperties": false }, "ping": { "$id": "opengeodeweb_back/ping", "route": "/ping", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": {}, "required": [], @@ -177,9 +140,7 @@ "missing_files": { "$id": "opengeodeweb_back/missing_files", "route": "/missing_files", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "geode_object_type": { @@ -191,18 +152,13 @@ "minLength": 1 } }, - "required": [ - "geode_object_type", - "filename" - ], + "required": ["geode_object_type", "filename"], "additionalProperties": false }, "kill": { "$id": "opengeodeweb_back/kill", "route": "/kill", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": {}, "required": [], @@ -211,9 +167,7 @@ "inspect_file": { "$id": "opengeodeweb_back/inspect_file", "route": "/inspect_file", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "filename": { @@ -225,29 +179,13 @@ "minLength": 1 } }, - "required": [ - "filename", - "geode_object_type" - ], + "required": ["filename", "geode_object_type"], "additionalProperties": false }, "import_project": { "$id": "opengeodeweb_back/import_project", "route": "/import_project", - "methods": [ - "POST" - ], - "type": "object", - "properties": {}, - "required": [], - "additionalProperties": false - }, - "import_extension": { - "$id": "opengeodeweb_back/import_extension", - "route": "/import_extension", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": {}, "required": [], @@ -256,9 +194,7 @@ "geographic_coordinate_systems": { "$id": "opengeodeweb_back/geographic_coordinate_systems", "route": "/geographic_coordinate_systems", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "geode_object_type": { @@ -266,17 +202,13 @@ "minLength": 1 } }, - "required": [ - "geode_object_type" - ], + "required": ["geode_object_type"], "additionalProperties": false }, "geode_objects_and_output_extensions": { "$id": "opengeodeweb_back/geode_objects_and_output_extensions", "route": "/geode_objects_and_output_extensions", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "geode_object_type": { @@ -288,18 +220,13 @@ "minLength": 1 } }, - "required": [ - "geode_object_type", - "filename" - ], + "required": ["geode_object_type", "filename"], "additionalProperties": false }, "geode_object_inheritance": { "$id": "opengeodeweb_back/geode_object_inheritance", "route": "/geode_object_inheritance", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "geode_object_type": { @@ -307,17 +234,13 @@ "minLength": 1 } }, - "required": [ - "geode_object_type" - ], + "required": ["geode_object_type"], "additionalProperties": false }, "export_project": { "$id": "opengeodeweb_back/export_project", "route": "/export_project", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "snapshot": { @@ -328,18 +251,13 @@ "minLength": 1 } }, - "required": [ - "snapshot", - "filename" - ], + "required": ["snapshot", "filename"], "additionalProperties": false }, "edge_attribute_names": { "$id": "opengeodeweb_back/edge_attribute_names", "route": "/edge_attribute_names", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "id": { @@ -347,17 +265,13 @@ "minLength": 1 } }, - "required": [ - "id" - ], + "required": ["id"], "additionalProperties": false }, "cell_attribute_names": { "$id": "opengeodeweb_back/cell_attribute_names", "route": "/cell_attribute_names", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "id": { @@ -365,17 +279,13 @@ "minLength": 1 } }, - "required": [ - "id" - ], + "required": ["id"], "additionalProperties": false }, "allowed_objects": { "$id": "opengeodeweb_back/allowed_objects", "route": "/allowed_objects", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": { "filename": { @@ -383,21 +293,17 @@ "minLength": 1 } }, - "required": [ - "filename" - ], + "required": ["filename"], "additionalProperties": false }, "allowed_files": { "$id": "opengeodeweb_back/allowed_files", "route": "/allowed_files", - "methods": [ - "POST" - ], + "methods": ["POST"], "type": "object", "properties": {}, "required": [], "additionalProperties": false } } -} \ No newline at end of file +} diff --git a/tests/test_models_routes.py b/tests/test_models_routes.py index bcd05b63..9a9c4045 100644 --- a/tests/test_models_routes.py +++ b/tests/test_models_routes.py @@ -192,85 +192,3 @@ def test_save_viewable_workflow_from_object(client: FlaskClient) -> None: assert response.get_json()["viewable_file"].endswith(".vtp") -def test_import_extension_route(client: FlaskClient, tmp_path: Path) -> None: - """Test importing a .vext extension file.""" - route = "/opengeodeweb_back/import_extension" - original_data_folder = client.application.config["DATA_FOLDER_PATH"] - new_data_folder = os.path.join(str(tmp_path), "extension_test_data") - client.application.config["DATA_FOLDER_PATH"] = new_data_folder - client.application.config["EXTENSIONS_FOLDER_PATH"] = os.path.join( - new_data_folder, "extensions" - ) - vext_path = tmp_path / "test-extension-1.0.0.vext" - with zipfile.ZipFile(vext_path, "w", compression=zipfile.ZIP_DEFLATED) as zipf: - zipf.writestr( - "test-extension-extension.es.js", - "export const metadata = { id: 'test-extension', name: 'Test Extension' };", - ) - zipf.writestr("test-extension-back", "#!/bin/bash\necho 'mock backend'") - zipf.writestr("test-extension.css", ".test { color: red; }") - with open(vext_path, "rb") as f: - response = client.post( - route, - data={"file": (f, "test-extension-1.0.0.vext")}, - content_type="multipart/form-data", - ) - assert response.status_code == 200 - json_data = response.get_json() - assert "extension_name" in json_data - assert "frontend_content" in json_data - assert "backend_path" in json_data - assert json_data["extension_name"] == "test-extension" - extensions_folder = os.path.join( - client.application.config["DATA_FOLDER_PATH"], "extensions" - ) - extension_path = os.path.join(extensions_folder, "test-extension") - assert os.path.exists(extension_path) - - # Verify frontend content is returned - frontend_content = json_data["frontend_content"] - assert isinstance(frontend_content, str) - assert len(frontend_content) > 0 - assert "export const metadata" in frontend_content - - backend_exec = json_data["backend_path"] - assert os.path.exists(backend_exec) - assert os.access(backend_exec, os.X_OK) - client.application.config["DATA_FOLDER_PATH"] = original_data_folder - - -def test_import_extension_invalid_file(client: FlaskClient, tmp_path: Path) -> None: - """Test importing an invalid .vext file (missing dist folder).""" - route = "/opengeodeweb_back/import_extension" - original_data_folder = client.application.config["DATA_FOLDER_PATH"] - new_data_folder = os.path.join(str(tmp_path), "extension_invalid_test") - client.application.config["DATA_FOLDER_PATH"] = new_data_folder - client.application.config["EXTENSIONS_FOLDER_PATH"] = os.path.join( - new_data_folder, "extensions" - ) - vext_path = tmp_path / "invalid-extension.vext" - with zipfile.ZipFile(vext_path, "w") as zipf: - zipf.writestr("README.md", "This is invalid") - with open(vext_path, "rb") as f: - response = client.post( - route, - data={"file": (f, "invalid-extension.vext")}, - content_type="multipart/form-data", - ) - assert response.status_code == 400 - client.application.config["DATA_FOLDER_PATH"] = original_data_folder - - -def test_import_extension_wrong_extension(client: FlaskClient, tmp_path: Path) -> None: - """Test uploading a file with wrong extension.""" - route = "/opengeodeweb_back/import_extension" - wrong_file = tmp_path / "not-an-extension.zip" - with open(wrong_file, "wb") as f: - f.write(b"test content") - with open(wrong_file, "rb") as f: - response = client.post( - route, - data={"file": (f, "not-an-extension.zip")}, - content_type="multipart/form-data", - ) - assert response.status_code == 400 From 585a408745f32d1004cf77136e02493aad9f5ae8 Mon Sep 17 00:00:00 2001 From: JulienChampagnol <91873154+JulienChampagnol@users.noreply.github.com> Date: Fri, 6 Mar 2026 08:49:21 +0000 Subject: [PATCH 5/6] Apply prepare changes --- opengeodeweb_back_schemas.json | 159 +++++++++++++++++++++++++-------- requirements.txt | 1 - tests/test_models_routes.py | 2 - 3 files changed, 121 insertions(+), 41 deletions(-) diff --git a/opengeodeweb_back_schemas.json b/opengeodeweb_back_schemas.json index d5ba1a76..633025ee 100644 --- a/opengeodeweb_back_schemas.json +++ b/opengeodeweb_back_schemas.json @@ -4,7 +4,9 @@ "point": { "$id": "opengeodeweb_back/create/point", "route": "/point", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "name": { @@ -21,7 +23,12 @@ "type": "number" } }, - "required": ["name", "x", "y", "z"], + "required": [ + "name", + "x", + "y", + "z" + ], "additionalProperties": false } }, @@ -29,7 +36,9 @@ "model_components": { "$id": "opengeodeweb_back/models/model_components", "route": "/model_components", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "id": { @@ -37,14 +46,18 @@ "minLength": 1 } }, - "required": ["id"], + "required": [ + "id" + ], "additionalProperties": false } }, "vertex_attribute_names": { "$id": "opengeodeweb_back/vertex_attribute_names", "route": "/vertex_attribute_names", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "id": { @@ -52,13 +65,18 @@ "minLength": 1 } }, - "required": ["id"], + "required": [ + "id" + ], "additionalProperties": false }, "upload_file": { "$id": "opengeodeweb_back/upload_file", "route": "/upload_file", - "methods": ["OPTIONS", "PUT"], + "methods": [ + "OPTIONS", + "PUT" + ], "type": "object", "properties": { "filename": { @@ -71,7 +89,9 @@ "texture_coordinates": { "$id": "opengeodeweb_back/texture_coordinates", "route": "/texture_coordinates", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "id": { @@ -79,13 +99,17 @@ "minLength": 1 } }, - "required": ["id"], + "required": [ + "id" + ], "additionalProperties": false }, "save_viewable_file": { "$id": "opengeodeweb_back/save_viewable_file", "route": "/save_viewable_file", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "geode_object_type": { @@ -97,13 +121,18 @@ "minLength": 1 } }, - "required": ["geode_object_type", "filename"], + "required": [ + "geode_object_type", + "filename" + ], "additionalProperties": false }, "polyhedron_attribute_names": { "$id": "opengeodeweb_back/polyhedron_attribute_names", "route": "/polyhedron_attribute_names", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "id": { @@ -111,13 +140,17 @@ "minLength": 1 } }, - "required": ["id"], + "required": [ + "id" + ], "additionalProperties": false }, "polygon_attribute_names": { "$id": "opengeodeweb_back/polygon_attribute_names", "route": "/polygon_attribute_names", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "id": { @@ -125,13 +158,17 @@ "minLength": 1 } }, - "required": ["id"], + "required": [ + "id" + ], "additionalProperties": false }, "ping": { "$id": "opengeodeweb_back/ping", "route": "/ping", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": {}, "required": [], @@ -140,7 +177,9 @@ "missing_files": { "$id": "opengeodeweb_back/missing_files", "route": "/missing_files", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "geode_object_type": { @@ -152,13 +191,18 @@ "minLength": 1 } }, - "required": ["geode_object_type", "filename"], + "required": [ + "geode_object_type", + "filename" + ], "additionalProperties": false }, "kill": { "$id": "opengeodeweb_back/kill", "route": "/kill", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": {}, "required": [], @@ -167,7 +211,9 @@ "inspect_file": { "$id": "opengeodeweb_back/inspect_file", "route": "/inspect_file", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "filename": { @@ -179,13 +225,18 @@ "minLength": 1 } }, - "required": ["filename", "geode_object_type"], + "required": [ + "filename", + "geode_object_type" + ], "additionalProperties": false }, "import_project": { "$id": "opengeodeweb_back/import_project", "route": "/import_project", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": {}, "required": [], @@ -194,7 +245,9 @@ "geographic_coordinate_systems": { "$id": "opengeodeweb_back/geographic_coordinate_systems", "route": "/geographic_coordinate_systems", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "geode_object_type": { @@ -202,13 +255,17 @@ "minLength": 1 } }, - "required": ["geode_object_type"], + "required": [ + "geode_object_type" + ], "additionalProperties": false }, "geode_objects_and_output_extensions": { "$id": "opengeodeweb_back/geode_objects_and_output_extensions", "route": "/geode_objects_and_output_extensions", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "geode_object_type": { @@ -220,13 +277,18 @@ "minLength": 1 } }, - "required": ["geode_object_type", "filename"], + "required": [ + "geode_object_type", + "filename" + ], "additionalProperties": false }, "geode_object_inheritance": { "$id": "opengeodeweb_back/geode_object_inheritance", "route": "/geode_object_inheritance", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "geode_object_type": { @@ -234,13 +296,17 @@ "minLength": 1 } }, - "required": ["geode_object_type"], + "required": [ + "geode_object_type" + ], "additionalProperties": false }, "export_project": { "$id": "opengeodeweb_back/export_project", "route": "/export_project", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "snapshot": { @@ -251,13 +317,18 @@ "minLength": 1 } }, - "required": ["snapshot", "filename"], + "required": [ + "snapshot", + "filename" + ], "additionalProperties": false }, "edge_attribute_names": { "$id": "opengeodeweb_back/edge_attribute_names", "route": "/edge_attribute_names", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "id": { @@ -265,13 +336,17 @@ "minLength": 1 } }, - "required": ["id"], + "required": [ + "id" + ], "additionalProperties": false }, "cell_attribute_names": { "$id": "opengeodeweb_back/cell_attribute_names", "route": "/cell_attribute_names", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "id": { @@ -279,13 +354,17 @@ "minLength": 1 } }, - "required": ["id"], + "required": [ + "id" + ], "additionalProperties": false }, "allowed_objects": { "$id": "opengeodeweb_back/allowed_objects", "route": "/allowed_objects", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": { "filename": { @@ -293,17 +372,21 @@ "minLength": 1 } }, - "required": ["filename"], + "required": [ + "filename" + ], "additionalProperties": false }, "allowed_files": { "$id": "opengeodeweb_back/allowed_files", "route": "/allowed_files", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", "properties": {}, "required": [], "additionalProperties": false } } -} +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b855bc40..4504a827 100644 --- a/requirements.txt +++ b/requirements.txt @@ -60,4 +60,3 @@ werkzeug==3.1.2 # flask # flask-cors -opengeodeweb-microservice==1.*,>=1.0.15 diff --git a/tests/test_models_routes.py b/tests/test_models_routes.py index 9a9c4045..cb74ce66 100644 --- a/tests/test_models_routes.py +++ b/tests/test_models_routes.py @@ -190,5 +190,3 @@ def test_save_viewable_workflow_from_object(client: FlaskClient) -> None: assert isinstance(data_id, str) and len(data_id) > 0 assert response.get_json()["geode_object_type"] == "PointSet3D" assert response.get_json()["viewable_file"].endswith(".vtp") - - From 79c85538518b8000807c08850ea134ddffe87263 Mon Sep 17 00:00:00 2001 From: JulienChampagnol Date: Fri, 6 Mar 2026 11:55:37 +0100 Subject: [PATCH 6/6] fix(Extensions): remove route and tests