Skip to content

Commit 4265418

Browse files
committed
update from master and raise invalidgeomname
2 parents b682eab + edf811b commit 4265418

File tree

4 files changed

+46
-50
lines changed

4 files changed

+46
-50
lines changed

tests/routes/test_item.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ def test_item(app):
1616
assert "text/html" in response.headers["content-type"]
1717
assert "Collection Item: 1" in response.text
1818

19-
# TODO Fix Table.query response
20-
# # not found
21-
# response = app.get("/collections/public.landsat_wrs/items/50000")
22-
# assert response.status_code == 404
19+
# not found
20+
response = app.get("/collections/public.landsat_wrs/items/50000")
21+
assert response.status_code == 404

tests/routes/test_items.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,9 @@ def test_items_properties_filter(app):
194194

195195
def test_items_filter_cql_ids(app):
196196
"""Test /items endpoint with ids options."""
197-
filter = {"op": "=", "args": [{"property": "ogc_fid"}, 1]}
197+
filter_query = {"op": "=", "args": [{"property": "ogc_fid"}, 1]}
198198
response = app.get(
199-
f"/collections/public.landsat_wrs/items?filter-lang=cql2-json&filter={json.dumps(filter)}"
199+
f"/collections/public.landsat_wrs/items?filter-lang=cql2-json&filter={json.dumps(filter_query)}"
200200
)
201201
assert response.status_code == 200
202202
assert response.headers["content-type"] == "application/geo+json"
@@ -222,7 +222,7 @@ def test_items_filter_cql_ids(app):
222222
response = app.get(
223223
"/collections/public.landsat_wrs/items?filter-lang=cql2-text&filter=ogc_fid IN (1,2)"
224224
)
225-
print(response.content)
225+
226226
assert response.status_code == 200
227227
assert response.headers["content-type"] == "application/geo+json"
228228
body = response.json()
@@ -237,9 +237,9 @@ def test_items_filter_cql_ids(app):
237237

238238
def test_items_properties_filter_cql2(app):
239239
"""Test /items endpoint with properties filter options."""
240-
filter = {"op": "=", "args": [{"property": "path"}, 13]}
240+
filter_query = {"op": "=", "args": [{"property": "path"}, 13]}
241241
response = app.get(
242-
f"/collections/public.landsat_wrs/items?filter-lang=cql2-json&filter={json.dumps(filter)}"
242+
f"/collections/public.landsat_wrs/items?filter-lang=cql2-json&filter={json.dumps(filter_query)}"
243243
)
244244
assert response.status_code == 200
245245
assert response.headers["content-type"] == "application/geo+json"
@@ -250,22 +250,22 @@ def test_items_properties_filter_cql2(app):
250250
assert body["features"][0]["properties"]["path"] == 13
251251

252252
# invalid type (str instead of int)
253-
filter = {"op": "=", "args": [{"property": "path"}, "d"]}
253+
filter_query = {"op": "=", "args": [{"property": "path"}, "d"]}
254254
response = app.get(
255-
f"/collections/public.landsat_wrs/items?filter-lang=cql2-json&filter={json.dumps(filter)}"
255+
f"/collections/public.landsat_wrs/items?filter-lang=cql2-json&filter={json.dumps(filter_query)}"
256256
)
257257
assert response.status_code == 500
258258
assert "integer is required" in response.json()["detail"]
259259

