From c1cfc87146840b471482207acbd105c260606eb2 Mon Sep 17 00:00:00 2001 From: krishnaglodha Date: Mon, 13 Jan 2025 21:24:20 +0530 Subject: [PATCH 1/3] fixed methods, variables --- src/geoserverx/_async/gsx.py | 109 +++++++----------- src/geoserverx/_sync/gsx.py | 109 ++++++++---------- src/geoserverx/cli/cli.py | 4 +- src/geoserverx/utils/auth.py | 2 +- .../utils/services/async_datastore.py | 14 +-- src/geoserverx/utils/services/datastore.py | 14 +-- 6 files changed, 104 insertions(+), 148 deletions(-) diff --git a/src/geoserverx/_async/gsx.py b/src/geoserverx/_async/gsx.py index 7f3e1b9..e67f6f9 100644 --- a/src/geoserverx/_async/gsx.py +++ b/src/geoserverx/_async/gsx.py @@ -33,7 +33,7 @@ from ..utils.services.async_datastore import ( AddDataStoreProtocol, CreateFileStore, - GPKGfileStore, + GpkgFileStore, ShapefileStore, ) @@ -44,10 +44,11 @@ class AsyncGeoServerX: Async Geoserver client """ + DEFAULT_HEADERS = {"Content-Type": "application/json"} username: str = "admin" password: str = "geoserver" url: str = "http://127.0.0.1:8080/geoserver/rest/" - headers = {"Content-Type": "application/json"} + headers = DEFAULT_HEADERS def __post_init__(self): if not self.username and not self.password and not self.url: @@ -78,23 +79,23 @@ def from_auth( ) -> "AsyncGeoServerX": return AsyncGeoServerX(auth.username, auth.password, auth.url) - def response_recognise(self, r) -> GSResponse: - if r == 401: + def recognize_response(self, response) -> GSResponse: + if response == 401: resp = GSResponseEnum._401.value - elif r == 500: + elif response == 500: resp = GSResponseEnum._500.value - elif r == 503: + elif response == 503: resp = GSResponseEnum._503.value - elif r == 404: + elif response == 404: resp = GSResponseEnum._404.value - elif r == 403: + elif response == 403: resp = GSResponseEnum._403.value - elif r == 201: + elif response == 201: resp = GSResponseEnum._201.value - elif r == 200: - resp = GSResponseEnum._200.value - elif r == 409: + elif response == 409: resp = GSResponseEnum._409.value + elif response == 200: + resp = GSResponseEnum._200.value return GSResponse.model_validate(resp) # check if certain module/plugin exists in geoserver @@ -110,16 +111,14 @@ async def check_modules(self, name) -> Union[bool, GSResponse]: ] if name.lower() in modules: return True - else: - # Raise exception if the plugin is not found - raise GSModuleNotFound(f"'{name}' plugin not found") + raise GSModuleNotFound(f"'{name}' plugin not found") except httpx.HTTPStatusError as e: # Handle HTTP errors (e.g., 4xx, 5xx) - return self.response_recognise(e.response.status_code) + return self.recognize_response(e.response.status_code) except httpx.RequestError as e: # Handle other request errors (e.g., network problems) - return self.response_recognise(e.response.status_code) + return self.recognize_response(e.response.status_code) except GSModuleNotFound as e: # Handle Module not found exception return GSResponse(code=412, response=str(e)) @@ -129,16 +128,14 @@ async def get_all_workspaces(self) -> Union[WorkspacesModel, GSResponse]: response = await client.get("workspaces") if response.status_code == 200: return WorkspacesModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_workspace(self, workspace: str) -> Union[WorkspaceModel, GSResponse]: client = self.http_client response = await client.get(f"workspaces/{workspace}") if response.status_code == 200: return WorkspaceModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def delete_workspace( self, workspace: str, recurse: bool = False @@ -147,14 +144,14 @@ async def delete_workspace( response = await client.delete( f"workspaces/{workspace}", params={"recurse": recurse} ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def create_workspace( - self, name: str, default: bool = False, Isolated: bool = False + self, name: str, default: bool = False, isolated: bool = False ) -> GSResponse: client = self.http_client payload: NewWorkspace = NewWorkspace( - workspace=NewWorkspaceInfo(name=name, isolated=Isolated) + workspace=NewWorkspaceInfo(name=name, isolated=isolated) ) response = await client.post( "workspaces", @@ -162,7 +159,7 @@ async def create_workspace( headers=self.headers, params={"default": default}, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def update_workspace( self, name: str, update: UpdateWorkspaceInfo @@ -174,15 +171,14 @@ async def update_workspace( data=update_ws.model_dump_json(exclude_none=True), headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_vector_stores_in_workspaces(self, workspace: str) -> DataStoresModel: client = self.http_client response = await client.get(f"workspaces/{workspace}/datastores") if response.status_code == 200: return DataStoresModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_raster_stores_in_workspaces( self, workspace: str @@ -191,8 +187,7 @@ async def get_raster_stores_in_workspaces( response = await client.get(f"workspaces/{workspace}/coveragestores") if response.status_code == 200: return CoveragesStoresModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_vector_store(self, workspace: str, store: str) -> DataStoreModel: url = f"workspaces/{workspace}/datastores/{store}.json" @@ -200,8 +195,7 @@ async def get_vector_store(self, workspace: str, store: str) -> DataStoreModel: response = await client.get(url) if response.status_code == 200: return DataStoreModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_raster_store(self, workspace: str, store: str) -> CoveragesStoreModel: url = f"workspaces/{workspace}/coveragestores/{store}.json" @@ -209,24 +203,21 @@ async def get_raster_store(self, workspace: str, store: str) -> CoveragesStoreMo response = await client.get(url) if response.status_code == 200: return CoveragesStoreModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_all_styles(self) -> AllStylesModel: client = self.http_client response = await client.get("styles") if response.status_code == 200: return AllStylesModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_style(self, style: str) -> StyleModel: client = self.http_client response = await client.get(f"styles/{style}.json") if response.status_code == 200: return StyleModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def create_pg_store( self, @@ -257,7 +248,7 @@ async def create_pg_store( data=payload.model_dump_json(), headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def create_file_store(self, workspace: str, store: str, file, service_type): service: AddDataStoreProtocol = CreateFileStore() @@ -270,7 +261,7 @@ async def create_file_store(self, workspace: str, store: str, file, service_type file=file, ) elif service_type == "gpkg": - service = GPKGfileStore( + service = GpkgFileStore( client=self.http_client, service=service, logger=std_out_logger("GeoPackage"), @@ -278,23 +269,8 @@ async def create_file_store(self, workspace: str, store: str, file, service_type ) else: raise ValueError(f"Service type {service_type} not supported") - response = await service.addFile(self.http_client, workspace, store) - return self.response_recognise(response) - - if service_type == "shapefile": - service = ShapefileStore( - client=self.http_client, - service=service, - logger=std_out_logger("Shapefile"), - file=file, - ) - elif service_type == "gpkg": - service = GPKGfileStore( - service=service, logger=std_out_logger("GeoPackage"), file=file - ) - else: - raise ValueError(f"Service type {service_type} not supported") - await service.addFile(self.http_client, workspace, store) + response = await service.add_file(self.http_client, workspace, store) + return self.recognize_response(response) async def get_all_layers( self, workspace: Optional[str] = None @@ -306,8 +282,7 @@ async def get_all_layers( response = await client.get("layers") if response.status_code == 200: return LayersModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_layer( self, workspace: str, layer: str @@ -316,13 +291,12 @@ async def get_layer( response = await client.get(f"layers/{workspace}:{layer}") if response.status_code == 200: return LayerModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def delete_layer(self, workspace: str, layer: str) -> GSResponse: client = self.http_client response = await client.delete(f"layers/{workspace}:{layer}") - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_all_layer_groups( self, workspace: Optional[str] = None @@ -334,8 +308,7 @@ async def get_all_layer_groups( response = await client.get("layergroups") if response.status_code == 200: return LayerGroupsModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_all_geofence_rules(self) -> Union[RulesResponse, GSResponse]: client = self.http_client @@ -349,8 +322,7 @@ async def get_all_geofence_rules(self) -> Union[RulesResponse, GSResponse]: ) if response.status_code == 200: return RulesResponse.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def get_geofence_rule(self, id: int) -> Union[Rule, GSResponse]: client = self.http_client @@ -364,8 +336,7 @@ async def get_geofence_rule(self, id: int) -> Union[Rule, GSResponse]: ) if response.status_code == 200: return Rule.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) async def create_geofence(self, rule: Rule) -> GSResponse: PostingRule = NewRule(Rule=rule) @@ -380,4 +351,4 @@ async def create_geofence(self, rule: Rule) -> GSResponse: content=PostingRule.model_dump_json(), headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) diff --git a/src/geoserverx/_sync/gsx.py b/src/geoserverx/_sync/gsx.py index d6c241f..2d7a1a0 100644 --- a/src/geoserverx/_sync/gsx.py +++ b/src/geoserverx/_sync/gsx.py @@ -36,7 +36,7 @@ from ..utils.services.datastore import ( AddDataStoreProtocol, CreateFileStore, - GPKGfileStore, + GpkgFileStore, ShapefileStore, ) @@ -47,10 +47,11 @@ class SyncGeoServerX: Sync Geoserver client """ + DEFAULT_HEADERS = {"Content-Type": "application/json"} username: str = "admin" password: str = "geoserver" url: str = "http://127.0.0.1:8080/geoserver/rest/" - headers = {"Content-Type": "application/json"} + headers = DEFAULT_HEADERS def __post_init__(self): if not self.username and not self.password and not self.url: @@ -69,7 +70,7 @@ def __post_init__(self): def __enter__(self) -> "SyncGeoServerX": return self - def __exit__(self, exc_t, exc_v, exc_tb) -> None: + def __exit__(self, exc_type, exc_val, exc_tb) -> None: # Changed abbreviated names self.close() def close(self) -> None: @@ -81,22 +82,22 @@ def from_auth( ) -> "SyncGeoServerX": return SyncGeoServerX(auth.username, auth.password, auth.url) - def response_recognise(self, r) -> GSResponse: - if r == 401: + def recognize_response(self, response) -> GSResponse: + if response == 401: resp = GSResponseEnum._401.value - elif r == 500: + elif response == 500: resp = GSResponseEnum._500.value - elif r == 503: + elif response == 503: resp = GSResponseEnum._503.value - elif r == 404: + elif response == 404: resp = GSResponseEnum._404.value - elif r == 403: + elif response == 403: resp = GSResponseEnum._403.value - elif r == 201: + elif response == 201: resp = GSResponseEnum._201.value - elif r == 409: + elif response == 409: resp = GSResponseEnum._409.value - elif r == 200: + elif response == 200: resp = GSResponseEnum._200.value return GSResponse.model_validate(resp) @@ -125,16 +126,14 @@ def check_modules(self, name) -> Union[bool, GSResponse]: ] if name.lower() in modules: return True - else: - # Raise exception if the plugin is not found - raise GSModuleNotFound(f"'{name}' plugin not found") + raise GSModuleNotFound(f"'{name}' plugin not found") except httpx.HTTPStatusError as e: # Handle HTTP errors (e.g., 4xx, 5xx) - return self.response_recognise(e.response.status_code) + return self.recognize_response(e.response.status_code) except httpx.RequestError as e: # Handle other request errors (e.g., network problems) - return self.response_recognise(e.response.status_code) + return self.recognize_response(e.response.status_code) except GSModuleNotFound as e: # Handle Module not found exception return GSResponse(code=412, response=str(e)) @@ -145,8 +144,7 @@ def get_all_workspaces(self) -> Union[WorkspacesModel, GSResponse]: response = client.get("workspaces") if response.status_code == 200: return WorkspacesModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_workspace(self, workspace: str) -> Union[WorkspaceModel, GSResponse]: @@ -154,21 +152,20 @@ def get_workspace(self, workspace: str) -> Union[WorkspaceModel, GSResponse]: response = client.get(f"workspaces/{workspace}") if response.status_code == 200: return WorkspaceModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def delete_workspace(self, workspace: str, recurse: bool = False) -> GSResponse: client = self.http_client response = client.delete(f"workspaces/{workspace}", params={"recurse": recurse}) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def create_workspace( - self, name: str, default: bool = False, Isolated: bool = False + self, name: str, default: bool = False, isolated: bool = False ) -> GSResponse: payload: NewWorkspace = NewWorkspace( - workspace=NewWorkspaceInfo(name=name, isolated=Isolated) + workspace=NewWorkspaceInfo(name=name, isolated=isolated) ) client = self.http_client response = client.post( @@ -177,7 +174,7 @@ def create_workspace( params={"default": default}, headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def update_workspace(self, name: str, update: UpdateWorkspaceInfo) -> GSResponse: @@ -188,7 +185,7 @@ def update_workspace(self, name: str, update: UpdateWorkspaceInfo) -> GSResponse data=update_ws.model_dump_json(exclude_none=True), headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_vector_stores_in_workspaces(self, workspace: str) -> DataStoresModel: @@ -196,8 +193,7 @@ def get_vector_stores_in_workspaces(self, workspace: str) -> DataStoresModel: response = client.get(f"workspaces/{workspace}/datastores") if response.status_code == 200: return DataStoresModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_raster_stores_in_workspaces(self, workspace: str) -> CoveragesStoresModel: @@ -205,8 +201,7 @@ def get_raster_stores_in_workspaces(self, workspace: str) -> CoveragesStoresMode response = client.get(f"workspaces/{workspace}/coveragestores") if response.status_code == 200: return CoveragesStoresModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_vector_store(self, workspace: str, store: str) -> DataStoreModel: @@ -215,8 +210,7 @@ def get_vector_store(self, workspace: str, store: str) -> DataStoreModel: response = client.get(url) if response.status_code == 200: return DataStoreModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def create_vector_store(self, workspace: str, store: DataStoresModel) -> GSResponse: @@ -226,7 +220,7 @@ def create_vector_store(self, workspace: str, store: DataStoresModel) -> GSRespo content=store.model_dump_json(), headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_raster_store(self, workspace: str, store: str) -> CoveragesStoreModel: @@ -235,8 +229,7 @@ def get_raster_store(self, workspace: str, store: str) -> CoveragesStoreModel: response = client.get(url) if response.status_code == 200: return CoveragesStoreModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def create_raster_store( @@ -248,7 +241,7 @@ def create_raster_store( content=store.model_dump_json(), headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def delete_store( @@ -263,7 +256,7 @@ def delete_store( response = client.delete( f"/workspaces/{workspace}/datastores/{store}", headers=self.headers ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_all_styles(self) -> AllStylesModel: @@ -271,8 +264,7 @@ def get_all_styles(self) -> AllStylesModel: response = client.get("styles") if response.status_code == 200: return AllStylesModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_style(self, style: str) -> StyleModel: @@ -280,8 +272,7 @@ def get_style(self, style: str) -> StyleModel: response = client.get(f"styles/{style}.json") if response.status_code == 200: return StyleModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def create_file_store( @@ -294,13 +285,13 @@ def create_file_store( service=service, logger=std_out_logger("Shapefile"), file=file ) elif service_type == "gpkg": - service = GPKGfileStore( + service = GpkgFileStore( service=service, logger=std_out_logger("GeoPackage"), file=file ) else: raise ValueError(f"Service type {service_type} not supported") - response = service.addFile(self.http_client, workspace, store) - return self.response_recognise(response) + response = service.add_file(self.http_client, workspace, store) + return self.recognize_response(response) @exception_handler def create_pg_store( @@ -332,7 +323,7 @@ def create_pg_store( data=payload.model_dump_json(), headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_all_layers( @@ -345,8 +336,7 @@ def get_all_layers( response = client.get("layers") if response.status_code == 200: return LayersModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_vector_layer( @@ -362,8 +352,7 @@ def get_vector_layer( except ValidationError as validation_error: print("Pydantic Validation Error:") print(validation_error) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_raster_layer( @@ -375,8 +364,7 @@ def get_raster_layer( ) if response.status_code == 200: return CoverageModel.parse_obj(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_layer( @@ -402,7 +390,7 @@ def get_layer( else: return LayerModel.parse_obj(response.json()) else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def create_vector_layer( @@ -414,7 +402,7 @@ def create_vector_layer( data=layer.model_dump(by_alias=True, exclude_none=True), headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def create_raster_layer(self, workspace: str, layer: CoverageModel) -> GSResponse: @@ -424,13 +412,13 @@ def create_raster_layer(self, workspace: str, layer: CoverageModel) -> GSRespons data=layer.model_dump_json(), headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def delete_layer(self, workspace: str, layer: str) -> GSResponse: client = self.http_client response = client.delete(f"layers/{workspace}:{layer}") - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_all_layer_groups( @@ -443,8 +431,7 @@ def get_all_layer_groups( response = client.get("layergroups") if response.status_code == 200: return LayerGroupsModel.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_all_geofence_rules(self) -> Union[RulesResponse, GSResponse]: @@ -458,8 +445,7 @@ def get_all_geofence_rules(self) -> Union[RulesResponse, GSResponse]: response = client.get("geofence/rules/", headers={"Accept": "application/json"}) if response.status_code == 200: return RulesResponse.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def get_geofence_rule(self, id: int) -> Union[GetRule, GSResponse]: @@ -474,8 +460,7 @@ def get_geofence_rule(self, id: int) -> Union[GetRule, GSResponse]: ) if response.status_code == 200: return Rule.model_validate(response.json()) - else: - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) @exception_handler def create_geofence(self, rule: Rule) -> GSResponse: @@ -491,4 +476,4 @@ def create_geofence(self, rule: Rule) -> GSResponse: content=PostingRule.model_dump_json(), headers=self.headers, ) - return self.response_recognise(response.status_code) + return self.recognize_response(response.status_code) diff --git a/src/geoserverx/cli/cli.py b/src/geoserverx/cli/cli.py index 6d4a0d3..aa5606f 100644 --- a/src/geoserverx/cli/cli.py +++ b/src/geoserverx/cli/cli.py @@ -27,7 +27,7 @@ def callback(): # Enum for vector file type -class vectorFileEnum(str, Enum): +class VectorFileEnum(str, Enum): shapefile = "shapefile" gpkg = "gpkg" @@ -287,7 +287,7 @@ def create_file( ), workspace: str = typer.Option(..., help="Workspace name"), store: str = typer.Option(..., help="Store name"), - service_type: vectorFileEnum = typer.Option(..., help="Vector file type"), + service_type: VectorFileEnum = typer.Option(..., help="Vector file type"), file: Path = typer.Option(..., help="File path"), password: str = typer.Option("geoserver", help="Geoserver Password"), username: str = typer.Option("admin", help="Geoserver username"), diff --git a/src/geoserverx/utils/auth.py b/src/geoserverx/utils/auth.py index f9cd715..0275844 100644 --- a/src/geoserverx/utils/auth.py +++ b/src/geoserverx/utils/auth.py @@ -7,7 +7,7 @@ class GeoServerXAuth: username: str = "admin" password: str = "geoserver" - url = "http://127.0.0.1:8080/geoserver/rest/" + url: str = "http://127.0.0.1:8080/geoserver/rest/" def __post_init__(self): if not self.username and not self.password and not self.url: diff --git a/src/geoserverx/utils/services/async_datastore.py b/src/geoserverx/utils/services/async_datastore.py index 6205689..8d6acb9 100644 --- a/src/geoserverx/utils/services/async_datastore.py +++ b/src/geoserverx/utils/services/async_datastore.py @@ -8,7 +8,7 @@ class AddDataStoreProtocol(Protocol): Represents functionality of sending a file to the server. """ - async def addFile( + async def add_file( self, client, workspace, @@ -22,7 +22,7 @@ async def addFile( class CreateFileStore: - async def addFile( + async def add_file( self, client, workspace, @@ -61,7 +61,7 @@ def __init__( self.file = file self.client = client - async def addFile(self, client, workspace, store): + async def add_file(self, client, workspace, store): store_payload: str = json.dumps( { "dataStore": { @@ -73,7 +73,7 @@ async def addFile(self, client, workspace, store): } ) # self.logger.debug(f"Shapefile store payload: {store_payload}") - result = await self.inner.addFile( + result = await self.inner.add_file( self.client, workspace, store, @@ -85,7 +85,7 @@ async def addFile(self, client, workspace, store): return result -class GPKGfileStore: +class GpkgFileStore: def __init__( self, service: AddDataStoreProtocol, logger: Logger, file, client ) -> None: @@ -94,7 +94,7 @@ def __init__( self.file = file self.client = client - async def addFile(self, client, workspace, store): + async def add_file(self, client, workspace, store): store_payload: str = json.dumps( { "dataStore": { @@ -109,7 +109,7 @@ async def addFile(self, client, workspace, store): } ) # self.logger.debug(f"GeoPackage store payload: {store_payload}") - result = await self.inner.addFile( + result = await self.inner.add_file( self.client, workspace, store, diff --git a/src/geoserverx/utils/services/datastore.py b/src/geoserverx/utils/services/datastore.py index f36d4a8..ec552ce 100644 --- a/src/geoserverx/utils/services/datastore.py +++ b/src/geoserverx/utils/services/datastore.py @@ -8,7 +8,7 @@ class AddDataStoreProtocol(Protocol): Represents functionality of sending a file to the server. """ - def addFile( + def add_file( self, client, workspace, @@ -22,7 +22,7 @@ def addFile( class CreateFileStore: - def addFile( + def add_file( self, client, workspace, @@ -54,7 +54,7 @@ def __init__(self, service: AddDataStoreProtocol, logger: Logger, file) -> None: self.file = file self.result = None - def addFile(self, client, workspace, store): + def add_file(self, client, workspace, store): store_payload: str = json.dumps( { "dataStore": { @@ -67,7 +67,7 @@ def addFile(self, client, workspace, store): ) # self.logger.debug(f"Shapefile store payload: {store_payload}") layer_payload = self.file - response = self.inner.addFile( + response = self.inner.add_file( client, workspace, store, @@ -81,14 +81,14 @@ def addFile(self, client, workspace, store): return self.result -class GPKGfileStore: +class GpkgFileStore: def __init__(self, service: AddDataStoreProtocol, logger: Logger, file) -> None: self.inner = service self.logger = logger self.file = file self.result = None - def addFile(self, client, workspace, store): + def add_file(self, client, workspace, store): store_payload: str = json.dumps( { "dataStore": { @@ -104,7 +104,7 @@ def addFile(self, client, workspace, store): ) # self.logger.debug(f"GeoPackage store payload: {store_payload}") layer_payload = self.file - response = self.inner.addFile( + response = self.inner.add_file( client, workspace, store, From 8e1b6873dd61f711a5efaa5c1d76129704ac3a47 Mon Sep 17 00:00:00 2001 From: krishnaglodha Date: Tue, 14 Jan 2025 09:55:17 +0530 Subject: [PATCH 2/3] added docstrings in sync and async gsx --- src/geoserverx/_async/gsx.py | 244 +++++++++++++++++++++++- src/geoserverx/_sync/gsx.py | 357 ++++++++++++++++++++++++++++++++++- 2 files changed, 587 insertions(+), 14 deletions(-) diff --git a/src/geoserverx/_async/gsx.py b/src/geoserverx/_async/gsx.py index e67f6f9..0afca8e 100644 --- a/src/geoserverx/_async/gsx.py +++ b/src/geoserverx/_async/gsx.py @@ -41,14 +41,21 @@ @dataclass class AsyncGeoServerX: """ - Async Geoserver client + Class to interact with GeoServer for async operations such as uploading, + deleting, and checking resources. + Initialize the GeoServer async utility. + + Args: + base_url (str): Base URL of the GeoServer instance. + username (str): GeoServer username. + password (str): GeoServer password. """ - DEFAULT_HEADERS = {"Content-Type": "application/json"} + default_headers = {"Content-Type": "application/json"} username: str = "admin" password: str = "geoserver" url: str = "http://127.0.0.1:8080/geoserver/rest/" - headers = DEFAULT_HEADERS + headers: dict = default_headers def __post_init__(self): if not self.username and not self.password and not self.url: @@ -100,16 +107,25 @@ def recognize_response(self, response) -> GSResponse: # check if certain module/plugin exists in geoserver async def check_modules(self, name) -> Union[bool, GSResponse]: + """ + Check if a specific module or plugin is available in GeoServer. + + Args: + name (str): Name of the module to check. + + Returns: + Union[bool, GSResponse]: True if the module exists, otherwise a GSResponse object. + """ client = self.http_client try: response = await client.get("about/status.json") response.raise_for_status() # Raises an HTTPError for bad response (4xx and 5xx) # Extract and check the modules - modules = [ + installed_modules = [ item["name"].lower() for item in response.json()["statuss"]["status"] ] - if name.lower() in modules: + if name.lower() in installed_modules: return True raise GSModuleNotFound(f"'{name}' plugin not found") @@ -124,6 +140,13 @@ async def check_modules(self, name) -> Union[bool, GSResponse]: return GSResponse(code=412, response=str(e)) async def get_all_workspaces(self) -> Union[WorkspacesModel, GSResponse]: + """ + Retrieve a list of all workspaces from GeoServer. + + Returns: + Union[WorkspacesModel, GSResponse]: A WorkspacesModel containing the list of workspaces if successful, + or a GSResponse object containing error details if the request fails. + """ client = self.http_client response = await client.get("workspaces") if response.status_code == 200: @@ -131,6 +154,17 @@ async def get_all_workspaces(self) -> Union[WorkspacesModel, GSResponse]: return self.recognize_response(response.status_code) async def get_workspace(self, workspace: str) -> Union[WorkspaceModel, GSResponse]: + """ + Retrieve information about a specific GeoServer workspace. + + Args: + workspace (str): The name of the workspace to retrieve. + + Returns: + Union[WorkspaceModel, GSResponse]: Returns either: + - WorkspaceModel: If the workspace exists (status code 200) + - GSResponse: If there's an error or workspace doesn't exist + """ client = self.http_client response = await client.get(f"workspaces/{workspace}") if response.status_code == 200: @@ -140,6 +174,16 @@ async def get_workspace(self, workspace: str) -> Union[WorkspaceModel, GSRespons async def delete_workspace( self, workspace: str, recurse: bool = False ) -> GSResponse: + """ + Delete a GeoServer workspace. + + Args: + workspace (str): The name of the workspace to delete + recurse (bool, optional): If True, recursively deletes all resources + contained within the workspace. Defaults to False. + Returns: + GSResponse: Response object indicating success or failure. + """ client = self.http_client response = await client.delete( f"workspaces/{workspace}", params={"recurse": recurse} @@ -149,6 +193,17 @@ async def delete_workspace( async def create_workspace( self, name: str, default: bool = False, isolated: bool = False ) -> GSResponse: + """ + Create a new workspace in GeoServer. + + Args: + name (str): Name of the workspace to create + default (bool, optional): Set this workspace as the default workspace. Defaults to False. + isolated (bool, optional): Enable workspace isolation. When True, the workspace + will have its own service URLs. Defaults to False. + Returns: + GSResponse: Response object indicating success or failure. + """ client = self.http_client payload: NewWorkspace = NewWorkspace( workspace=NewWorkspaceInfo(name=name, isolated=isolated) @@ -164,6 +219,16 @@ async def create_workspace( async def update_workspace( self, name: str, update: UpdateWorkspaceInfo ) -> GSResponse: + """ + Update an existing workspace in GeoServer. + + Args: + name (str): Name of the workspace to update + update (UpdateWorkspaceInfo): Object containing the workspace properties to update + + Returns: + GSResponse: Response object indicating success or failure: + """ client = self.http_client update_ws = UpdateWorkspace(workspace=update) response = await client.put( @@ -174,6 +239,15 @@ async def update_workspace( return self.recognize_response(response.status_code) async def get_vector_stores_in_workspaces(self, workspace: str) -> DataStoresModel: + """ + Retrieve all vector data stores within a specified workspace. + + Args: + workspace (str): Name of the workspace to query for data stores + + Returns: + DataStoresModel: A model containing a list of data stores if successful + """ client = self.http_client response = await client.get(f"workspaces/{workspace}/datastores") if response.status_code == 200: @@ -183,6 +257,16 @@ async def get_vector_stores_in_workspaces(self, workspace: str) -> DataStoresMod async def get_raster_stores_in_workspaces( self, workspace: str ) -> CoveragesStoresModel: + """ + Retrieve all raster (coverage) stores within a specified workspace. + + Args: + workspace (str): Name of the workspace to query for coverage stores + + Returns: + CoveragesStoresModel: A model containing a list of coverage stores if successful + GSResponse: Error response if the request fails + """ client = self.http_client response = await client.get(f"workspaces/{workspace}/coveragestores") if response.status_code == 200: @@ -190,6 +274,17 @@ async def get_raster_stores_in_workspaces( return self.recognize_response(response.status_code) async def get_vector_store(self, workspace: str, store: str) -> DataStoreModel: + """ + Retrieve details of a specific vector data store from a workspace. + + Args: + workspace (str): Name of the workspace containing the store + store (str): Name of the vector data store to retrieve + + Returns: + DataStoreModel: Details of the requested vector store if successful + GSResponse: Error response if the request fails + """ url = f"workspaces/{workspace}/datastores/{store}.json" client = self.http_client response = await client.get(url) @@ -198,6 +293,17 @@ async def get_vector_store(self, workspace: str, store: str) -> DataStoreModel: return self.recognize_response(response.status_code) async def get_raster_store(self, workspace: str, store: str) -> CoveragesStoreModel: + """ + Retrieve details of a specific raster (coverage) store from a workspace. + + Args: + workspace (str): Name of the workspace containing the store + store (str): Name of the raster store to retrieve + + Returns: + CoveragesStoreModel: Details of the requested raster store if successful + GSResponse: Error response if the request fails + """ url = f"workspaces/{workspace}/coveragestores/{store}.json" client = self.http_client response = await client.get(url) @@ -206,6 +312,13 @@ async def get_raster_store(self, workspace: str, store: str) -> CoveragesStoreMo return self.recognize_response(response.status_code) async def get_all_styles(self) -> AllStylesModel: + """ + Retrieve all styles configured in GeoServer. + + Returns: + AllStylesModel: List of all available styles and their configurations + GSResponse: Response object indicating failure + """ client = self.http_client response = await client.get("styles") if response.status_code == 200: @@ -213,6 +326,16 @@ async def get_all_styles(self) -> AllStylesModel: return self.recognize_response(response.status_code) async def get_style(self, style: str) -> StyleModel: + """ + Retrieve a specific style configuration from GeoServer. + + Args: + style (str): Name of the style to retrieve + + Returns: + StyleModel: Configuration details of the requested style + GSResponse: Response object indicating failure + """ client = self.http_client response = await client.get(f"styles/{style}.json") if response.status_code == 200: @@ -229,6 +352,21 @@ async def create_pg_store( password: str, database: str, ) -> GSResponse: + """ + Create a new PostgreSQL/PostGIS data store in GeoServer. + + Args: + name (str): Name of the store to create + workspace (str): Name of the workspace where the store will be created + host (str): PostgreSQL server hostname + port (int): PostgreSQL server port + username (str): Database username + password (str): Database password + database (str): Name of the database + + Returns: + GSResponse: Response object indicating success or failure + """ payload = MainCreateDataStoreModel( dataStore=CreateDataStoreModel( name=name, @@ -251,6 +389,18 @@ async def create_pg_store( return self.recognize_response(response.status_code) async def create_file_store(self, workspace: str, store: str, file, service_type): + """ + Create a new file-based data store in GeoServer. + + Args: + workspace (str): Name of the workspace where the store will be created + store (str): Name of the store to create + file: File object to be uploaded + service_type (str): Type of file store to create ('shapefile' or 'gpkg') + + Returns: + GSResponse: Response object indicating success or failure + """ service: AddDataStoreProtocol = CreateFileStore() if service_type == "shapefile": @@ -275,6 +425,17 @@ async def create_file_store(self, workspace: str, store: str, file, service_type async def get_all_layers( self, workspace: Optional[str] = None ) -> Union[LayersModel, GSResponse]: + """ + Retrieve all layers configured in GeoServer. + + Args: + workspace (Optional[str]): Name of the workspace to filter layers. + If None, returns layers from all workspaces. + + Returns: + LayersModel: List of all available layers and their configurations + GSResponse: Response object indicating failure + """ client = self.http_client if workspace: response = await client.get(f"/workspaces/{workspace}/layers") @@ -287,6 +448,22 @@ async def get_all_layers( async def get_layer( self, workspace: str, layer: str ) -> Union[LayerModel, GSResponse]: + """ + Retrieve layer information from GeoServer, with optional detailed information. + + Args: + workspace (str): Name of the workspace containing the layer + layer (str): Name of the layer to retrieve + detail (bool, optional): If True, returns detailed layer information including + specific vector or raster properties. Defaults to False. + + Returns: + Union[LayerModel, FeatureTypesModel, GSResponse]: One of the following: + - LayerModel: Basic layer information if detail=False + - FeatureTypesModel: Detailed vector layer information if detail=True and layer is vector + - CoverageModel: Detailed raster layer information if detail=True and layer is raster + - GSResponse: Error response if the request fails + """ client = self.http_client response = await client.get(f"layers/{workspace}:{layer}") if response.status_code == 200: @@ -294,6 +471,16 @@ async def get_layer( return self.recognize_response(response.status_code) async def delete_layer(self, workspace: str, layer: str) -> GSResponse: + """ + Delete a layer from a specified workspace in GeoServer. + + Args: + workspace (str): Name of the workspace containing the layer + layer (str): Name of the layer to delete + + Returns: + GSResponse: Response object indicating success or failure + """ client = self.http_client response = await client.delete(f"layers/{workspace}:{layer}") return self.recognize_response(response.status_code) @@ -301,6 +488,18 @@ async def delete_layer(self, workspace: str, layer: str) -> GSResponse: async def get_all_layer_groups( self, workspace: Optional[str] = None ) -> Union[LayerGroupsModel, GSResponse]: + """ + Retrieve all layer groups from GeoServer, optionally filtered by workspace. + + Args: + workspace (Optional[str]): Name of the workspace to filter layer groups. + If None, retrieves layer groups from all workspaces. + + Returns: + Union[LayerGroupsModel, GSResponse]: Either a list of layer groups or an error response + - LayerGroupsModel: Contains a list of all layer groups if successful + - GSResponse: Error response if the request fails + """ client = self.http_client if workspace: response = await client.get(f"workspaces/{workspace}/layergroups") @@ -311,6 +510,14 @@ async def get_all_layer_groups( return self.recognize_response(response.status_code) async def get_all_geofence_rules(self) -> Union[RulesResponse, GSResponse]: + """ + Retrieve all GeoFence rules configured in GeoServer. + + Returns: + Union[RulesResponse, GSResponse]: Either a list of all rules or an error response + - RulesResponse: Contains a list of all GeoFence rules if successful + - GSResponse: Error response if the request fails + """ client = self.http_client # Check if the geofence plugin exists module_check = await self.check_modules("geofence") @@ -325,6 +532,17 @@ async def get_all_geofence_rules(self) -> Union[RulesResponse, GSResponse]: return self.recognize_response(response.status_code) async def get_geofence_rule(self, id: int) -> Union[Rule, GSResponse]: + """ + Retrieve a specific GeoFence rule by its ID from GeoServer. + + Args: + id (int): The unique identifier of the GeoFence rule to retrieve + + Returns: + Union[GetRule, GSResponse]: Either the requested rule or an error response + - GetRule: The rule details if found + - GSResponse: Error response if the request fails + """ client = self.http_client # Check if the geofence plugin exists module_check = await self.check_modules("geofence") @@ -339,6 +557,22 @@ async def get_geofence_rule(self, id: int) -> Union[Rule, GSResponse]: return self.recognize_response(response.status_code) async def create_geofence(self, rule: Rule) -> GSResponse: + """ + Create a new GeoFence rule in GeoServer. + + Args: + rule (Rule): Rule object containing the GeoFence rule configuration details. + The Rule object should specify access control parameters such as: + - workspace + - layer + - access rights + - user/role information + - service type + - request type + + Returns: + GSResponse: Response object indicating success or failure + """ PostingRule = NewRule(Rule=rule) # Check if the geofence plugin exists module_check = await self.check_modules("geofence") diff --git a/src/geoserverx/_sync/gsx.py b/src/geoserverx/_sync/gsx.py index 2d7a1a0..9b67296 100644 --- a/src/geoserverx/_sync/gsx.py +++ b/src/geoserverx/_sync/gsx.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from enum import Enum from typing import Optional, Union import httpx @@ -41,17 +42,31 @@ ) +class StoreType(Enum): + """Enum for GeoServer store types""" + + raster = "raster" + vector = "vector" + + @dataclass class SyncGeoServerX: """ - Sync Geoserver client + Class to interact with GeoServer for syncing operations such as uploading, + deleting, and checking resources. + Initialize the GeoServer sync utility. + + Args: + base_url (str): Base URL of the GeoServer instance. + username (str): GeoServer username. + password (str): GeoServer password. """ - DEFAULT_HEADERS = {"Content-Type": "application/json"} + default_headers = {"Content-Type": "application/json"} username: str = "admin" password: str = "geoserver" url: str = "http://127.0.0.1:8080/geoserver/rest/" - headers = DEFAULT_HEADERS + headers: dict = default_headers def __post_init__(self): if not self.username and not self.password and not self.url: @@ -70,7 +85,7 @@ def __post_init__(self): def __enter__(self) -> "SyncGeoServerX": return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: # Changed abbreviated names + def __exit__(self, exc_type, exc_val, exc_tb) -> None: self.close() def close(self) -> None: @@ -115,16 +130,25 @@ def inner_function(*args, **kwargs): # check if certain module/plugin exists in geoserver @exception_handler def check_modules(self, name) -> Union[bool, GSResponse]: + """ + Check if a specific module or plugin is available in GeoServer. + + Args: + name (str): Name of the module to check. + + Returns: + Union[bool, GSResponse]: True if the module exists, otherwise a GSResponse object. + """ client = self.http_client try: response = client.get("about/status.json") response.raise_for_status() # Raises an HTTPError for bad response (4xx and 5xx) # Extract and check the modules - modules = [ + installed_modules = [ item["name"].lower() for item in response.json()["statuss"]["status"] ] - if name.lower() in modules: + if name.lower() in installed_modules: return True raise GSModuleNotFound(f"'{name}' plugin not found") @@ -140,6 +164,13 @@ def check_modules(self, name) -> Union[bool, GSResponse]: @exception_handler def get_all_workspaces(self) -> Union[WorkspacesModel, GSResponse]: + """ + Retrieve a list of all workspaces from GeoServer. + + Returns: + Union[WorkspacesModel, GSResponse]: A WorkspacesModel containing the list of workspaces if successful, + or a GSResponse object containing error details if the request fails. + """ client = self.http_client response = client.get("workspaces") if response.status_code == 200: @@ -148,6 +179,17 @@ def get_all_workspaces(self) -> Union[WorkspacesModel, GSResponse]: @exception_handler def get_workspace(self, workspace: str) -> Union[WorkspaceModel, GSResponse]: + """ + Retrieve information about a specific GeoServer workspace. + + Args: + workspace (str): The name of the workspace to retrieve. + + Returns: + Union[WorkspaceModel, GSResponse]: Returns either: + - WorkspaceModel: If the workspace exists (status code 200) + - GSResponse: If there's an error or workspace doesn't exist + """ client = self.http_client response = client.get(f"workspaces/{workspace}") if response.status_code == 200: @@ -156,6 +198,16 @@ def get_workspace(self, workspace: str) -> Union[WorkspaceModel, GSResponse]: @exception_handler def delete_workspace(self, workspace: str, recurse: bool = False) -> GSResponse: + """ + Delete a GeoServer workspace. + + Args: + workspace (str): The name of the workspace to delete + recurse (bool, optional): If True, recursively deletes all resources + contained within the workspace. Defaults to False. + Returns: + GSResponse: Response object indicating success or failure. + """ client = self.http_client response = client.delete(f"workspaces/{workspace}", params={"recurse": recurse}) return self.recognize_response(response.status_code) @@ -164,6 +216,17 @@ def delete_workspace(self, workspace: str, recurse: bool = False) -> GSResponse: def create_workspace( self, name: str, default: bool = False, isolated: bool = False ) -> GSResponse: + """ + Create a new workspace in GeoServer. + + Args: + name (str): Name of the workspace to create + default (bool, optional): Set this workspace as the default workspace. Defaults to False. + isolated (bool, optional): Enable workspace isolation. When True, the workspace + will have its own service URLs. Defaults to False. + Returns: + GSResponse: Response object indicating success or failure. + """ payload: NewWorkspace = NewWorkspace( workspace=NewWorkspaceInfo(name=name, isolated=isolated) ) @@ -178,6 +241,16 @@ def create_workspace( @exception_handler def update_workspace(self, name: str, update: UpdateWorkspaceInfo) -> GSResponse: + """ + Update an existing workspace in GeoServer. + + Args: + name (str): Name of the workspace to update + update (UpdateWorkspaceInfo): Object containing the workspace properties to update + + Returns: + GSResponse: Response object indicating success or failure: + """ client = self.http_client update_ws = UpdateWorkspace(workspace=update) response = client.put( @@ -189,6 +262,15 @@ def update_workspace(self, name: str, update: UpdateWorkspaceInfo) -> GSResponse @exception_handler def get_vector_stores_in_workspaces(self, workspace: str) -> DataStoresModel: + """ + Retrieve all vector data stores within a specified workspace. + + Args: + workspace (str): Name of the workspace to query for data stores + + Returns: + DataStoresModel: A model containing a list of data stores if successful + """ client = self.http_client response = client.get(f"workspaces/{workspace}/datastores") if response.status_code == 200: @@ -197,6 +279,16 @@ def get_vector_stores_in_workspaces(self, workspace: str) -> DataStoresModel: @exception_handler def get_raster_stores_in_workspaces(self, workspace: str) -> CoveragesStoresModel: + """ + Retrieve all raster (coverage) stores within a specified workspace. + + Args: + workspace (str): Name of the workspace to query for coverage stores + + Returns: + CoveragesStoresModel: A model containing a list of coverage stores if successful + GSResponse: Error response if the request fails + """ client = self.http_client response = client.get(f"workspaces/{workspace}/coveragestores") if response.status_code == 200: @@ -205,6 +297,17 @@ def get_raster_stores_in_workspaces(self, workspace: str) -> CoveragesStoresMode @exception_handler def get_vector_store(self, workspace: str, store: str) -> DataStoreModel: + """ + Retrieve details of a specific vector data store from a workspace. + + Args: + workspace (str): Name of the workspace containing the store + store (str): Name of the vector data store to retrieve + + Returns: + DataStoreModel: Details of the requested vector store if successful + GSResponse: Error response if the request fails + """ url = f"workspaces/{workspace}/datastores/{store}.json" client = self.http_client response = client.get(url) @@ -214,6 +317,17 @@ def get_vector_store(self, workspace: str, store: str) -> DataStoreModel: @exception_handler def create_vector_store(self, workspace: str, store: DataStoresModel) -> GSResponse: + """ + Create a new vector data store in a specified workspace. + + Args: + workspace (str): Name of the workspace where the store will be created + store (DataStoresModel): Model containing the vector store configuration details + + Returns: + GSResponse: Response object indicating success or failure + """ + client = self.http_client response = client.post( f"workspaces/{workspace}/datastores", @@ -224,6 +338,18 @@ def create_vector_store(self, workspace: str, store: DataStoresModel) -> GSRespo @exception_handler def get_raster_store(self, workspace: str, store: str) -> CoveragesStoreModel: + """ + Retrieve details of a specific raster (coverage) store from a workspace. + + Args: + workspace (str): Name of the workspace containing the store + store (str): Name of the raster store to retrieve + + Returns: + CoveragesStoreModel: Details of the requested raster store if successful + GSResponse: Error response if the request fails + """ + url = f"workspaces/{workspace}/coveragestores/{store}.json" client = self.http_client response = client.get(url) @@ -235,6 +361,17 @@ def get_raster_store(self, workspace: str, store: str) -> CoveragesStoreModel: def create_raster_store( self, workspace: str, store: CoveragesStoreModel ) -> GSResponse: + """ + Create a new raster (coverage) store in a specified workspace. + + Args: + workspace (str): Name of the workspace where the store will be created + store (CoveragesStoreModel): Model containing the raster store configuration details + + Returns: + GSResponse: Response object indicating success or failure + """ + client = self.http_client response = client.post( f"workspaces/{workspace}/coveragestores", @@ -244,9 +381,23 @@ def create_raster_store( return self.recognize_response(response.status_code) @exception_handler - def delete_store( - self, workspace: str, store: str, type: str - ) -> GSResponse: # TODO : add enum for type + def delete_store(self, workspace: str, store: str, type: StoreType) -> GSResponse: + """ + Create a new data store (vector) or coverage store (raster) in a specified workspace. + + Args: + workspace (str): Name of the workspace where the store will be created + store (Union[CoverageStoreModel, DataStoreModel]): Store configuration model + - CoverageStoreModel: For raster data stores + - DataStoreModel: For vector data stores + type (StoreType): Type of store to create, either "raster" or "vector" + - "raster": Creates a coverage store for raster data + - "vector": Creates a data store for vector data + + Returns: + GSResponse: Response object indicating success or failure + """ + client = self.http_client if type == "raster": response = client.delete( @@ -260,6 +411,14 @@ def delete_store( @exception_handler def get_all_styles(self) -> AllStylesModel: + """ + Retrieve all styles configured in GeoServer. + + Returns: + AllStylesModel: List of all available styles and their configurations + GSResponse: Response object indicating failure + """ + client = self.http_client response = client.get("styles") if response.status_code == 200: @@ -268,6 +427,17 @@ def get_all_styles(self) -> AllStylesModel: @exception_handler def get_style(self, style: str) -> StyleModel: + """ + Retrieve a specific style configuration from GeoServer. + + Args: + style (str): Name of the style to retrieve + + Returns: + StyleModel: Configuration details of the requested style + GSResponse: Response object indicating failure + """ + client = self.http_client response = client.get(f"styles/{style}.json") if response.status_code == 200: @@ -278,6 +448,19 @@ def get_style(self, style: str) -> StyleModel: def create_file_store( self, workspace: str, store: str, file, service_type ) -> GSResponse: + """ + Create a new file-based data store in GeoServer. + + Args: + workspace (str): Name of the workspace where the store will be created + store (str): Name of the store to create + file: File object to be uploaded + service_type (str): Type of file store to create ('shapefile' or 'gpkg') + + Returns: + GSResponse: Response object indicating success or failure + """ + service: AddDataStoreProtocol = CreateFileStore() if service_type == "shapefile": @@ -304,6 +487,22 @@ def create_pg_store( password: str, database: str, ) -> GSResponse: + """ + Create a new PostgreSQL/PostGIS data store in GeoServer. + + Args: + name (str): Name of the store to create + workspace (str): Name of the workspace where the store will be created + host (str): PostgreSQL server hostname + port (int): PostgreSQL server port + username (str): Database username + password (str): Database password + database (str): Name of the database + + Returns: + GSResponse: Response object indicating success or failure + """ + payload = MainCreateDataStoreModel( dataStore=CreateDataStoreModel( name=name, @@ -329,6 +528,18 @@ def create_pg_store( def get_all_layers( self, workspace: Optional[str] = None ) -> Union[LayersModel, GSResponse]: + """ + Retrieve all layers configured in GeoServer. + + Args: + workspace (Optional[str]): Name of the workspace to filter layers. + If None, returns layers from all workspaces. + + Returns: + LayersModel: List of all available layers and their configurations + GSResponse: Response object indicating failure + """ + client = self.http_client if workspace: response = client.get(f"/workspaces/{workspace}/layers") @@ -342,6 +553,19 @@ def get_all_layers( def get_vector_layer( self, workspace: str, store: str, layer: str ) -> Union[FeatureTypesModel, GSResponse]: + """ + Retrieve configuration details of a vector layer from GeoServer. + + Args: + workspace (str): Name of the workspace containing the layer + store (str): Name of the data store containing the layer + layer (str): Name of the vector layer to retrieve + + Returns: + FeatureTypesModel: Configuration details of the vector layer + GSResponse: Response object indicating failure + """ + client = self.http_client response = client.get( f"/workspaces/{workspace}/datastores/{store}/featuretypes/{layer}.json" @@ -358,6 +582,19 @@ def get_vector_layer( def get_raster_layer( self, workspace: str, store: str, layer: str ) -> Union[CoverageModel, GSResponse]: + """ + Retrieve configuration details of a raster layer from GeoServer. + + Args: + workspace (str): Name of the workspace containing the layer + store (str): Name of the coverage store containing the layer + layer (str): Name of the raster layer to retrieve + + Returns: + CoverageModel: Configuration details of the raster layer + GSResponse: Response object indicating failure + """ + client = self.http_client response = client.get( f"/workspaces/{workspace}/coveragestores/{store}/coverages/{layer}.json" @@ -370,6 +607,23 @@ def get_raster_layer( def get_layer( self, workspace: str, layer: str, detail: bool = False ) -> Union[LayerModel, FeatureTypesModel, GSResponse]: + """ + Retrieve layer information from GeoServer, with optional detailed information. + + Args: + workspace (str): Name of the workspace containing the layer + layer (str): Name of the layer to retrieve + detail (bool, optional): If True, returns detailed layer information including + specific vector or raster properties. Defaults to False. + + Returns: + Union[LayerModel, FeatureTypesModel, GSResponse]: One of the following: + - LayerModel: Basic layer information if detail=False + - FeatureTypesModel: Detailed vector layer information if detail=True and layer is vector + - CoverageModel: Detailed raster layer information if detail=True and layer is raster + - GSResponse: Error response if the request fails + """ + client = self.http_client response = client.get(f"layers/{workspace}:{layer}") if response.status_code == 200: @@ -396,6 +650,17 @@ def get_layer( def create_vector_layer( self, workspace: str, layer: FeatureTypesModel ) -> GSResponse: + """ + Create a new vector layer (feature type) in a specified workspace. + + Args: + workspace (str): Name of the workspace where the layer will be created + layer (FeatureTypesModel): Model containing the vector layer configuration details, + including metadata, attribute information, and other feature type parameters + + Returns: + GSResponse: Response object indicating success or failure + """ client = self.http_client response = client.post( f"/workspaces/{workspace}/featuretypes", @@ -406,6 +671,18 @@ def create_vector_layer( @exception_handler def create_raster_layer(self, workspace: str, layer: CoverageModel) -> GSResponse: + """ + Create a new raster (coverage) layer in a specified workspace. + + Args: + workspace (str): Name of the workspace where the layer will be created + layer (CoverageModel): Model containing the raster layer configuration details, + including metadata, native format, projection, and other coverage parameters + + Returns: + GSResponse: Response object indicating success or failure + """ + client = self.http_client response = client.post( f"/workspaces/{workspace}/coverages", @@ -416,6 +693,17 @@ def create_raster_layer(self, workspace: str, layer: CoverageModel) -> GSRespons @exception_handler def delete_layer(self, workspace: str, layer: str) -> GSResponse: + """ + Delete a layer from a specified workspace in GeoServer. + + Args: + workspace (str): Name of the workspace containing the layer + layer (str): Name of the layer to delete + + Returns: + GSResponse: Response object indicating success or failure + """ + client = self.http_client response = client.delete(f"layers/{workspace}:{layer}") return self.recognize_response(response.status_code) @@ -424,6 +712,19 @@ def delete_layer(self, workspace: str, layer: str) -> GSResponse: def get_all_layer_groups( self, workspace: Optional[str] = None ) -> Union[LayerGroupsModel, GSResponse]: + """ + Retrieve all layer groups from GeoServer, optionally filtered by workspace. + + Args: + workspace (Optional[str]): Name of the workspace to filter layer groups. + If None, retrieves layer groups from all workspaces. + + Returns: + Union[LayerGroupsModel, GSResponse]: Either a list of layer groups or an error response + - LayerGroupsModel: Contains a list of all layer groups if successful + - GSResponse: Error response if the request fails + """ + client = self.http_client if workspace: response = client.get(f"workspaces/{workspace}/layergroups") @@ -435,6 +736,15 @@ def get_all_layer_groups( @exception_handler def get_all_geofence_rules(self) -> Union[RulesResponse, GSResponse]: + """ + Retrieve all GeoFence rules configured in GeoServer. + + Returns: + Union[RulesResponse, GSResponse]: Either a list of all rules or an error response + - RulesResponse: Contains a list of all GeoFence rules if successful + - GSResponse: Error response if the request fails + """ + client = self.http_client # Check if the geofence plugin exists module_check = self.check_modules("geofence") @@ -449,6 +759,18 @@ def get_all_geofence_rules(self) -> Union[RulesResponse, GSResponse]: @exception_handler def get_geofence_rule(self, id: int) -> Union[GetRule, GSResponse]: + """ + Retrieve a specific GeoFence rule by its ID from GeoServer. + + Args: + id (int): The unique identifier of the GeoFence rule to retrieve + + Returns: + Union[GetRule, GSResponse]: Either the requested rule or an error response + - GetRule: The rule details if found + - GSResponse: Error response if the request fails + """ + client = self.http_client # Check if the geofence plugin exists module_check = self.check_modules("geofence") @@ -464,6 +786,23 @@ def get_geofence_rule(self, id: int) -> Union[GetRule, GSResponse]: @exception_handler def create_geofence(self, rule: Rule) -> GSResponse: + """ + Create a new GeoFence rule in GeoServer. + + Args: + rule (Rule): Rule object containing the GeoFence rule configuration details. + The Rule object should specify access control parameters such as: + - workspace + - layer + - access rights + - user/role information + - service type + - request type + + Returns: + GSResponse: Response object indicating success or failure + """ + PostingRule = NewRule(Rule=rule) # Check if the geofence plugin exists module_check = self.check_modules("geofence") From 231be0499cfe705f45f65797eba682a09e53fbae Mon Sep 17 00:00:00 2001 From: krishnaglodha Date: Tue, 14 Jan 2025 10:10:17 +0530 Subject: [PATCH 3/3] fixed dataclasses headers default --- src/geoserverx/_async/gsx.py | 11 +++++++---- src/geoserverx/_sync/gsx.py | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/geoserverx/_async/gsx.py b/src/geoserverx/_async/gsx.py index 0afca8e..9f43ee6 100644 --- a/src/geoserverx/_async/gsx.py +++ b/src/geoserverx/_async/gsx.py @@ -1,5 +1,5 @@ -from dataclasses import dataclass -from typing import Optional, Union +from dataclasses import dataclass, field +from typing import Dict, Optional, Union import httpx @@ -51,11 +51,14 @@ class AsyncGeoServerX: password (str): GeoServer password. """ - default_headers = {"Content-Type": "application/json"} username: str = "admin" password: str = "geoserver" url: str = "http://127.0.0.1:8080/geoserver/rest/" - headers: dict = default_headers + headers: Dict[str, str] = field( + default_factory=lambda: { + "Content-Type": "application/json", + } + ) def __post_init__(self): if not self.username and not self.password and not self.url: diff --git a/src/geoserverx/_sync/gsx.py b/src/geoserverx/_sync/gsx.py index 9b67296..15a71e0 100644 --- a/src/geoserverx/_sync/gsx.py +++ b/src/geoserverx/_sync/gsx.py @@ -1,6 +1,6 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import Enum -from typing import Optional, Union +from typing import Dict, Optional, Union import httpx from pydantic import ValidationError @@ -62,11 +62,14 @@ class SyncGeoServerX: password (str): GeoServer password. """ - default_headers = {"Content-Type": "application/json"} username: str = "admin" password: str = "geoserver" url: str = "http://127.0.0.1:8080/geoserver/rest/" - headers: dict = default_headers + headers: Dict[str, str] = field( + default_factory=lambda: { + "Content-Type": "application/json", + } + ) def __post_init__(self): if not self.username and not self.password and not self.url: