Skip to content

Commit

Permalink
Add layer data class
Browse files Browse the repository at this point in the history
  • Loading branch information
vuilleumierc committed Nov 7, 2024
1 parent 4cb998e commit 12f4c73
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 88 deletions.
7 changes: 5 additions & 2 deletions geoservercloud/geoservercloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
PostGisDataStore,
Workspace,
)
from geoservercloud.models.layer import Layer
from geoservercloud.services import OwsService, RestService
from geoservercloud.templates import Templates

Expand Down Expand Up @@ -403,9 +404,11 @@ def create_style_from_file(
return self.rest_service.create_style_from_file(style, file, workspace_name)

def set_default_layer_style(
self, layer: str, workspace_name: str, style: str
self, layer_name: str, workspace_name: str, style: str
) -> tuple[str, int]:
return self.rest_service.set_default_layer_style(layer, workspace_name, style)
"""Set the default style for a layer"""
layer = Layer(layer_name, default_style_name=style)
return self.rest_service.update_layer(layer, workspace_name)

def get_wms_layers(
self, workspace_name: str, accept_languages: str | None = None
Expand Down
21 changes: 15 additions & 6 deletions geoservercloud/models/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ def post_payload(self) -> dict[str, Any]:
def put_payload(self) -> dict[str, Any]:
raise NotImplementedError

@staticmethod
def add_items_to_dict(content: dict, items: dict[str, Any]) -> dict[Any, Any]:
for key, value in items.items():
content = EntityModel.add_item_to_dict(content, key, value)
return content

@staticmethod
def add_item_to_dict(content: dict, key: str, value: Any) -> dict[Any, Any]:
if value is not None:
content[key] = value
return content


class ListModel(BaseModel):
def aslist(self) -> list:
Expand All @@ -26,19 +38,16 @@ def aslist(self) -> list:

class ReferencedObjectModel(BaseModel):
def __init__(self, name: str, href: str | None = None):
self.name = name
self.href = href
self.name: str = name
self.href: str | None = href

@classmethod
def from_get_response_payload(cls, content: dict):
cls.name = content["name"]
cls.href = content["href"]

def asdict(self) -> dict[str, str]:
content = {"name": self.name}
if self.href:
content["href"] = self.href
return content
return EntityModel.add_item_to_dict({"name": self.name}, "href", self.href)


class KeyDollarListDict(dict):
Expand Down
16 changes: 7 additions & 9 deletions geoservercloud/models/datastore.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,13 @@ def asdict(self) -> dict[str, Any]:
"connectionParameters": {"entry": dict(self.connection_parameters)},
"workspace": self.workspace_name,
}
if self.description:
content["description"] = self.description
if self.enabled:
content["enabled"] = self.enabled
if self._default is not None:
content["_default"] = self._default
if self.disable_on_conn_failure is not None:
content["disableOnConnFailure"] = self.disable_on_conn_failure
return content
optional_items = {
"description": self.description,
"enabled": self.enabled,
"_default": self._default,
"disableOnConnFailure": self.disable_on_conn_failure,
}
return EntityModel.add_items_to_dict(content, optional_items)

def post_payload(self) -> dict[str, Any]:
content = self.asdict()
Expand Down
52 changes: 19 additions & 33 deletions geoservercloud/models/featuretype.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,10 @@ def asdict(self) -> dict[str, Any]:
}
if self.namespace is not None:
content["namespace"] = self.namespace.asdict()
if self.srs:
content["srs"] = self.srs
if self.title:
content.update(self.title.asdict())
if self.abstract:
content.update(self.abstract.asdict())
if self.keywords:
content["keywords"] = self.keywords
if self.native_bounding_box:
content["nativeBoundingBox"] = self.native_bounding_box
elif self.epsg_code:
Expand All @@ -178,35 +174,25 @@ def asdict(self) -> dict[str, Any]:
content["latLonBoundingBox"] = EPSG_BBOX[self.epsg_code][
"latLonBoundingBox"
]
if self.attributes:
content["attributes"] = self.attributes
if self.projection_policy is not None:
content["projectionPolicy"] = self.projection_policy
if self.enabled is not None:
content["enabled"] = self.enabled
if self.advertised is not None:
content["advertised"] = self.advertised
if self.service_configuration is not None:
content["serviceConfiguration"] = self.service_configuration
if self.simple_conversion_enabled is not None:
content["simpleConversionEnabled"] = self.simple_conversion_enabled
if self.max_features is not None:
content["maxFeatures"] = self.max_features
if self.num_decimals is not None:
content["numDecimals"] = self.num_decimals
if self.pad_with_zeros is not None:
content["padWithZeros"] = self.pad_with_zeros
if self.forced_decimals is not None:
content["forcedDecimals"] = self.forced_decimals
if self.overriding_service_srs is not None:
content["overridingServiceSRS"] = self.overriding_service_srs
if self.skip_number_match is not None:
content["skipNumberMatch"] = self.skip_number_match
if self.circular_arc_present is not None:
content["circularArcPresent"] = self.circular_arc_present
if self.encode_measures is not None:
content["encodeMeasures"] = self.encode_measures
return content
optional_items = {
"srs": self.srs,
"keywords": self.keywords,
"attributes": self.attributes,
"projectionPolicy": self.projection_policy,
"enabled": self.enabled,
"advertised": self.advertised,
"serviceConfiguration": self.service_configuration,
"simpleConversionEnabled": self.simple_conversion_enabled,
"maxFeatures": self.max_features,
"numDecimals": self.num_decimals,
"padWithZeros": self.pad_with_zeros,
"forcedDecimals": self.forced_decimals,
"overridingServiceSRS": self.overriding_service_srs,
"skipNumberMatch": self.skip_number_match,
"circularArcPresent": self.circular_arc_present,
"encodeMeasures": self.encode_measures,
}
return EntityModel.add_items_to_dict(content, optional_items)

