Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add layer data class #45

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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