diff --git a/doc/rest.md b/doc/rest.md index 131e2525a..c952121f1 100644 --- a/doc/rest.md +++ b/doc/rest.md @@ -50,6 +50,7 @@ JSON array of objects representing available layers with following structure: - **title**: String. Title of the layer. - **uuid**: String. UUID of the layer. - **url**: String. URL of the layer. It points to [GET Workspace Layer](#get-workspace-layer). +- **updated_at**: String. Date and time of last POST/PATCH of the publication. Format is [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), more specifically `YYYY-MM-DDThh:mm:ss.sss±hh:mm`, always in UTC. Sample value: `"2021-03-18T09:29:53.769233+00:00"` - **access_rights**: - **read**: Array of strings. Names of [users](./models.md#user) and [roles](./models.md#role) with [read access](./security.md#Authorization). - **write**: Array of strings. Names of [users](./models.md#user) and [roles](./models.md#role) with [write access](./security.md#Authorization). @@ -72,6 +73,7 @@ JSON array of objects representing available layers with following structure: - **title**: String. Title of the layer. - **uuid**: String. UUID of the layer. - **url**: String. URL of the layer. It points to [GET Workspace Layer](#get-workspace-layer). +- **updated_at**: String. Date and time of last POST/PATCH of the publication. Format is [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), more specifically `YYYY-MM-DDThh:mm:ss.sss±hh:mm`, always in UTC. Sample value: `"2021-03-18T09:29:53.769233+00:00"` - **access_rights**: - **read**: Array of strings. Names of [users](./models.md#user) and [roles](./models.md#role) with [read access](./security.md#Authorization). - **write**: Array of strings. Names of [users](./models.md#user) and [roles](./models.md#role) with [write access](./security.md#Authorization). @@ -196,6 +198,7 @@ JSON object with following structure: - **url**: String. URL pointing to this endpoint. - **title**: String. - **description**: String. +- **updated_at**: String. Date and time of last POST/PATCH of the publication. Format is [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), more specifically `YYYY-MM-DDThh:mm:ss.sss±hh:mm`, always in UTC. Sample value: `"2021-03-18T09:29:53.769233+00:00"` - **wms** - *url*: String. URL of WMS endpoint. It points to WMS endpoint of appropriate GeoServer workspace. - *status*: Status information about GeoServer import and availability of WMS layer. No status object means the source is available. Usual state values are @@ -412,6 +415,7 @@ JSON array of objects representing available maps with following structure: - **title**: String. Title of the map. - **uuid**: String. UUID of the map. - **url**: String. URL of the map. It points to [GET Workspace Map](#get-workspace-map). +- **updated_at**: String. Date and time of last POST/PATCH of the publication. Format is [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), more specifically `YYYY-MM-DDThh:mm:ss.sss±hh:mm`, always in UTC. Sample value: `"2021-03-18T09:29:53.769233+00:00"` - **access_rights**: - **read**: Array of strings. Names of [users](./models.md#user) and [roles](./models.md#role) with [read access](./security.md#Authorization). - **write**: Array of strings. Names of [users](./models.md#user) and [roles](./models.md#role) with [write access](./security.md#Authorization). @@ -435,6 +439,7 @@ JSON array of objects representing available maps with following structure: - **title**: String. Title of the map. - **uuid**: String. UUID of the map. - **url**: String. URL of the map. It points to [GET Workspace Map](#get-workspace-map). +- **updated_at**: String. Date and time of last POST/PATCH of the publication. Format is [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), more specifically `YYYY-MM-DDThh:mm:ss.sss±hh:mm`, always in UTC. Sample value: `"2021-03-18T09:29:53.769233+00:00"` - **access_rights**: - **read**: Array of strings. Names of [users](./models.md#user) and [roles](./models.md#role) with [read access](./security.md#Authorization). - **write**: Array of strings. Names of [users](./models.md#user) and [roles](./models.md#role) with [write access](./security.md#Authorization). @@ -527,6 +532,7 @@ JSON object with following structure: - **url**: String. URL pointing to this endpoint. - **title**: String. Taken from `title` attribute of JSON root object - **description**: String. Taken from `abstract` attribute of JSON root object. +- **updated_at**: String. Date and time of last POST/PATCH of the publication. Format is [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), more specifically `YYYY-MM-DDThh:mm:ss.sss±hh:mm`, always in UTC. Sample value: `"2021-03-18T09:29:53.769233+00:00"` - **file** - *url*: String. URL of map-composition JSON file. It points to [GET Workspace Map File](#get-workspace-map-file). - *path*: String. Path to map-composition JSON file, relative to workspace directory. diff --git a/src/layman/common/prime_db_schema/publications.py b/src/layman/common/prime_db_schema/publications.py index 4ba943235..870bf1e14 100644 --- a/src/layman/common/prime_db_schema/publications.py +++ b/src/layman/common/prime_db_schema/publications.py @@ -65,6 +65,7 @@ def get_publication_infos(workspace_name=None, pub_type=None, style_type=None, p.title, p.uuid::text, p.style_type, + p.updated_at, (select rtrim(concat(case when u.id is not null then w.name || ',' end, string_agg(w2.name, ',') || ',', case when p.everyone_can_read then %s || ',' end @@ -130,10 +131,11 @@ def get_publication_infos(workspace_name=None, pub_type=None, style_type=None, 'uuid': uuid, 'type': type, 'style_type': style_type, + 'updated_at': updated_at, 'access_rights': {'read': [x for x in can_read_users.split(',')], 'write': [x for x in can_write_users.split(',')]} } - for id_publication, workspace_name, type, publication_name, title, uuid, style_type, can_read_users, can_write_users + for id_publication, workspace_name, type, publication_name, title, uuid, style_type, updated_at, can_read_users, can_write_users in values} return infos diff --git a/src/layman/common/rest.py b/src/layman/common/rest.py index 33ae45276..8abf82789 100644 --- a/src/layman/common/rest.py +++ b/src/layman/common/rest.py @@ -144,6 +144,7 @@ def get_publications(publication_type, user, request_args): 'url': layman_util.get_workspace_publication_url(publication_type, workspace, name), 'uuid': info["uuid"], 'access_rights': info['access_rights'], + 'updated_at': info['updated_at'].isoformat(), } for (workspace, _, name), info in publication_infos_whole.items() ] diff --git a/src/layman/common/util.py b/src/layman/common/util.py index 20c197cd7..47fefa0c5 100644 --- a/src/layman/common/util.py +++ b/src/layman/common/util.py @@ -20,4 +20,5 @@ def clear_publication_info(info): del info[key] except KeyError: pass + info['updated_at'] = info['updated_at'].isoformat() return info diff --git a/src/layman/layer/rest_workspace_layers.py b/src/layman/layer/rest_workspace_layers.py index 93435afea..d2cef1ccf 100644 --- a/src/layman/layer/rest_workspace_layers.py +++ b/src/layman/layer/rest_workspace_layers.py @@ -41,6 +41,7 @@ def get(username): 'title': info.get("title", None), 'url': url_for('rest_workspace_layer.get', layername=name, username=username), 'uuid': info["uuid"], + 'updated_at': info['updated_at'].isoformat(), 'access_rights': info['access_rights'], } for (workspace, publication_type, name), info in layer_infos_whole.items() diff --git a/src/layman/map/rest_workspace_maps.py b/src/layman/map/rest_workspace_maps.py index 8e0ea6759..7752150a0 100644 --- a/src/layman/map/rest_workspace_maps.py +++ b/src/layman/map/rest_workspace_maps.py @@ -44,6 +44,7 @@ def get(username): 'title': info.get("title", None), 'url': url_for('rest_workspace_map.get', mapname=name, username=username), 'uuid': info['uuid'], + 'updated_at': info['updated_at'].isoformat(), 'access_rights': info['access_rights'], } for (workspace, publication_type, name), info in mapinfos_whole.items() diff --git a/src/layman/rest_publication_test.py b/src/layman/rest_publication_test.py index bec105070..03b2de021 100644 --- a/src/layman/rest_publication_test.py +++ b/src/layman/rest_publication_test.py @@ -1,5 +1,6 @@ import pytest import datetime +from dateutil.parser import parse from test import process_client from layman import LaymanError, settings, app @@ -133,8 +134,13 @@ def test_updated_at(publication_type): with app.app_context(): results = db_util.run_query(query, (workspace, publication_type, publication)) assert len(results) == 1 and len(results[0]) == 1, results - updated_at = results[0][0] - assert timestamp1 < updated_at < timestamp2 + updated_at_db = results[0][0] + assert timestamp1 < updated_at_db < timestamp2 + + info = process_client.get_workspace_publication(publication_type, workspace, publication) + updated_at_rest_str = info['updated_at'] + updated_at_rest = parse(updated_at_rest_str) + assert timestamp1 < updated_at_rest < timestamp2 timestamp3 = datetime.datetime.now(datetime.timezone.utc) process_client.patch_workspace_publication(publication_type, workspace, publication, title='Title') @@ -143,6 +149,12 @@ def test_updated_at(publication_type): with app.app_context(): results = db_util.run_query(query, (workspace, publication_type, publication)) assert len(results) == 1 and len(results[0]) == 1, results - updated_at = results[0][0] - assert timestamp3 < updated_at < timestamp4 + updated_at_db = results[0][0] + assert timestamp3 < updated_at_db < timestamp4 + + info = process_client.get_workspace_publication(publication_type, workspace, publication) + updated_at_rest_str = info['updated_at'] + updated_at_rest = parse(updated_at_rest_str) + assert timestamp3 < updated_at_rest < timestamp4 + process_client.delete_workspace_publication(publication_type, workspace, publication) diff --git a/src/layman/util_test.py b/src/layman/util_test.py index c0644a97a..b3682f256 100644 --- a/src/layman/util_test.py +++ b/src/layman/util_test.py @@ -233,6 +233,8 @@ def test_get_publication_infos(publication_type): for publication_name in publication_infos: if publication_infos[publication_name].get('id'): del publication_infos[publication_name]['id'] + if publication_infos[publication_name].get('updated_at'): + del publication_infos[publication_name]['updated_at'] assert publication_infos == expected_result, (publication_infos, expected_result) process_client.delete_workspace_publication(publication_type, workspace, publication)