geoservercloud 0.8.3.dev2__tar.gz → 0.8.3.dev4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/PKG-INFO +1 -1
  2. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/README.md +7 -6
  3. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/conftest.py +10 -0
  4. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_cascaded_stores.py +1 -1
  5. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_gwc.py +1 -1
  6. geoservercloud-0.8.3.dev4/geoserver_acceptance_tests/tests/test_s3_credentials_chain.py +247 -0
  7. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/pyproject.toml +1 -1
  8. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/LICENSE +0 -0
  9. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/README.md +0 -0
  10. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/.gitignore +0 -0
  11. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/__init__.py +0 -0
  12. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/cli.py +0 -0
  13. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/compose/example.compose.yaml +0 -0
  14. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/compose/geodatabase/001_create_schemas.sql +0 -0
  15. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/config.py +0 -0
  16. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/data/sampledata.tgz +0 -0
  17. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/example.config.yaml +0 -0
  18. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/__init__.py +0 -0
  19. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/__init__.py +0 -0
  20. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/__init__.py +0 -0
  21. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/default_locale/default_value/language_None_expected.png +0 -0
  22. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/default_locale/default_value/language__expected.png +0 -0
  23. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/default_locale/default_value/language_de_expected.png +0 -0
  24. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/default_locale/default_value/language_fr_expected.png +0 -0
  25. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/default_locale/default_value/language_it_expected.png +0 -0
  26. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/default_locale/no_default_value/language_None_expected.png +0 -0
  27. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/default_locale/no_default_value/language__expected.png +0 -0
  28. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/default_locale/no_default_value/language_it_expected.png +0 -0
  29. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/no_default_locale/default_value/language_None_expected.png +0 -0
  30. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/no_default_locale/default_value/language__expected.png +0 -0
  31. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/no_default_locale/default_value/language_de_expected.png +0 -0
  32. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/no_default_locale/default_value/language_fr_expected.png +0 -0
  33. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/no_default_locale/default_value/language_it_expected.png +0 -0
  34. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/no_default_locale/no_default_value/language_None_expected.png +0 -0
  35. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/no_default_locale/no_default_value/language__expected.png +0 -0
  36. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/labels/no_default_locale/no_default_value/language_it_expected.png +0 -0
  37. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/localized_labels.sld +0 -0
  38. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/localized_no_default.sld +0 -0
  39. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/i18n/localized_with_default.sld +0 -0
  40. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/wfs/__init__.py +0 -0
  41. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/wfs/wfs_delete_payload.xml +0 -0
  42. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/wfs/wfs_insert_payload.xml +0 -0
  43. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/wms/__init__.py +0 -0
  44. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/resources/wms/getmap_expected.png +0 -0
  45. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_cog.py +0 -0
  46. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_datastore.py +0 -0
  47. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_get_version.py +0 -0
  48. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_i18n.py +0 -0
  49. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_imagemosaic.py +0 -0
  50. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_imagemosaic_cog.py +0 -0
  51. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_installed_fonts.py +0 -0
  52. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_ogcapi_features.py +0 -0
  53. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_wfs.py +0 -0
  54. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_wms.py +0 -0
  55. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/test_workspace.py +0 -0
  56. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoserver_acceptance_tests/tests/utils.py +0 -0
  57. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/__init__.py +0 -0
  58. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/geoservercloud.py +0 -0
  59. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/geoservercloudsync.py +0 -0
  60. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/gridsets/2056.xml +0 -0
  61. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/gridsets/21781.xml +0 -0
  62. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/gridsets/3857.xml +0 -0
  63. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/__init__.py +0 -0
  64. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/abstractlayer.py +0 -0
  65. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/common.py +0 -0
  66. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/coverage.py +0 -0
  67. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/coverages.py +0 -0
  68. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/coveragestore.py +0 -0
  69. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/datastore.py +0 -0
  70. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/datastores.py +0 -0
  71. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/featuretype.py +0 -0
  72. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/featuretypes.py +0 -0
  73. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/layer.py +0 -0
  74. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/layergroup.py +0 -0
  75. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/layergroups.py +0 -0
  76. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/resourcedirectory.py +0 -0
  77. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/style.py +0 -0
  78. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/styles.py +0 -0
  79. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/wmslayer.py +0 -0
  80. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/wmssettings.py +0 -0
  81. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/wmsstore.py +0 -0
  82. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/workspace.py +0 -0
  83. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/models/workspaces.py +0 -0
  84. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/services/__init__.py +0 -0
  85. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/services/owsservice.py +0 -0
  86. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/services/restclient.py +0 -0
  87. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/services/restlogger.py +0 -0
  88. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/services/restservice.py +0 -0
  89. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/templates.py +0 -0
  90. {geoservercloud-0.8.3.dev2 → geoservercloud-0.8.3.dev4}/geoservercloud/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: geoservercloud