260-
filter = {
260+
filter_query = {
261261
"op": "and",
262262
"args": [
263263
{"op": "=", "args": [{"property": "path"}, 13]},
264264
{"op": "=", "args": [{"property": "row"}, 10]},
265265
],
266266
}
267267
response = app.get(
268-
f"/collections/public.landsat_wrs/items?filter-lang=cql2-json&filter={json.dumps(filter)}"
268+
f"/collections/public.landsat_wrs/items?filter-lang=cql2-json&filter={json.dumps(filter_query)}"
269269
)
270270
assert response.status_code == 200
271271
assert response.headers["content-type"] == "application/geo+json"
@@ -353,14 +353,15 @@ def test_items_datetime(app):
353353
body = response.json()
354354
assert body["detail"] == "Invalid Datetime Column: the_datetime."
355355

356-
# TODO Fix table.Query
357356
# no items for 2004-10-10T10:23:54
358-
# response = app.get("/collections/public.my_data/items?datetime=2004-10-10T10:23:54Z")
359-
# assert response.status_code == 200
360-
# assert response.headers["content-type"] == "application/geo+json"
361-
# body = response.json()
362-
# assert body["numberMatched"] == 0
363-
# assert body["numberReturned"] == 0
357+
response = app.get(
358+
"/collections/public.my_data/items?datetime=2004-10-10T10:23:54Z"
359+
)
360+
assert response.status_code == 200
361+
assert response.headers["content-type"] == "application/geo+json"
362+
body = response.json()
363+
assert body["numberMatched"] == 0
364+
assert body["numberReturned"] == 0
364365

365366
# Closed Interval
366367
response = app.get(

tifeatures/dbmodel.py

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,42 +56,39 @@ class Table(BaseModel):
5656
dbschema: str = Field(..., alias="schema")
5757
description: Optional[str]
5858
id_column: Optional[str]
59-
geometry_columns: Optional[List[GeometryColumn]]
59+
geometry_columns: List[GeometryColumn]
6060
properties: List[Column]
6161

6262
@property
63-
def datetime_columns(self) -> Optional[List[Column]]:
63+
def datetime_columns(self) -> List[Column]:
6464
"""Return the name of all timestamptz columns."""
6565
return [p for p in self.properties if p.type.startswith("timestamp")]
6666

67-
def datetime_column(self, dtcol: Optional[str] = None):
67+
def datetime_column(self, name: Optional[str] = None) -> Optional[Column]:
6868
"""Return the Column for either the passed in tstz column or the first tstz column."""
69-
if self.datetime_columns:
70-
for col in self.datetime_columns:
71-
if dtcol is None or col.name == dtcol:
72-
return col
69+
for col in self.datetime_columns:
70+
if name is None or col.name == name:
71+
return col
7372

7473
return None
7574

76-
def geometry_column(self, gcol: Optional[str] = None) -> Optional[GeometryColumn]:
75+
def geometry_column(self, name: Optional[str] = None) -> Optional[GeometryColumn]:
7776
"""Return the name of the first geometry column."""
78-
if (
79-
self.geometry_columns is not None
80-
and len(self.geometry_columns) > 0
81-
and gcol != "none"
82-
):
83-
for c in self.geometry_columns:
84-
if gcol is None or c.name == gcol:
85-
return c
77+
if name and name.lower() == "none":
78+
return None
79+
80+
for col in self.geometry_columns:
81+
if name is None or col.name == name:
82+
return col
8683

8784
return None
8885

8986
@property
9087
def id_column_info(self) -> Column: # type: ignore
9188
"""Return Column for a unique identifier."""
92-
for c in self.properties:
93-
if c.name == self.id_column:
94-
return c
89+
for col in self.properties:
90+
if col.name == self.id_column:
91+
return col
9592

9693
def columns(self, properties: Optional[List[str]] = None) -> List[str]:
9794
"""Return table columns optionally filtered to only include columns from properties."""
@@ -104,7 +101,7 @@ def columns(self, properties: Optional[List[str]] = None) -> List[str]:
104101
if geom_col:
105102
properties.append(geom_col.name)
106103

107-
cols = [c for c in cols if c in properties]
104+
cols = [col for col in cols if col in properties]
108105

109106
if len(cols) < 1:
110107
raise TypeError("No columns selected")

tifeatures/layer.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from tifeatures.errors import (
1818
InvalidDatetime,
1919
InvalidDatetimeColumnName,
20+
InvalidGeometryColumnName,
2021
InvalidPropertyName,
2122
MissingDatetimeColumn,
2223
)
@@ -133,6 +134,7 @@ def _geom(
133134

134135
if bbox_only:
135136
g = logic.Func("ST_Envelope", g)
137+
136138
elif simplify:
137139
g = logic.Func(
138140
"ST_SnapToGrid",
@@ -142,12 +144,14 @@ def _geom(
142144

143145
if geometry_column.srid == 4326:
144146
g = logic.Func("ST_AsGeoJson", g)
147+
145148
else:
146149
g = logic.Func(
147150
"ST_Transform",
148151
logic.Func("ST_AsGeoJson", g),
149152
4326,
150153
)
154+
151155
return g
152156

153157
def _where(
@@ -337,9 +341,8 @@ async def query(
337341
simplify: Optional[float] = None,
338342
) -> Tuple[FeatureCollection, int]:
339343
"""Build and run Pg query."""
340-
341-
geometry_columns = self.geometry_columns or []
342-
selected_geom_column = self.geometry_column(geom)
344+
if geom and geom.lower() != "none" and not self.geometry_column(geom):
345+
raise InvalidGeometryColumnName(f"Invalid Geometry Column: {geom}.")
343346

344347
sql_query = """
345348
WITH
@@ -389,19 +392,16 @@ async def query(
389392
),
390393
id_column=logic.V(self.id_column),
391394
geometry_q=self._geom(
392-
geometry_column=selected_geom_column,
395+
geometry_column=self.geometry_column(geom),
393396
bbox_only=bbox_only,
394397
simplify=simplify,
395398
),
396-
geom_columns=[g.name for g in geometry_columns],
399+
geom_columns=[g.name for g in self.geometry_columns],
397400
)
398401

399402
async with pool.acquire() as conn:
400403
items = await conn.fetchval(q, *p)
401404

402-
# TODO:
403-
# - make sure we always return features (even empty)
404-
# - make sure we always return total_count
405405
if items:
406406
return (
407407
FeatureCollection(features=items["features"]),
@@ -459,10 +459,9 @@ async def feature(
459459
@property
460460
def queryables(self) -> Dict:
461461
"""Return the queryables."""
462-
geometries = self.geometry_columns or []
463462
geoms = {
464463
col.name: {"$ref": geojson_schema.get(col.geometry_type.upper(), "")}
465-
for col in geometries
464+
for col in self.geometry_columns
466465
}
467466
props = {
468467
col.name: {"name": col.name, "type": col.json_type}

0 commit comments

Comments
 (0)