Skip to content

Commit 25db357

Browse files
authored
Merge pull request #115 from xcube-dev/thomas-114-reload_capabilities
Correctly invalidate capabilities cache
2 parents d90b287 + 6f6fd42 commit 25db357

File tree

4 files changed

+105
-25
lines changed

4 files changed

+105
-25
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### New Features
44

5+
- the geoDB client correctly refreshes the capabilities of the PostGREST server
56
- geoDB now logs collection read events, if deliberately triggered by the user
67
- cleaned up documentation (#110)
78

tests/core/test_geodb.py

+40
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,46 @@ def test_create_collection(self, m):
578578
json.loads(log_event_endpoint.last_request.text),
579579
)
580580

581+
# noinspection DuplicatedCode
582+
def test_capabilities_cache_on_create(self, m):
583+
self.set_global_mocks(m)
584+
585+
response = "Success"
586+
url = f"{self._base_url}/geodb_user_databases?name=eq.helge"
587+
m.get(url, text=json.dumps(response))
588+
url = f"{self._base_url}/rpc/geodb_create_collections"
589+
m.post(url, text=json.dumps(response))
590+
url = f"{self._base_url}/"
591+
capabilities_endpoint = m.get(
592+
url, text=json.dumps({"definitions": {"helge_test": "dummy"}})
593+
)
594+
595+
self.assertEqual(0, capabilities_endpoint.call_count)
596+
self._api.create_collection(collection="test", properties={"test_col": "inger"})
597+
self._api.get_collection_info(collection="test")
598+
599+
self.assertEqual(1, capabilities_endpoint.call_count)
600+
601+
# noinspection DuplicatedCode
602+
def test_capabilities_cache_on_drop(self, m):
603+
self.set_global_mocks(m)
604+
605+
response = "Success"
606+
url = f"{self._base_url}/geodb_user_databases?name=eq.helge"
607+
m.get(url, text=json.dumps(response))
608+
url = f"{self._base_url}/rpc/geodb_drop_collections"
609+
m.post(url, text=json.dumps(response))
610+
url = f"{self._base_url}/"
611+
capabilities_endpoint = m.get(
612+
url, text=json.dumps({"definitions": {"helge_test": "dummy"}})
613+
)
614+
615+
self.assertEqual(0, capabilities_endpoint.call_count)
616+
self._api.drop_collection(collection="test")
617+
self._api.get_collection_info(collection="test")
618+
619+
self.assertEqual(1, capabilities_endpoint.call_count)
620+
581621
def test_create_collections(self, m):
582622
expected_response = {
583623
"collections": {

xcube_geodb/core/geodb.py

+7-11
Original file line numberDiff line numberDiff line change
@@ -845,8 +845,6 @@ def create_collections(
845845
except GeoDBError:
846846
pass
847847

848-
self._refresh_capabilities()
849-
850848
database = database or self.database
851849

852850
if not self.database_exists(database):
@@ -861,6 +859,7 @@ def create_collections(
861859
self._post(path="/rpc/geodb_create_collections", payload=collections)
862860
for collection in collections["collections"]:
863861
self._log_event(EventType.CREATED, f"collection {collection}")
862+
self._refresh_capabilities()
864863
return Message(collections)
865864
except GeoDBError as e:
866865
return self._maybe_raise(e)
@@ -894,8 +893,6 @@ def create_collection(
894893
crs = check_crs(crs)
895894
collections = {collection: {"properties": properties, "crs": str(crs)}}
896895

897-
self._refresh_capabilities()
898-
899896
return self.create_collections(
900897
collections=collections, database=database, clear=clear
901898
)
@@ -944,8 +941,6 @@ def drop_collections(
944941
>>> geodb.drop_collections(collections=['[MyCollection1]', '[MyCollection2]'])
945942
"""
946943

947-
self._refresh_capabilities()
948-
949944
database = database or self.database
950945
collections = [database + "_" + collection for collection in collections]
951946
payload = {
@@ -957,6 +952,7 @@ def drop_collections(
957952
self._post(path="/rpc/geodb_drop_collections", payload=payload)
958953
for collection in collections:
959954
self._log_event(EventType.DROPPED, f"collection {collection}")
955+
self._refresh_capabilities()
960956
return Message(f"Collection {str(collections)} deleted")
961957
except GeoDBError as e:
962958
return self._maybe_raise(e)
@@ -1253,8 +1249,6 @@ def add_properties(
12531249
properties=properties)
12541250
"""
12551251

1256-
self._refresh_capabilities()
1257-
12581252
database = database or self.database
12591253
dn = database + "_" + collection
12601254

@@ -1269,6 +1263,7 @@ def add_properties(
12691263
EventType.PROPERTY_ADDED,
12701264
f"{{name: {prop_name}, type: {prop_type}}} to collection {dn}",
12711265
)
1266+
self._refresh_capabilities()
12721267
return Message("Properties added")
12731268
except GeoDBError as e:
12741269
return self._maybe_raise(e)
@@ -1319,7 +1314,6 @@ def drop_properties(
13191314
'MyProperty2'])
13201315
"""
13211316

1322-
self._refresh_capabilities()
13231317
database = database or self.database
13241318
collection = database + "_" + collection
13251319

@@ -1336,8 +1330,10 @@ def drop_properties(
13361330
self._log_event(
13371331
EventType.PROPERTY_DROPPED, f"{prop} from collection {collection}"
13381332
)
1339-
1340-
return Message(f"Properties {str(properties)} dropped from {collection}")
1333+
self._refresh_capabilities()
1334+
return Message(
1335+
f"Properties {str(properties)} dropped from " f"{collection}"
1336+
)
13411337
except GeoDBError as e:
13421338
return self._maybe_raise(e)
13431339

xcube_geodb/sql/geodb.sql

+57-14
Original file line numberDiff line numberDiff line change
@@ -129,20 +129,6 @@ CREATE TABLE IF NOT EXISTS public.geodb_user_databases
129129
GRANT SELECT, INSERT, UPDATE ON geodb_user_databases TO PUBLIC;
130130
GRANT SELECT, UPDATE, USAGE ON geodb_user_databases_seq TO PUBLIC;
131131

132-
CREATE OR REPLACE FUNCTION public.notify_ddl_postgrest()
133-
RETURNS event_trigger
134-
LANGUAGE plpgsql
135-
AS
136-
$$
137-
BEGIN
138-
NOTIFY ddl_command_end;
139-
END;
140-
$$;
141-
142-
DROP EVENT TRIGGER IF EXISTS ddl_postgrest;
143-
CREATE EVENT TRIGGER ddl_postgrest ON ddl_command_end
144-
EXECUTE PROCEDURE public.notify_ddl_postgrest();
145-
146132
-- ensures that database of collection belongs to user or group
147133
CREATE
148134
OR REPLACE FUNCTION public.geodb_user_allowed(
@@ -1685,3 +1671,60 @@ BEGIN
16851671
) as res', collection);
16861672
END
16871673
$BODY$;
1674+
1675+
-- Below: watching PostGREST schema cache changes to the database, and trigger a
1676+
-- reload.
1677+
-- Code copied from https://postgrest.org/en/stable/references/schema_cache.html.
1678+
-- BEGIN of copied code
1679+
-- watch CREATE and ALTER
1680+
CREATE OR REPLACE FUNCTION pgrst_ddl_watch() RETURNS event_trigger AS
1681+
$$
1682+
DECLARE
1683+
cmd record;
1684+
BEGIN
1685+
FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands()
1686+
LOOP
1687+
IF cmd.command_tag IN (
1688+
'CREATE SCHEMA', 'ALTER SCHEMA', 'CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO',
1689+
'ALTER TABLE', 'CREATE FOREIGN TABLE', 'ALTER FOREIGN TABLE', 'CREATE VIEW',
1690+
'ALTER VIEW', 'CREATE MATERIALIZED VIEW', 'ALTER MATERIALIZED VIEW',
1691+
'CREATE FUNCTION', 'ALTER FUNCTION', 'CREATE TRIGGER', 'CREATE TYPE', 'ALTER TYPE',
1692+
'CREATE RULE', 'COMMENT'
1693+
)
1694+
-- don't notify in case of CREATE TEMP table or other objects created on pg_temp
1695+
AND cmd.schema_name is distinct from 'pg_temp'
1696+
THEN
1697+
NOTIFY pgrst, 'reload schema';
1698+
END IF;
1699+
END LOOP;
1700+
END;
1701+
$$ LANGUAGE plpgsql;
1702+
1703+
-- watch DROP
1704+
CREATE OR REPLACE FUNCTION pgrst_drop_watch() RETURNS event_trigger AS
1705+
$$
1706+
DECLARE
1707+
obj record;
1708+
BEGIN
1709+
FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
1710+
LOOP
1711+
IF obj.object_type IN (
1712+
'schema', 'table', 'foreign table', 'view', 'materialized view', 'function',
1713+
'trigger', 'type', 'rule'
1714+
)
1715+
AND obj.is_temporary IS false -- no pg_temp objects
1716+
THEN
1717+
NOTIFY pgrst, 'reload schema';
1718+
END IF;
1719+
END LOOP;
1720+
END;
1721+
$$ LANGUAGE plpgsql;
1722+
1723+
CREATE EVENT TRIGGER pgrst_ddl_watch
1724+
ON ddl_command_end
1725+
EXECUTE PROCEDURE pgrst_ddl_watch();
1726+
1727+
CREATE EVENT TRIGGER pgrst_drop_watch
1728+
ON sql_drop
1729+
EXECUTE PROCEDURE pgrst_drop_watch();
1730+
-- END of copied code

0 commit comments

Comments
 (0)