3
- Version: 0.8.3.dev2
3
+ Version: 0.8.3.dev4
4
4
  Summary: Lightweight Python client to interact with GeoServer Cloud REST API, GeoServer ACL and OGC services
5
5
  License: BSD-2-Clause
6
6
  Author: Camptocamp
@@ -90,12 +90,13 @@ GEOSERVER_ACCEPTANCE_CONFIG=./config.yaml pytest --pyargs geoserver_acceptance_t
90
90
 
91
91
  Certain kinds of tests can be enabled or disabled through environment variables:
92
92
 
93
- | Environment Variable | Description | Default |
94
- | ------------------------------------- | ------------------------------------------------------------- | ------- |
95
- | `GEOSERVER_ACCEPTANCE_RUN_DB_TESTS` | Enable/disable tests requiring DB access | `true` |
96
- | `GEOSERVER_ACCEPTANCE_RUN_SLOW_TESTS` | Enable/disable slow tests | `false` |
97
- | `GEOSERVER_ACCEPTANCE_RUN_COG_TESTS` | Enable/disable COG tests | `false` |
98
- | `GEOSERVER_ACCEPTANCE_RUN_JNDI_TESTS` | Enable/disable tests requiring a JNDI resource (jdbc/postgis) | `false` |
93
+ | Environment Variable | Description | Default |
94
+ | --------------------------------------- | ------------------------------------------------------------- | ------- |
95
+ | `GEOSERVER_ACCEPTANCE_RUN_DB_TESTS` | Enable/disable tests requiring DB access | `true` |
96
+ | `GEOSERVER_ACCEPTANCE_RUN_SLOW_TESTS` | Enable/disable slow tests | `false` |
97
+ | `GEOSERVER_ACCEPTANCE_RUN_COG_TESTS` | Enable/disable COG tests | `false` |
98
+ | `GEOSERVER_ACCEPTANCE_RUN_JNDI_TESTS` | Enable/disable tests requiring a JNDI resource (jdbc/postgis) | `false` |
99
+ | `GEOSERVER_ACCEPTANCE_RUN_AWS_S3_TESTS` | Enable/disable tests that use S3 buckets with AWS credentials | `false` |
99
100
 
100
101
  ### Run the example docker composition
101
102
 
@@ -112,6 +112,7 @@ def pytest_configure(config):
112
112
  "cog: mark test as related to COG ImageMosaic",
113
113
  "jndi: mark test as requiring JNDI resource (jdbc/postgis) for DB access",
114
114
  "slow: mark test as slow to skip or run conditionally",
115
+ "aws_s3: mark test as requiring AWS S3 access",
115
116
  ]
116
117
  for marker in markers:
117
118
  config.addinivalue_line("markers", marker)
@@ -162,3 +163,12 @@ def pytest_collection_modifyitems(config, items):
162
163
  for item in items:
163
164
  if "jndi" in item.keywords:
164
165
  item.add_marker(skip_jndi)
166
+
167
+ # Run AWS S3 test (default: false)
168
+ if not env_var_is_true("GEOSERVER_ACCEPTANCE_RUN_AWS_S3_TESTS", False):
169
+ skip_aws_s3 = pytest.mark.skip(
170
+ reason="use env var GEOSERVER_ACCEPTANCE_RUN_AWS_S3_TESTS=true to run"
171
+ )
172
+ for item in items:
173
+ if "aws_s3" in item.keywords:
174
+ item.add_marker(skip_aws_s3)
@@ -91,7 +91,7 @@ def test_cascaded_wmts(geoserver_factory):
91
91
 
92
92
  # Publish the layer in GWC