def post_payload(self) -> dict[str, Any]:
content = self.asdict()
Expand Down
78 changes: 78 additions & 0 deletions geoservercloud/models/layer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from typing import Any

from geoservercloud.models import EntityModel, ReferencedObjectModel
from geoservercloud.models.styles import Styles


class Layer(EntityModel):
def __init__(
self,
name: str,
resource_name: str | None = None,
type: str | None = None,
default_style_name: str | None = None,
styles: list | None = None,
queryable: bool | None = None,
attribution: dict[str, int] | None = None,
) -> None:
self.name: str = name
self.type: str | None = type
self.resource: ReferencedObjectModel | None = None
if resource_name:
self.resource = ReferencedObjectModel(resource_name)
self.default_style: ReferencedObjectModel | None = None
if default_style_name:
self.default_style = ReferencedObjectModel(default_style_name)
self.styles: list | None = styles
self.queryable: bool | None = queryable
self.attribution: dict[str, Any] | None = attribution

@property
def resource_name(self) -> str | None:
return self.resource.name if self.resource else None

@property
def default_style_name(self) -> str | None:
return self.default_style.name if self.default_style else None

@classmethod
def from_get_response_payload(cls, content: dict):
layer = content["layer"]
if layer.get("styles"):
styles = Styles.from_get_response_payload(layer).aslist()
else:
styles = None
return cls(
name=layer["name"],
resource_name=layer["resource"]["name"],
type=layer["type"],
default_style_name=layer["defaultStyle"]["name"],
styles=styles,
attribution=layer["attribution"],
queryable=layer.get("queryable"),
)

def asdict(self) -> dict[str, Any]:
content: dict[str, Any] = {"name": self.name}
if self.styles is not None:
content.update(Styles(self.styles).post_payload())
optional_items = {
"name": self.name,
"type": self.type,
"resource": self.resource_name,
"defaultStyle": self.default_style_name,
"attribution": self.attribution,
"queryable": self.queryable,
}
return EntityModel.add_items_to_dict(content, optional_items)

def post_payload(self) -> dict[str, dict[str, Any]]:
content = self.asdict()
if self.resource:
content["resource"] = self.resource.asdict()
if self.default_style:
content["defaultStyle"] = self.default_style.asdict()
return {"layer": content}

def put_payload(self) -> dict[str, dict[str, Any]]:
return self.post_payload()
19 changes: 8 additions & 11 deletions geoservercloud/models/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,14 @@ def asdict(self) -> dict[str, Any]:
"format": self.format,
"languageVersion": self.language_version,
}
if self.workspace_name:
content["workspace"] = self.workspace_name
if self.filename:
content["filename"] = self.filename
if self.date_created:
content["dateCreated"] = self.date_created
if self.date_modified:
content["dateModified"] = self.date_modified
if self.legend:
content["legend"] = self.legend
return content
optional_items = {
"workspace": self.workspace_name,
"filename": self.filename,
"dateCreated": self.date_created,
"dateModified": self.date_modified,
"legend": self.legend,
}
return EntityModel.add_items_to_dict(content, optional_items)

