diff --git a/opengeodeweb_back_schemas.json b/opengeodeweb_back_schemas.json index 3e72e45f..62d0cfda 100644 --- a/opengeodeweb_back_schemas.json +++ b/opengeodeweb_back_schemas.json @@ -63,7 +63,9 @@ "type": "string" }, "id": { - "type": "string" + "type": "string", + "minLength": 32, + "maxLength": 32 }, "name": { "type": "string" @@ -95,7 +97,9 @@ "type": "integer" }, "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -106,13 +110,17 @@ "boundaries": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "internals": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -137,7 +145,9 @@ "type": "object", "properties": { "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -148,7 +158,9 @@ "items": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -164,6 +176,11 @@ ], "additionalProperties": false } + }, + "geode_id": { + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "required": [ @@ -172,7 +189,8 @@ "name", "viewer_type", "is_viewable", - "geode_object_type" + "geode_object_type", + "geode_id" ], "additionalProperties": false } @@ -226,7 +244,9 @@ "type": "string" }, "id": { - "type": "string" + "type": "string", + "minLength": 32, + "maxLength": 32 }, "name": { "type": "string" @@ -258,7 +278,9 @@ "type": "integer" }, "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -269,13 +291,17 @@ "boundaries": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "internals": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -300,7 +326,9 @@ "type": "object", "properties": { "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -311,7 +339,9 @@ "items": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -327,6 +357,11 @@ ], "additionalProperties": false } + }, + "geode_id": { + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "required": [ @@ -335,7 +370,8 @@ "name", "viewer_type", "is_viewable", - "geode_object_type" + "geode_object_type", + "geode_id" ], "additionalProperties": false } @@ -403,7 +439,9 @@ "type": "string" }, "id": { - "type": "string" + "type": "string", + "minLength": 32, + "maxLength": 32 }, "name": { "type": "string" @@ -435,7 +473,9 @@ "type": "integer" }, "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -446,13 +486,17 @@ "boundaries": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "internals": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -477,7 +521,9 @@ "type": "object", "properties": { "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -488,7 +534,9 @@ "items": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -504,6 +552,11 @@ ], "additionalProperties": false } + }, + "geode_id": { + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "required": [ @@ -512,7 +565,8 @@ "name", "viewer_type", "is_viewable", - "geode_object_type" + "geode_object_type", + "geode_id" ], "additionalProperties": false } @@ -528,7 +582,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ @@ -603,7 +658,9 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "minLength": 32, + "maxLength": 32 } }, "required": [ @@ -672,7 +729,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ @@ -724,7 +782,9 @@ "type": "string" }, "id": { - "type": "string" + "type": "string", + "minLength": 32, + "maxLength": 32 }, "name": { "type": "string" @@ -756,7 +816,9 @@ "type": "integer" }, "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -767,13 +829,17 @@ "boundaries": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "internals": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -798,7 +864,9 @@ "type": "object", "properties": { "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -809,7 +877,9 @@ "items": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -825,6 +895,11 @@ ], "additionalProperties": false } + }, + "geode_id": { + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "required": [ @@ -833,7 +908,8 @@ "name", "viewer_type", "is_viewable", - "geode_object_type" + "geode_object_type", + "geode_id" ], "additionalProperties": false } @@ -848,7 +924,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ @@ -924,7 +1001,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ @@ -1023,13 +1101,15 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 }, "component_ids": { "type": "array", "items": { "type": "string", - "minLength": 1 + "minLength": 36, + "maxLength": 36 }, "minItems": 1 } @@ -1108,13 +1188,15 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 }, "component_ids": { "type": "array", "items": { "type": "string", - "minLength": 1 + "minLength": 36, + "maxLength": 36 }, "minItems": 1 } @@ -1193,13 +1275,15 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 }, "component_ids": { "type": "array", "items": { "type": "string", - "minLength": 1 + "minLength": 36, + "maxLength": 36 }, "minItems": 1 } @@ -1278,13 +1362,15 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 }, "component_ids": { "type": "array", "items": { "type": "string", - "minLength": 1 + "minLength": 36, + "maxLength": 36 }, "minItems": 1 } @@ -1614,7 +1700,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ @@ -1690,7 +1777,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ diff --git a/opengeodeweb_back_typed_schemas.d.ts b/opengeodeweb_back_typed_schemas.d.ts index 7becd36e..998c8a15 100644 --- a/opengeodeweb_back_typed_schemas.d.ts +++ b/opengeodeweb_back_typed_schemas.d.ts @@ -16,6 +16,7 @@ export interface PolygonalSurfacePoint { export interface CreatePolygonalSurfaceResponse { binary_light_viewable?: string; collection_components?: CreatePolygonalSurfaceResponseCollectionComponent[]; + geode_id: string; geode_object_type: string; id: string; is_viewable: boolean; @@ -59,6 +60,7 @@ export interface Point { export interface CreatePointSetResponse { binary_light_viewable?: string; collection_components?: CreatePointSetResponseCollectionComponent[]; + geode_id: string; geode_object_type: string; id: string; is_viewable: boolean; @@ -103,6 +105,7 @@ export interface EdgedCurvePoint { export interface CreateEdgedCurveResponse { binary_light_viewable?: string; collection_components?: CreateEdgedCurveResponseCollectionComponent[]; + geode_id: string; geode_object_type: string; id: string; is_viewable: boolean; @@ -185,6 +188,7 @@ export interface SaveViewableFileParams { export interface SaveViewableFileResponse { binary_light_viewable?: string; collection_components?: SaveViewableFileResponseCollectionComponent[]; + geode_id: string; geode_object_type: string; id: string; is_viewable: boolean; diff --git a/requirements.txt b/requirements.txt index 35032b5c..39a99cfa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -66,4 +66,3 @@ werkzeug==3.1.8 # flask # flask-cors -opengeodeweb-microservice==1.*,>=1.3.0rc1 diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index 64e23c56..4e7d3bb9 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -649,7 +649,8 @@ def get_all_bases(geode_class: type) -> set[type]: return bases def get_all_subclasses(geode_class: type) -> set[type]: - subclasses = set() + subclasses: set[type] = set() + subclass_class: type for subclass_class in geode_class.__subclasses__(): subclasses.add(subclass_class) subclasses.update(get_all_subclasses(subclass_class)) diff --git a/src/opengeodeweb_back/routes/create/schemas/edged_curve.json b/src/opengeodeweb_back/routes/create/schemas/edged_curve.json index 714b005f..6e050553 100644 --- a/src/opengeodeweb_back/routes/create/schemas/edged_curve.json +++ b/src/opengeodeweb_back/routes/create/schemas/edged_curve.json @@ -60,7 +60,9 @@ "type": "string" }, "id": { - "type": "string" + "type": "string", + "minLength": 32, + "maxLength": 32 }, "name": { "type": "string" @@ -92,7 +94,9 @@ "type": "integer" }, "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -103,13 +107,17 @@ "boundaries": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "internals": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -134,7 +142,9 @@ "type": "object", "properties": { "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -145,7 +155,9 @@ "items": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -161,6 +173,11 @@ ], "additionalProperties": false } + }, + "geode_id": { + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "required": [ @@ -169,7 +186,8 @@ "name", "viewer_type", "is_viewable", - "geode_object_type" + "geode_object_type", + "geode_id" ], "additionalProperties": false } diff --git a/src/opengeodeweb_back/routes/create/schemas/edged_curve.py b/src/opengeodeweb_back/routes/create/schemas/edged_curve.py index 260c1c8a..7f7d87bd 100644 --- a/src/opengeodeweb_back/routes/create/schemas/edged_curve.py +++ b/src/opengeodeweb_back/routes/create/schemas/edged_curve.py @@ -57,6 +57,7 @@ class EdgedCurveResponse(DataClassJsonMixin): def __post_init__(self) -> None: print_dataclass(self) + geode_id: str geode_object_type: str id: str is_viewable: bool diff --git a/src/opengeodeweb_back/routes/create/schemas/point_set.json b/src/opengeodeweb_back/routes/create/schemas/point_set.json index 2a0995ca..04b66ba5 100644 --- a/src/opengeodeweb_back/routes/create/schemas/point_set.json +++ b/src/opengeodeweb_back/routes/create/schemas/point_set.json @@ -46,7 +46,9 @@ "type": "string" }, "id": { - "type": "string" + "type": "string", + "minLength": 32, + "maxLength": 32 }, "name": { "type": "string" @@ -78,7 +80,9 @@ "type": "integer" }, "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -89,13 +93,17 @@ "boundaries": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "internals": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -120,7 +128,9 @@ "type": "object", "properties": { "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -131,7 +141,9 @@ "items": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -147,6 +159,11 @@ ], "additionalProperties": false } + }, + "geode_id": { + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "required": [ @@ -155,7 +172,8 @@ "name", "viewer_type", "is_viewable", - "geode_object_type" + "geode_object_type", + "geode_id" ], "additionalProperties": false } diff --git a/src/opengeodeweb_back/routes/create/schemas/point_set.py b/src/opengeodeweb_back/routes/create/schemas/point_set.py index 78fe3728..b28e6e0b 100644 --- a/src/opengeodeweb_back/routes/create/schemas/point_set.py +++ b/src/opengeodeweb_back/routes/create/schemas/point_set.py @@ -56,6 +56,7 @@ class PointSetResponse(DataClassJsonMixin): def __post_init__(self) -> None: print_dataclass(self) + geode_id: str geode_object_type: str id: str is_viewable: bool diff --git a/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.json b/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.json index 4351c9be..187cf17e 100644 --- a/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.json +++ b/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.json @@ -59,7 +59,9 @@ "type": "string" }, "id": { - "type": "string" + "type": "string", + "minLength": 32, + "maxLength": 32 }, "name": { "type": "string" @@ -91,7 +93,9 @@ "type": "integer" }, "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -102,13 +106,17 @@ "boundaries": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "internals": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -133,7 +141,9 @@ "type": "object", "properties": { "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -144,7 +154,9 @@ "items": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -160,6 +172,11 @@ ], "additionalProperties": false } + }, + "geode_id": { + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "required": [ @@ -168,7 +185,8 @@ "name", "viewer_type", "is_viewable", - "geode_object_type" + "geode_object_type", + "geode_id" ], "additionalProperties": false } diff --git a/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.py b/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.py index e0ecb46e..24344104 100644 --- a/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.py +++ b/src/opengeodeweb_back/routes/create/schemas/polygonal_surface.py @@ -57,6 +57,7 @@ class PolygonalSurfaceResponse(DataClassJsonMixin): def __post_init__(self) -> None: print_dataclass(self) + geode_id: str geode_object_type: str id: str is_viewable: bool diff --git a/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json b/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json index d6bc6764..455c9b6f 100644 --- a/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json @@ -7,7 +7,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ diff --git a/src/opengeodeweb_back/routes/schemas/edge_attribute_names.json b/src/opengeodeweb_back/routes/schemas/edge_attribute_names.json index de60b5da..275e06a5 100644 --- a/src/opengeodeweb_back/routes/schemas/edge_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/edge_attribute_names.json @@ -7,7 +7,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ diff --git a/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json b/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json index bf458b20..4b7e2ca9 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/model_component_edge_attribute_names.json @@ -7,13 +7,15 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 }, "component_ids": { "type": "array", "items": { "type": "string", - "minLength": 1 + "minLength": 36, + "maxLength": 36 }, "minItems": 1 } diff --git a/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json b/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json index ce1b46d9..174dbd26 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/model_component_polygon_attribute_names.json @@ -7,13 +7,15 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 }, "component_ids": { "type": "array", "items": { "type": "string", - "minLength": 1 + "minLength": 36, + "maxLength": 36 }, "minItems": 1 } diff --git a/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json b/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json index acc14fff..b4403082 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/model_component_polyhedron_attribute_names.json @@ -7,13 +7,15 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 }, "component_ids": { "type": "array", "items": { "type": "string", - "minLength": 1 + "minLength": 36, + "maxLength": 36 }, "minItems": 1 } diff --git a/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json b/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json index 5e02652b..e5a82e74 100644 --- a/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/model_component_vertex_attribute_names.json @@ -7,13 +7,15 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 }, "component_ids": { "type": "array", "items": { "type": "string", - "minLength": 1 + "minLength": 36, + "maxLength": 36 }, "minItems": 1 } diff --git a/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json b/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json index f8e8eb3e..a069701b 100644 --- a/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/polygon_attribute_names.json @@ -7,7 +7,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ diff --git a/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json b/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json index 8483a781..e527a2be 100644 --- a/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/polyhedron_attribute_names.json @@ -7,7 +7,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ diff --git a/src/opengeodeweb_back/routes/schemas/save_viewable_file.json b/src/opengeodeweb_back/routes/schemas/save_viewable_file.json index 48048e81..c3335d36 100644 --- a/src/opengeodeweb_back/routes/schemas/save_viewable_file.json +++ b/src/opengeodeweb_back/routes/schemas/save_viewable_file.json @@ -26,7 +26,9 @@ "type": "string" }, "id": { - "type": "string" + "type": "string", + "minLength": 32, + "maxLength": 32 }, "name": { "type": "string" @@ -58,7 +60,9 @@ "type": "integer" }, "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -69,13 +73,17 @@ "boundaries": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "internals": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -100,7 +108,9 @@ "type": "object", "properties": { "geode_id": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 }, "name": { "type": "string" @@ -111,7 +121,9 @@ "items": { "type": "array", "items": { - "type": "string" + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "is_active": { @@ -127,6 +139,11 @@ ], "additionalProperties": false } + }, + "geode_id": { + "type": "string", + "minLength": 36, + "maxLength": 36 } }, "required": [ @@ -135,7 +152,8 @@ "name", "viewer_type", "is_viewable", - "geode_object_type" + "geode_object_type", + "geode_id" ], "additionalProperties": false } diff --git a/src/opengeodeweb_back/routes/schemas/save_viewable_file.py b/src/opengeodeweb_back/routes/schemas/save_viewable_file.py index 4bad3022..71d3a181 100644 --- a/src/opengeodeweb_back/routes/schemas/save_viewable_file.py +++ b/src/opengeodeweb_back/routes/schemas/save_viewable_file.py @@ -45,6 +45,7 @@ class SaveViewableFileResponse(DataClassJsonMixin): def __post_init__(self) -> None: print_dataclass(self) + geode_id: str geode_object_type: str id: str is_viewable: bool diff --git a/src/opengeodeweb_back/routes/schemas/texture_coordinates.json b/src/opengeodeweb_back/routes/schemas/texture_coordinates.json index 398295b5..49ab2ceb 100644 --- a/src/opengeodeweb_back/routes/schemas/texture_coordinates.json +++ b/src/opengeodeweb_back/routes/schemas/texture_coordinates.json @@ -7,7 +7,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ diff --git a/src/opengeodeweb_back/routes/schemas/validate.json b/src/opengeodeweb_back/routes/schemas/validate.json index fd0f249a..8be97456 100644 --- a/src/opengeodeweb_back/routes/schemas/validate.json +++ b/src/opengeodeweb_back/routes/schemas/validate.json @@ -6,7 +6,9 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "minLength": 32, + "maxLength": 32 } }, "required": [ diff --git a/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json b/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json index 3eefd1ce..9939d854 100644 --- a/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json +++ b/src/opengeodeweb_back/routes/schemas/vertex_attribute_names.json @@ -7,7 +7,8 @@ "properties": { "id": { "type": "string", - "minLength": 1 + "minLength": 32, + "maxLength": 32 } }, "required": [ diff --git a/src/opengeodeweb_back/utils_functions.py b/src/opengeodeweb_back/utils_functions.py index 6ae05c84..dc6a1b99 100644 --- a/src/opengeodeweb_back/utils_functions.py +++ b/src/opengeodeweb_back/utils_functions.py @@ -1,8 +1,10 @@ # Standard library imports import base64 +import hashlib import os import threading import time +import uuid import xml.etree.ElementTree as ET import zipfile from collections.abc import Callable @@ -16,6 +18,7 @@ import shutil from werkzeug.exceptions import HTTPException import werkzeug +import opengeode as og from opengeodeweb_microservice.schemas import ErrorResponse, SchemaDict from opengeodeweb_microservice.database.data import Data from opengeodeweb_microservice.database.connection import get_session @@ -206,6 +209,12 @@ def create_data_folder_from_id(data_id: str) -> str: return data_path +def content_based_uuid(file_path: str) -> og.uuid: + with open(file_path, "rb") as file: + digest = hashlib.file_digest(file, "sha256").hexdigest() + return og.uuid(str(uuid.uuid5(uuid.NAMESPACE_OID, digest))) + + def model_components( data_id: str, model: GeodeModel, viewable_file: str | None ) -> dict[str, Any]: @@ -328,6 +337,7 @@ def save_all_viewables_and_return_info( response: dict[str, Any] = { "native_file": data.native_file, "id": data.id, + "geode_id": data.geode_id, "name": name, "viewer_type": data.viewer_object, "is_viewable": geode_object.is_viewable(), @@ -347,6 +357,7 @@ def generate_files_from_object( geode_object: GeodeObject, ) -> dict[str, Any]: data = Data.create( + geode_id=geode_object.identifier.id().string(), geode_object=geode_object.geode_object_type(), viewer_object=geode_object.viewer_type(), viewer_elements_type=geode_object.viewer_elements_type(), @@ -359,13 +370,16 @@ def generate_files_from_file( geode_object_type: GeodeObjectType, input_file: str ) -> dict[str, Any]: generic_geode_object = geode_objects[geode_object_type] + full_input_filename = geode_functions.upload_file_path(input_file) + geode_object = generic_geode_object.load(full_input_filename) + geode_object.builder().set_name(os.path.splitext(input_file)[0]) + if not input_file.lower().endswith("." + geode_object.native_extension()): + geode_object.builder().set_id(content_based_uuid(full_input_filename)) data = Data.create( + geode_id=geode_object.identifier.id().string(), geode_object=geode_object_type, viewer_object=generic_geode_object.viewer_type(), viewer_elements_type=generic_geode_object.viewer_elements_type(), ) data_path = create_data_folder_from_id(data.id) - full_input_filename = geode_functions.upload_file_path(input_file) - geode_object = generic_geode_object.load(full_input_filename) - geode_object.builder().set_name(os.path.splitext(input_file)[0]) return save_all_viewables_and_return_info(geode_object, data, data_path) diff --git a/tests/conftest.py b/tests/conftest.py index e558da8f..d543e495 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,6 +13,7 @@ # Local application imports from opengeodeweb_back.app import create_app, register_ogw_back_blueprints +from opengeodeweb_microservice.database import connection from opengeodeweb_microservice.database.connection import init_database TEST_ID = "1" @@ -34,7 +35,10 @@ def configure_test_environment() -> Generator[None, None, None]: app.config["DATA_FOLDER_PATH"] = "./data/" app.config["UPLOAD_FOLDER_PATH"] = "./tests/data/" - db_path = os.path.join(base_path, "data", "project.db") + # The database lives in the data folder like in the app (DATA_FOLDER_PATH/project.db), so it is removed with it at session end. + db_path = os.path.abspath( + os.path.join(app.config["DATA_FOLDER_PATH"], "project.db") + ) app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}" print("Current working directory:", os.getcwd()) @@ -45,6 +49,10 @@ def configure_test_environment() -> Generator[None, None, None]: register_ogw_back_blueprints(app) yield + if connection.scoped_session_registry: + connection.scoped_session_registry.remove() + if connection.engine: + connection.engine.dispose() tmp_data_path = app.config.get("DATA_FOLDER_PATH") if tmp_data_path and os.path.exists(tmp_data_path): shutil.rmtree(tmp_data_path, ignore_errors=True) diff --git a/tests/test_routes.py b/tests/test_routes.py index 3db46707..24e56aca 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -31,6 +31,7 @@ base_dir = os.path.abspath(os.path.dirname(__file__)) data_dir = os.path.join(base_dir, "data") +DUMMY_GEODE_ID = "00000000-0000-0000-0000-000000000000" def test_allowed_files(client: FlaskClient) -> None: @@ -296,6 +297,7 @@ def test_texture_coordinates(client: FlaskClient, test_id: str) -> None: with client.application.app_context(): file = os.path.join(data_dir, "hat.vtp") data = Data.create( + geode_id=DUMMY_GEODE_ID, geode_object=GeodePolygonalSurface3D.geode_object_type(), viewer_object=GeodePolygonalSurface3D.viewer_type(), viewer_elements_type=GeodePolygonalSurface3D.viewer_elements_type(), @@ -324,6 +326,7 @@ def test_vertex_attribute_names(client: FlaskClient, test_id: str) -> None: with client.application.app_context(): file = os.path.join(data_dir, "test.vtp") data = Data.create( + geode_id=DUMMY_GEODE_ID, geode_object=GeodePolygonalSurface3D.geode_object_type(), viewer_object=GeodePolygonalSurface3D.viewer_type(), viewer_elements_type=GeodePolygonalSurface3D.viewer_elements_type(), @@ -362,6 +365,7 @@ def test_cell_attribute_names(client: FlaskClient, test_id: str) -> None: with client.application.app_context(): file = os.path.join(data_dir, "test.og_rgd2d") data = Data.create( + geode_id=DUMMY_GEODE_ID, geode_object=GeodeRegularGrid2D.geode_object_type(), viewer_object=GeodeRegularGrid2D.viewer_type(), viewer_elements_type=GeodeRegularGrid2D.viewer_elements_type(), @@ -400,6 +404,7 @@ def test_polygon_attribute_names(client: FlaskClient, test_id: str) -> None: with client.application.app_context(): file = os.path.join(data_dir, "test.vtp") data = Data.create( + geode_id=DUMMY_GEODE_ID, geode_object=GeodePolygonalSurface3D.geode_object_type(), viewer_object=GeodePolygonalSurface3D.viewer_type(), viewer_elements_type=GeodePolygonalSurface3D.viewer_elements_type(), @@ -438,6 +443,7 @@ def test_polyhedron_attribute_names(client: FlaskClient, test_id: str) -> None: with client.application.app_context(): file = os.path.join(data_dir, "test.vtu") data = Data.create( + geode_id=DUMMY_GEODE_ID, geode_object=GeodePolyhedralSolid3D.geode_object_type(), viewer_object=GeodePolyhedralSolid3D.viewer_type(), viewer_elements_type=GeodePolyhedralSolid3D.viewer_elements_type(), @@ -480,6 +486,7 @@ def test_edge_attribute_names(client: FlaskClient, test_id: str) -> None: with client.application.app_context(): file = os.path.join(data_dir, "test.og_edc3d") data = Data.create( + geode_id=DUMMY_GEODE_ID, geode_object=GeodeEdgedCurve3D.geode_object_type(), viewer_object=GeodeEdgedCurve3D.viewer_type(), viewer_elements_type=GeodeEdgedCurve3D.viewer_elements_type(), @@ -517,8 +524,9 @@ def test_edge_attribute_names(client: FlaskClient, test_id: str) -> None: def test_database_uri_path(client: FlaskClient) -> None: app = client.application with app.app_context(): - base_dir = os.path.abspath(os.path.dirname(__file__)) - expected_db_path = os.path.join(base_dir, "data", "project.db") + expected_db_path = os.path.abspath( + os.path.join(app.config["DATA_FOLDER_PATH"], "project.db") + ) expected_uri = f"sqlite:///{expected_db_path}" assert app.config["SQLALCHEMY_DATABASE_URI"] == expected_uri @@ -615,8 +623,7 @@ def test_export_project_route(client: FlaskClient, tmp_path: Path) -> None: project_folder = client.application.config["DATA_FOLDER_PATH"] os.makedirs(project_folder, exist_ok=True) database_root_path = os.path.join(project_folder, "project.db") - with open(database_root_path, "wb") as f: - f.write(b"test_project_db") + assert os.path.isfile(database_root_path) with get_session() as session: session.query(Data).delete() @@ -624,6 +631,7 @@ def test_export_project_route(client: FlaskClient, tmp_path: Path) -> None: data1 = Data( id="test_data_1", + geode_id=DUMMY_GEODE_ID, geode_object="BRep", viewer_object="BRep", viewer_elements_type="default", @@ -631,6 +639,7 @@ def test_export_project_route(client: FlaskClient, tmp_path: Path) -> None: ) data2 = Data( id="test_data_2", + geode_id=DUMMY_GEODE_ID, geode_object="Section", viewer_object="Section", viewer_elements_type="default", @@ -692,7 +701,7 @@ def test_import_project_route(client: FlaskClient, tmp_path: Path) -> None: temp_db = tmp_path / "temp_project.db" conn = sqlite3.connect(str(temp_db)) conn.execute( - "CREATE TABLE datas (id TEXT PRIMARY KEY, geode_object TEXT, viewer_object TEXT, viewer_elements_type TEXT, native_file TEXT, " + "CREATE TABLE datas (id TEXT PRIMARY KEY, geode_id TEXT, geode_object TEXT, viewer_object TEXT, viewer_elements_type TEXT, native_file TEXT, " "viewable_file TEXT, light_viewable_file TEXT)" ) conn.commit() @@ -877,3 +886,17 @@ def test_extract_valid_attribute_values_with_non_transferable_attribute() -> Non ) assert has_nan is False assert valid_values == [] + + +def test_data_id_length_is_strict(client: FlaskClient) -> None: + route = "/opengeodeweb_back/vertex_attribute_names" + for wrong_id in ["a" * 31, "a" * 33, DUMMY_GEODE_ID]: + response = client.post(route, json={"id": wrong_id}) + assert response.status_code == 400, wrong_id + + +def test_component_id_length_is_strict(client: FlaskClient) -> None: + route = "/opengeodeweb_back/model_component_vertex_attribute_names" + model_id, _ = _load_brep_components(client) + response = client.post(route, json={"id": model_id, "component_ids": ["a" * 32]}) + assert response.status_code == 400 diff --git a/tests/test_utils_functions.py b/tests/test_utils_functions.py index 7c959ae4..17babace 100644 --- a/tests/test_utils_functions.py +++ b/tests/test_utils_functions.py @@ -14,8 +14,11 @@ # Local application imports from opengeodeweb_microservice.database.data import Data -from opengeodeweb_back import utils_functions +from opengeodeweb_back import geode_functions, utils_functions from opengeodeweb_back.geode_objects.geode_brep import GeodeBRep +from opengeodeweb_back.geode_objects.geode_polygonal_surface3d import ( + GeodePolygonalSurface3D, +) base_dir = os.path.abspath(os.path.dirname(__file__)) data_dir = os.path.join(base_dir, "data") @@ -99,7 +102,9 @@ def test_create_data_folder_from_id(client: FlaskClient) -> None: def test_save_all_viewables_and_return_info(client: FlaskClient) -> None: app = client.application with app.app_context(): - expected_db_path = os.path.join(data_dir, "project.db") + expected_db_path = os.path.abspath( + os.path.join(app.config["DATA_FOLDER_PATH"], "project.db") + ) expected_uri = f"sqlite:///{expected_db_path}" assert app.config["SQLALCHEMY_DATABASE_URI"] == expected_uri @@ -108,6 +113,7 @@ def test_save_all_viewables_and_return_info(client: FlaskClient) -> None: geode_object = GeodeBRep.load(os.path.join(data_dir, "test.og_brep")) data_entry = Data.create( + geode_id=geode_object.identifier.id().string(), geode_object=geode_object.geode_object_type(), viewer_object=geode_object.viewer_type(), viewer_elements_type=geode_object.viewer_elements_type(), @@ -128,6 +134,7 @@ def test_save_all_viewables_and_return_info(client: FlaskClient) -> None: assert isinstance(result["id"], str) assert len(result["id"]) == 32 assert re.match(r"[0-9a-f]{32}", result["id"]) + assert result["geode_id"] == geode_object.identifier.id().string() assert isinstance(result["viewer_type"], str) assert isinstance(result["binary_light_viewable"], str) assert result["geode_object_type"] == geode_object.geode_object_type() @@ -147,6 +154,7 @@ def test_save_all_viewables_commits_to_db(client: FlaskClient) -> None: with app.app_context(): geode_object = GeodeBRep.load(os.path.join(data_dir, "test.og_brep")) data_entry = Data.create( + geode_id=geode_object.identifier.id().string(), geode_object=geode_object.geode_object_type(), viewer_object=geode_object.viewer_type(), viewer_elements_type=geode_object.viewer_elements_type(), @@ -232,6 +240,64 @@ def test_generate_files_from_file_with_multi_dots( assert result["name"] == "cube.test" +def test_generate_files_from_file_returns_geode_id(client: FlaskClient) -> None: + app = client.application + with app.app_context(): + result = utils_functions.generate_files_from_file( + GeodeBRep.geode_object_type(), "test.og_brep" + ) + expected_geode_id = ( + GeodeBRep.load(os.path.join(data_dir, "test.og_brep")) + .identifier.id() + .string() + ) + assert result["geode_id"] == expected_geode_id + assert len(result["geode_id"]) == 36 + assert len(result["id"]) == 32 + data = Data.get(result["id"]) + assert data is not None + assert data.geode_id == expected_geode_id + + +def test_generate_files_from_file_twice_shares_geode_id(client: FlaskClient) -> None: + app = client.application + with app.app_context(): + first = utils_functions.generate_files_from_file( + GeodeBRep.geode_object_type(), "test.og_brep" + ) + second = utils_functions.generate_files_from_file( + GeodeBRep.geode_object_type(), "test.og_brep" + ) + assert first["id"] != second["id"] + assert first["geode_id"] == second["geode_id"] + + +def test_generate_files_from_non_native_file_twice_shares_geode_id( + client: FlaskClient, +) -> None: + app = client.application + with app.app_context(): + first = utils_functions.generate_files_from_file( + GeodePolygonalSurface3D.geode_object_type(), "hat.vtp" + ) + second = utils_functions.generate_files_from_file( + GeodePolygonalSurface3D.geode_object_type(), "hat.vtp" + ) + assert first["id"] != second["id"] + assert first["geode_id"] == second["geode_id"] + native_path = geode_functions.data_file_path(first["id"], first["native_file"]) + reloaded = GeodePolygonalSurface3D.load(native_path) + assert reloaded.identifier.id().string() == first["geode_id"] + + +def test_generate_files_from_object_returns_geode_id(client: FlaskClient) -> None: + app = client.application + with app.app_context(): + geode_object = GeodeBRep.load(os.path.join(data_dir, "test.og_brep")) + result = utils_functions.generate_files_from_object(geode_object) + assert result["geode_id"] == geode_object.identifier.id().string() + + def test_send_file_multiple_returns_zip(client: FlaskClient, tmp_path: Path) -> None: app = client.application with app.app_context():