93
93
  content, status = geoserver.publish_gwc_layer(workspace, wmts_layer)
94
- assert content == ""
94
+ assert content in ["", "layer saved"]
95
95
  assert status == 200
96
96
  content, status = geoserver.get_gwc_layer(workspace, wmts_layer)
97
97
  assert status == 200
@@ -21,7 +21,7 @@ def test_tile_cache(geoserver_factory):
21
21
 
22
22
  content, status = geoserver.publish_gwc_layer(workspace, wmts_layer)
23
23
  assert status == 200
24
- assert content == ""
24
+ assert content in ["", "layer saved"]
25
25
 
26
26
  response = geoserver.get_tile(
27
27
  format="image/png",
@@ -0,0 +1,247 @@
1
+ """
2
+ Integration tests for cloud native data formats on private S3 buckets using AWS credentials chain.
3
+
4
+ These tests verify that GeoServer Cloud can access and serve cloud-optimized formats (COG, GeoParquet, PMTiles)
5
+ stored on private S3 buckets by using the AWS default credentials chain for authentication.
6
+
7
+ The credentials can be provided through:
8
+ - Environment variables: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
9
+ - AWS credentials file: ~/.aws/credentials (mounted at /opt/app/home/.aws/credentials in containers)
10
+ - IAM roles (when running on AWS infrastructure)
11
+
12
+ In local development, credentials are typically mounted from compose/secrets/aws directory.
13
+ In CI/CD (GitHub Actions), credentials are configured from repository secrets.
14
+ """
15
+
16
+ import pytest
17
+
18
+
19
+ @pytest.mark.aws_s3
20
+ def test_cog(geoserver_factory):
21
+ """Test creating a COG coverage store and coverage on private S3 bucket"""
22
+ workspace = "s3_private_cog"
23
+ store_name = "land_shallow_topo_21600_NE_cog"
24
+ coverage_name = "land_shallow_topo_21600_NE_cog"
25
+ geoserver = geoserver_factory(workspace)
26
+
27
+ # Create COG coverage store
28
+ content, status = geoserver.create_coverage_store(
29
+ workspace_name=workspace,
30
+ coveragestore_name=store_name,
31
+ type="GeoTIFF",
32
+ url=f"cog://https://s3-us-east-1.amazonaws.com/geoserver-test-data-private/cog/land_shallow_topo/land_shallow_topo_21600_NE_cog.tif",
33
+ metadata={"cogSettings": {"rangeReaderSettings": "S3"}},
34
+ )
35
+ assert status == 201
36
+ assert content == store_name
37
+
38
+ # Create coverage
39
+ content, status = geoserver.create_coverage(
40
+ workspace_name=workspace,
41
+ coveragestore_name=store_name,
42
+ coverage_name=coverage_name,
43
+ native_name=store_name,
44
+ )
45
+ assert status == 201
46
+ assert content == coverage_name
47
+
48
+ # Verify the coverage was created - try listing coverages first
49
+ content, status = geoserver.get_coverages(workspace, store_name)
50
+ assert status == 200, f"Failed to get coverages: {status} - {content}"
51
+ assert content[0].get("name") == store_name
52
+
53
+ # Check specific coverage
54
+ content, status = geoserver.get_coverage(workspace, store_name, coverage_name)
55
+ assert status == 200, f"Failed to get coverage: {status} - {content}"
56
+
57
+ # Verify coverage properties
58
+ assert content.get("name") == coverage_name
59
+ assert content.get("nativeName") == store_name
60
+ assert content.get("enabled") is True
61
+
62
+ # Test WMS GetMap request
63
+ wms_response = geoserver.get_map(
64
+ layers=[f"{workspace}:{coverage_name}"],
65
+ bbox=(0, 0, 180, 90),
66
+ size=(256, 256),
67
+ srs="EPSG:4326",
68
+ format="image/jpeg",
69
+ )._response
70
+ assert wms_response.status_code == 200
71
+ assert wms_response.headers.get("content-type").startswith("image/jpeg")
72
+
73
+
74
+ @pytest.mark.aws_s3
75
+ def test_geoparquet(geoserver_factory):
76
+ """Test creating a GeoParquet datastore on private S3 bucket"""
77
+ workspace = "s3_private_geoparquet"
78
+ datastore = "germany"
79
+ geoserver = geoserver_factory(workspace)
80
+
81
+ # Create GeoParquet datastore with S3 credentials chain
82
+ content, status = geoserver.create_datastore(
83
+ workspace_name=workspace,
84
+ datastore_name=datastore,
85
+ datastore_type="GeoParquet",
86
+ connection_parameters={
87
+ "dbtype": "geoparquet",
88
+ "uri": "s3://geoserver-test-data-private/geoparquet/overture/singlefiles/germany/*",
89
+ "namespace": workspace,
90
+ "use_aws_credential_chain": True,
91
+ "fetch size": 1000,
92
+ "screenmap": True,
93
+ "simplification": True,
94
+ },
95
+ description="GeoParquet datastore on private S3 bucket using AWS credentials chain",
96
+ )
97
+ assert status == 201, f"Failed to create datastore: {status} - {content}"
98
+ assert content == datastore
99
+
100
+ # Verify the datastore was created
101
+ datastores, status = geoserver.get_datastores(workspace)
102
+ assert status == 200
103
+ assert datastore in [ds.get("name") for ds in datastores]
104
+
105
+ # Get the datastore details
106
+ datastore_info, status = geoserver.get_pg_datastore(workspace, datastore)
107
+ assert status == 200
108
+ assert datastore_info.get("name") == datastore
109
+ assert datastore_info.get("type") == "GeoParquet"
110
+ assert datastore_info.get("enabled") is True
111
+
112
+ # Create feature type with explicit attributes since GeoParquet schema is known
113
+ feature_type = "addresses"
114
+ attributes = {
115
+ "geometry": {"type": "Point", "required": False},
116
+ "bbox": {"type": "string", "required": False}, # Struct type
117
+ "country": {"type": "string", "required": False},
118
+ "postcode": {"type": "string", "required": False},
119
+ "street": {"type": "string", "required": False},
120
+ "number": {"type": "string", "required": False},
121
+ "unit": {"type": "string", "required": False},
122
+ "postal_city": {"type": "string", "required": False},
123
+ "version": {"type": "integer", "required": False},
124
+ "theme": {"type": "string", "required": False},
125
+ "type": {"type": "string", "required": False},
126
+ }
127
+ content, status = geoserver.create_feature_type(
128
+ layer_name=feature_type,
129
+ workspace_name=workspace,
130
+ datastore_name=datastore,
131
+ title="Germany Addresses from Overture Maps",
132
+ abstract="Address points from Overture Maps for Germany, stored as GeoParquet on S3",
133
+ epsg=4326,
134
+ attributes=attributes,
135
+ )
136
+ assert status == 201, f"Failed to create feature type: {status} - {content}"
137
+
138
+ # Verify the feature type was created
139
+ feature_types, status = geoserver.get_feature_types(workspace, datastore)
140
+ assert status == 200
141
+ assert feature_type in [ft.get("name") for ft in feature_types]
142
+
143
+ # Get the feature type details
144
+ ft_info, status = geoserver.get_feature_type(workspace, datastore, feature_type)
145
+ assert status == 200
146
+ assert ft_info.get("name") == feature_type
147
+ assert ft_info.get("enabled") is True
148
+
149
+ # Test WFS GetFeature request (limit to 10 features for faster test)
150
+ feature_collection = geoserver.get_feature(workspace, feature_type, max_feature=10)
151
+ assert isinstance(feature_collection, dict)
152
+ assert isinstance(feature_collection.get("features"), list)
153
+ assert len(feature_collection.get("features", [])) > 0
154
+
155
+ # Verify feature has expected properties
156
+ feature = feature_collection["features"][0]
157
+ properties = feature.get("properties")
158
+ assert "geometry" in feature or "geometry" in properties
159
+ # Check for some expected GeoParquet attributes
160
+ assert any(
161
+ key in properties for key in ["country", "street", "postcode", "postal_city"]
162
+ )
163
+
164
+
165
+ @pytest.mark.aws_s3
166
+ def test_pmtiles(geoserver_factory):
167
+ """Test creating a PMTiles datastore on private S3 bucket"""
168
+ workspace = "s3_private_pmtiles"
169
+ datastore = "europe"
170
+ geoserver = geoserver_factory(workspace)
171
+
172
+ # Create PMTiles datastore with S3 credentials chain
173
+ content, status = geoserver.create_datastore(
174
+ workspace_name=workspace,
175
+ datastore_name=datastore,
176
+ datastore_type="PMTiles",
177
+ connection_parameters={
178
+ "pmtiles": "s3://geoserver-test-data-private/pmtiles/shortbread/europe.pmtiles",
179
+ "namespace": workspace,
180
+ "io.tileverse.rangereader.s3.use-default-credentials-provider": True,
181
+ "io.tileverse.rangereader.caching.enabled": True,
182
+ "io.tileverse.rangereader.caching.blockaligned": True,
183
+ },
184
+ description="PMTiles datastore on private S3 bucket using AWS credentials chain",
185
+ )
186
+ assert status == 201, f"Failed to create datastore: {status} - {content}"
187
+ assert content == datastore
188
+
189
+ # Verify the datastore was created
190
+ datastores, status = geoserver.get_datastores(workspace)
191
+ assert status == 200
192
+ assert datastore in [ds.get("name") for ds in datastores]
193
+
194
+ # Get the datastore details
195
+ datastore_info, status = geoserver.get_pg_datastore(workspace, datastore)
196
+ assert status == 200
197
+ assert datastore_info.get("name") == datastore
198
+ assert datastore_info.get("type") == "PMTiles"
199
+ assert datastore_info.get("enabled") is True
200
+
201
+ # Create feature type with explicit attributes for PMTiles boundaries layer
202
+ # Using EPSG:3857 (Web Mercator) - supported via monkey-patch at top of file
203
+ feature_type = "boundaries"
204
+ attributes = {
205
+ "the_geom": {
206
+ "type": "MultiPolygon",
207
+ "required": False,
208
+ }, # Boundaries are typically MultiPolygons
209
+ "admin_level": {"type": "double", "required": False},
210
+ "disputed": {"type": "boolean", "required": False},
211
+ "maritime": {"type": "boolean", "required": False},
212
+ }
213
+ content, status = geoserver.create_feature_type(
214
+ layer_name=feature_type,
215
+ workspace_name=workspace,
216
+ datastore_name=datastore,
217
+ title="Boundaries from Shortbread Europe PMTiles",
218
+ abstract="Administrative boundaries from Shortbread schema, stored as PMTiles on S3",
219
+ epsg=3857, # PMTiles uses Web Mercator (EPSG:3857)
220
+ attributes=attributes,
221
+ )
222
+ assert status == 201, f"Failed to create feature type: {status} - {content}"
223
+
224
+ # Verify the feature type was created
225
+ feature_types, status = geoserver.get_feature_types(workspace, datastore)
226
+ assert status == 200
227
+ assert feature_type in [ft.get("name") for ft in feature_types]
228
+
229
+ # Get the feature type details
230
+ ft_info, status = geoserver.get_feature_type(workspace, datastore, feature_type)
231
+ assert status == 200
232
+ assert ft_info.get("name") == feature_type
233
+ assert ft_info.get("enabled") is True
234
+ assert ft_info.get("srs") == "EPSG:3857"
235
+
236
+ # Test WFS GetFeature request (limit to 10 features for faster test)
237
+ feature_collection = geoserver.get_feature(workspace, feature_type, max_feature=10)
238
+ assert isinstance(feature_collection, dict)
239
+ assert isinstance(feature_collection.get("features"), list)
240
+ assert len(feature_collection.get("features", [])) > 0
241
+
242
+ # Verify feature has expected properties
243
+ feature = feature_collection["features"][0]
244
+ properties = feature.get("properties")
245
+ assert "geometry" in feature or "the_geom" in properties
246
+ # Check for some expected PMTiles attributes
247
+ assert any(key in properties for key in ["admin_level", "disputed", "maritime"])
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "geoservercloud"
3
- version = "0.8.3.dev2"
3
+ version = "0.8.3.dev4"
4
4
  description = "Lightweight Python client to interact with GeoServer Cloud REST API, GeoServer ACL and OGC services"
5
5
  authors = ["Camptocamp <info@camptocamp.com>"]
6
6
  license = "BSD-2-Clause"