def post_payload(self) -> dict[str, dict[str, Any]]:
content = self.asdict()
Expand Down
3 changes: 3 additions & 0 deletions geoservercloud/models/styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ def from_get_response_payload(cls, content: dict):
if not styles:
return cls([])
return cls([style["name"] for style in styles["style"]]) # type: ignore

def post_payload(self) -> dict[str, dict[str, list[dict[str, str]]]]:
return {"styles": {"style": [{"name": style} for style in self._styles]}}
9 changes: 4 additions & 5 deletions geoservercloud/services/restservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
)
from geoservercloud.models.featuretype import FeatureType
from geoservercloud.models.featuretypes import FeatureTypes
from geoservercloud.models.layer import Layer
from geoservercloud.services.restclient import RestClient
from geoservercloud.templates import Templates

Expand Down Expand Up @@ -394,12 +395,10 @@ def create_style_from_file(
response = self.rest_client.put(resource_path, data=data, headers=headers)
return response.content.decode(), response.status_code

def set_default_layer_style(
self, layer: str, workspace_name: str, style: str
) -> tuple[str, int]:
data = {"layer": {"defaultStyle": {"name": style}}}
def update_layer(self, layer: Layer, workspace_name: str) -> tuple[str, int]:
response: Response = self.rest_client.put(
self.rest_endpoints.workspace_layer(workspace_name, layer), json=data
self.rest_endpoints.workspace_layer(workspace_name, layer.name),
json=layer.put_payload(),
)
return response.content.decode(), response.status_code

Expand Down
78 changes: 78 additions & 0 deletions tests/models/test_layer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from geoservercloud.models.layer import Layer


def test_layer_post_payload():
layer = Layer(
name="test_point",
resource_name="test_workspace:test_point",
type="VECTOR",
default_style_name="point",
styles=["burg", "capitals"],
queryable=True,
attribution={"logoWidth": 0, "logoHeight": 0},
)

content = layer.post_payload()

assert content == {
"layer": {
"name": "test_point",
"type": "VECTOR",
"defaultStyle": {"name": "point"},
"styles": {
"style": [
{"name": "burg"},
{"name": "capitals"},
],
},
"resource": {
"name": "test_workspace:test_point",
},
"queryable": True,
"attribution": {"logoWidth": 0, "logoHeight": 0},
}
}


def test_from_get_response_payload():
content = {
"layer": {
"name": "test_point",
"type": "VECTOR",
"defaultStyle": {
"name": "point",
"href": "http://localhost:9099/geoserver/rest/styles/point.json",
},
"styles": {
"@class": "linked-hash-set",
"style": [
{
"name": "burg",
"href": "http://localhost:9099/geoserver/rest/styles/burg.json",
},
{
"name": "capitals",
"href": "http://localhost:9099/geoserver/rest/styles/capitals.json",
},
],
},
"resource": {
"@class": "featureType",
"name": "test_workspace:test_point",
"href": "http://localhost:9099/geoserver/rest/workspaces/elden/datastores/elden/featuretypes/test_point.json",
},
"attribution": {"logoWidth": 0, "logoHeight": 0},
"dateCreated": "2024-11-06 10:16:07.328 UTC",
"dateModified": "2024-11-06 14:48:09.460 UTC",
}
}

layer = Layer.from_get_response_payload(content)

assert layer.name == "test_point"
assert layer.resource_name == "test_workspace:test_point"
assert layer.type == "VECTOR"
assert layer.default_style_name == "point"
assert layer.styles == ["burg", "capitals"]
assert layer.attribution == {"logoWidth": 0, "logoHeight": 0}
assert layer.queryable is None
15 changes: 15 additions & 0 deletions tests/models/test_styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,18 @@ def test_styles_from_get_response_empty(empty_styles_get_response_payload):
)

assert styles_instance.aslist() == []


def test_styles_post_payload():
styles = ["style1", "style2"]

styles_instance = Styles(styles)

assert styles_instance.post_payload() == {
"styles": {
"style": [
{"name": "style1"},
{"name": "style2"},
]
}
}
Loading

0 comments on commit 12f4c73

Please sign in to comment.