diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a1e0fe0f..3f5efc5db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - [#1126](https://github.com/LayerManager/layman/issues/1126) Endpoint [Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#workspace-layer) (with methods GET, PATCH, DELETE) was removed and replaced with endpoint [Layer](doc/rest.md#layer) (with methods GET, PATCH, DELETE) to use UUID-based URL `/rest/layers/{uuid}` instead of workspace&name-based URL. - [#1126](https://github.com/LayerManager/layman/issues/1126) Endpoint [Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#workspace-layers) (with methods GET, POST, DELETE) was unified into endpoint [Layers](doc/rest.md#get-layers); `workspace` is now supplied via request query/body parameter instead of URL path. - [#1126](https://github.com/LayerManager/layman/issues/1126) Endpoint [Workspace Layer Chunk](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#workspace-layer-chunk) (with methods GET, POST) was removed and replaced with endpoint [Layer Chunk](doc/rest.md#layer-chunk) to use UUID-based URL `/rest/layers/{uuid}/chunk` instead of workspace&name-based URL. +- [#1126](https://github.com/LayerManager/layman/issues/1126) Endpoint [Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#workspace-maps) (with methods GET, POST, DELETE) was unified into endpoint [Maps](doc/rest.md#get-maps); `workspace` is now supplied via request query/body parameter instead of URL path. ## v2.1.0 2025-05-02 @@ -78,15 +79,15 @@ - Metadata properties [wfs_url](doc/metadata.md#wfs_url) and [wms_url](doc/metadata.md#wms_url) contain new layer names `l_`. - Layer in map JSON file is considered [internal](doc/models.md#internal-map-layer) if named `l_` and located in GeoServer workspace `layman` or `layman_wms`. - [#1048](https://github.com/LayerManager/layman/issues/1048) New keys `wfs.name` and `wms.name` were added to [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer) response. Use values from these keys when communicating with WFS and WMS. -- [#1048](https://github.com/LayerManager/layman/issues/1048) Actions [POST Workspace Maps](doc/rest.md#post-workspace-maps) and [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) raises error if there is internal layer referenced by its Layman name and Layman workspace instead of new GeoServer name `l_` and new GeoServer workspace `layman` or `layman_wms`. +- [#1048](https://github.com/LayerManager/layman/issues/1048) Actions [POST Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-maps) and [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) raises error if there is internal layer referenced by its Layman name and Layman workspace instead of new GeoServer name `l_` and new GeoServer workspace `layman` or `layman_wms`. - [#1048](https://github.com/LayerManager/layman/issues/1048) Layers with QML style are named by their title instead of name in WMS graphical legend. - [#1048](https://github.com/LayerManager/layman/issues/1048) Keys `file.paths`, `file.path` and `thumbnail.path` of GET Workspace [Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer)/[Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map) are relative to [LAYMAN_DATA_DIR](doc/env-settings.md#layman_data_dir) instead of workspace directory. - [#1048](https://github.com/LayerManager/layman/issues/1048) Stop saving publication UUID to `uuid.txt` file. - [#1048](https://github.com/LayerManager/layman/issues/1048) Information about layer in WMS and WFS (e.g. keys `wms` and `wfs` in [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer)) is obtained from GeoServer REST API instead of WMS GetCapabilities to improve speed. -- [#1048](https://github.com/LayerManager/layman/issues/1048) POST Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers)/[Maps](doc/rest.md#post-workspace-maps) accepts new optional body parameter `uuid`. It's meant mostly for testing purposes. +- [#1048](https://github.com/LayerManager/layman/issues/1048) POST Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers)/[Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-maps) accepts new optional body parameter `uuid`. It's meant mostly for testing purposes. - [#161](https://github.com/LayerManager/layman/issues/161) New method [DELETE User](doc/rest.md#delete-user) allows users to delete user accounts. - [#942](https://github.com/LayerManager/layman/issues/942) New key `used_in_maps` was added to responses of requests [GET Publications](doc/rest.md#get-publications), [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), and [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer). It can be used to warn user before deleting layer that the layer is used in some maps. -- [#1009](https://github.com/LayerManager/layman/issues/1009) PATCH Workspace [Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer)/[Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) returns same response as POST Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers)/[Maps](doc/rest.md#post-workspace-maps) with only `name`, `uuid`, `url` and for Layer also optional `files_to_upload` keys. +- [#1009](https://github.com/LayerManager/layman/issues/1009) PATCH Workspace [Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer)/[Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) returns same response as POST Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers)/[Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-maps) with only `name`, `uuid`, `url` and for Layer also optional `files_to_upload` keys. - [#1009](https://github.com/LayerManager/layman/issues/1009) Updating Micka record as part of PATCH Workspace [Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer) runs asynchronously to make PATCH request faster. - [#909](https://github.com/LayerManager/layman/issues/909) Upgrade QGIS Server from v3.32.2 to v3.40.4. Also use docker hub repo [layermanager/qgis-server](https://hub.docker.com/r/layermanager/qgis-server) instead of jirikcz/qgis-server, - QML styles up to v3.40.2 are supported. @@ -183,21 +184,21 @@ - [#165](https://github.com/LayerManager/layman/issues/165) Roles (except of `EVERYONE`) are managed by [role service](doc/security.md#role-service). - [#165](https://github.com/LayerManager/layman/issues/165) New REST endpoint [GET Roles](doc/rest.md#get-roles) with list of all roles registered in [role service](doc/security.md#role-service), that can be used in access rights. - This new endpoint was added to Test Client into tab "Others". -- [#165](https://github.com/LayerManager/layman/issues/165) POST Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers)/[Maps](doc/rest.md#post-workspace-maps) and PATCH Workspace [Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer)/[Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) saves [role names](doc/models.md#role) mentioned in `access_rights.read` and `access_rights.write` parameters into [prime DB schema](doc/data-storage.md#postgresql). +- [#165](https://github.com/LayerManager/layman/issues/165) POST Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers)/[Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-maps) and PATCH Workspace [Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer)/[Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) saves [role names](doc/models.md#role) mentioned in `access_rights.read` and `access_rights.write` parameters into [prime DB schema](doc/data-storage.md#postgresql). - [#165](https://github.com/LayerManager/layman/issues/165) Many requests respect roles in access rights: - [GET](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer)/[PATCH](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer)/[DELETE](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layer) Workspace Layer - GET Workspace Layer [Thumbnail](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer-thumbnail)/[Style](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer-style)/[Metadata Comparison](doc/rest.md#get-workspace-layer-metadata-comparison)/[Chunk](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer-chunk) - [GET](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map)/[PATCH](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map)/[DELETE](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-map) Workspace Map - GET Workspace Map [File](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map-file)/[Thumbnail](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map-thumbnail)/[Metadata Comparison](doc/rest.md#get-workspace-map-metadata-comparison) - - GET Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers)/[Maps](doc/rest.md#get-workspace-maps) + - GET Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers)/[Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps) - GET [Layers](doc/rest.md#get-layers)/[Maps](doc/rest.md#get-maps)/[Publications](doc/rest.md#get-publications) - - DELETE Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layers)/[Maps](doc/rest.md#delete-workspace-maps) + - DELETE Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layers)/[Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-maps) - requests to [WMS](doc/endpoints.md#web-map-service) and [WFS](doc/endpoints.md#web-feature-service) endpoints -- [#165](https://github.com/LayerManager/layman/issues/165) POST Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers)/[Maps](doc/rest.md#post-workspace-maps) respects roles in [GRANT_CREATE_PUBLIC_WORKSPACE](doc/env-settings.md#grant_create_public_workspace) and [GRANT_PUBLISH_IN_PUBLIC_WORKSPACE](doc/env-settings.md#grant_publish_in_public_workspace) +- [#165](https://github.com/LayerManager/layman/issues/165) POST Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers)/[Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-maps) respects roles in [GRANT_CREATE_PUBLIC_WORKSPACE](doc/env-settings.md#grant_create_public_workspace) and [GRANT_PUBLISH_IN_PUBLIC_WORKSPACE](doc/env-settings.md#grant_publish_in_public_workspace) - [#165](https://github.com/LayerManager/layman/issues/165) Many endpoints return previously associated [role names](doc/models.md#role) in `access_rights.read` and `access_rights.write` keys: - [GET](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer)/[PATCH](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer) Workspace Layer - [GET](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map)/[PATCH](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) Workspace Map - - GET Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers)/[Maps](doc/rest.md#get-workspace-maps) + - GET Workspace [Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers)/[Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps) - GET [Layers](doc/rest.md#get-layers)/[Maps](doc/rest.md#get-maps)/[Publications](doc/rest.md#get-publications) - [#165](https://github.com/LayerManager/layman/issues/165) Name of [users](doc/models.md#username) and [public workspaces](doc/models.md#public-workspace) are from now on restricted to a maximum length of 59 characters. - [940](https://github.com/LayerManager/layman/issues/940) Use `userId` as OAuth2 "sub" instead of `username`. This is suitable for Wagtail. @@ -280,16 +281,16 @@ - [#868](https://github.com/LayerManager/layman/issues/868) Fill table `map_layer` with relations between maps and [internal layers](doc/models.md#internal-map-layer) (layers published on this Layman instance). Relations to [external layers](doc/models.md#internal-map-layer) (layers of other servers) are not imported into the table. ### Changes - [#868](https://github.com/LayerManager/layman/issues/868) Responses to many requests respect [HTTP X-Forwarded headers](doc/client-proxy.md#x-forwarded-http-headers) of the request. Those requests are: - - GET [Publications](doc/rest.md#get-publications), [Layers](doc/rest.md#get-layers), [Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [Maps](doc/rest.md#get-maps), and [Workspace Maps](doc/rest.md#get-workspace-maps) + - GET [Publications](doc/rest.md#get-publications), [Layers](doc/rest.md#get-layers), [Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [Maps](doc/rest.md#get-maps), and [Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps) - [GET](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer), [PATCH](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer), and [DELETE](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layer) Workspace Layer - [GET](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map), [PATCH](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map), and [DELETE](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-map) Workspace Map - [GET Workspace Map File](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map-file) - [POST](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers) and [DELETE](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layers) Workspace Layers - - [POST](doc/rest.md#post-workspace-maps) and [DELETE](doc/rest.md#delete-workspace-maps) Workspace Maps + - [POST](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-maps) and [DELETE](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-maps) Workspace Maps - requests to [WMS](doc/endpoints.md#web-map-service) and [WFS](doc/endpoints.md#web-feature-service) endpoints - [#868](https://github.com/LayerManager/layman/issues/868) Responses to [GET Workspace Layer Metadata Comparison](doc/rest.md#get-workspace-layer-metadata-comparison) and [GET Workspace Map Metadata Comparison](doc/rest.md#get-workspace-map-metadata-comparison) do not respect [HTTP X-Forwarded headers](doc/client-proxy.md#x-forwarded-http-headers) of the request intentionally, in order to keep URLs in canonical form. -- [#868](https://github.com/LayerManager/layman/issues/868) Relations between map and [internal layers](doc/models.md#internal-map-layer) are updated in `map_layer` table when calling [POST Workspace Maps](doc/rest.md#post-workspace-maps), [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map), [DELETE Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-map), and [DELETE Workspace Maps](doc/rest.md#delete-workspace-maps). -- [#901](https://github.com/LayerManager/layman/issues/901) Endpoints [POST](doc/rest.md#post-workspace-maps) and [PATCH](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) accept map compositions in version `3.x.x`. +- [#868](https://github.com/LayerManager/layman/issues/868) Relations between map and [internal layers](doc/models.md#internal-map-layer) are updated in `map_layer` table when calling [POST Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-maps), [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map), [DELETE Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-map), and [DELETE Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-maps). +- [#901](https://github.com/LayerManager/layman/issues/901) Endpoints [POST](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-maps) and [PATCH](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) accept map compositions in version `3.x.x`. - [#927](https://github.com/LayerManager/layman/issues/927) SLD styles are internally published to GeoServer with [`raw`](https://docs-archive.geoserver.org/2.21.x/en/user/rest/api/styles.html#raw) parameter set to `True`. - [#880](https://github.com/LayerManager/layman/issues/880) Use Docker Compose v2 (`docker compose`) in Makefile without `compatibility` flag and remove `Makefile_docker-compose_v1` file. Docker containers are named according to Docker Compose v2 and may have different name after upgrade. - [#765](https://github.com/LayerManager/layman/issues/765) Stop saving OAuth2 claims in filesystem, use prime DB schema only. @@ -366,7 +367,7 @@ - [#520](https://github.com/LayerManager/layman/issues/520) Set MetadataURL for each layer in WFS and WMS workspace in GeoServer. ### Changes - [#769](https://github.com/LayerManager/layman/issues/769) New request [GET Publications](doc/rest.md#get-publications) was added. It enables querying both [layers](doc/models.md#layer) and [maps](doc/models.md#map) by single request. -- [#769](https://github.com/LayerManager/layman/issues/769) New key `publication_type` was added to responses of requests [GET Publications](doc/rest.md#get-publications), [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Maps](doc/rest.md#get-maps), and [GET Workspace Maps](doc/rest.md#get-workspace-maps). Possible values of the key are `layer` and `map`. +- [#769](https://github.com/LayerManager/layman/issues/769) New key `publication_type` was added to responses of requests [GET Publications](doc/rest.md#get-publications), [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Maps](doc/rest.md#get-maps), and [GET Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps). Possible values of the key are `layer` and `map`. - [#528](https://github.com/LayerManager/layman/issues/528) New key `wfs_wms_status` was added to layer items in responses of requests [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), and [GET Publications](doc/rest.md#get-publications). - [#520](https://github.com/LayerManager/layman/issues/520) New element `MetadataURL` was added for each layer to GetCapabilities response of WFS `2.0.0` and WMS `1.3.0`. The element contains URL of CSW metadata record of the layer. - [#800](https://github.com/LayerManager/layman/issues/800) Requests [POST Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers) and [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer) support new parameter `time_regex_format`. Its value is later accessible in the new subkey `wms`.`time`.`regex_format` in responses of [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer) and [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer) requests. @@ -610,13 +611,13 @@ make client-build - [#64](https://github.com/LayerManager/layman/issues/64) Native CRS of previously uploaded maps is set according their composition file (either `EPSG:3857` or `EPSG:4326`) and their composition file is upgraded to [version 2.0.0](https://raw.githubusercontent.com/hslayers/map-compositions/2.0.0/schema.json). ### Changes - [#64](https://github.com/LayerManager/layman/issues/64) Upgrade GeoServer to 2.15.2, because 2.13.0 had serious problem with transformations of EPSG:5514. -- [#64](https://github.com/LayerManager/layman/issues/64) Responses of [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer), [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer), [GET Maps](doc/rest.md#get-maps), [GET Workspace Maps](doc/rest.md#get-workspace-maps), [GET Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map), [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) contains new attributes +- [#64](https://github.com/LayerManager/layman/issues/64) Responses of [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer), [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer), [GET Maps](doc/rest.md#get-maps), [GET Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps), [GET Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map), [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) contains new attributes - `native_crs` with native CRS in form "EPSG:<code>", e.g. "EPSG:4326" - `native_bounding_box` with coordinates in native CRS [minx, miny, maxx, maxy] - [#64](https://github.com/LayerManager/layman/issues/64) New environment variable [LAYMAN_INPUT_SRS_LIST](doc/env-settings.md#LAYMAN_INPUT_SRS_LIST) - [#64](https://github.com/LayerManager/layman/issues/64) Layman supports import of layers in EPSG:3034, EPSG:3035, EPSG:5514, EPSG:32633, EPSG:32634 and EPSG:3059. - [#64](https://github.com/LayerManager/layman/issues/64) New raster layers are normalized in native CRS. New vector layers are imported into DB also in native CRS. Existing layers (normalized raster files, vector tables in DB) are kept in `EPSG:3857` until they are patched with another file, or deleted. -- [#519](https://github.com/LayerManager/layman/issues/64) Endpoints [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Maps](doc/rest.md#get-maps), [GET Workspace Maps](doc/rest.md#get-workspace-maps) support new query parameters *bbox_filter_crs* and *ordering_bbox_crs*. +- [#519](https://github.com/LayerManager/layman/issues/64) Endpoints [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Maps](doc/rest.md#get-maps), [GET Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps) support new query parameters *bbox_filter_crs* and *ordering_bbox_crs*. - [#64](https://github.com/LayerManager/layman/issues/64) Layer thumbnails are generated in native CRS of the layer. - [#64](https://github.com/LayerManager/layman/issues/64) WMS proxy was added to [WMS endpoint](doc/endpoints.md#web-map-service). In case of some special WMS GetMap requests, it changes requested CRS to fix some GeoServer issues. - [#64](https://github.com/LayerManager/layman/issues/64) For layers in `EPSG:5514` and WFS requests in `CRS:84`, the features may have wrong coordinates by hundreds of meters. For requests in `EPSG:4326`, coordinates are correct. @@ -788,9 +789,9 @@ make timgen-build - bounding box is updated in DB, QGIS file, WMS/WFS capabilities, and CSW metadata record - thumbnail is updated in filesystem and it is accessible using [GET Workspace Layer Thumbnail](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer-thumbnail) - update of thumbnail of each [map](doc/models.md#map) that points to at least one edited layer (thumbnail is updated in filesystem and accessible using [GET Workspace Map Thumbnail](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map-thumbnail)) - These updates run in [asynchronous chain](doc/async-tasks.md). Documentation describes concurrency of WFS-T request and its asynchronous chains with another [WFS-T request](doc/endpoints.md#web-feature-service), [POST Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers), [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer), [DELETE Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layer), [DELETE Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layers), [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map), [DELETE Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-map), and [DELETE Workspace Maps](doc/rest.md#delete-workspace-maps). + These updates run in [asynchronous chain](doc/async-tasks.md). Documentation describes concurrency of WFS-T request and its asynchronous chains with another [WFS-T request](doc/endpoints.md#web-feature-service), [POST Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers), [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer), [DELETE Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layer), [DELETE Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layers), [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map), [DELETE Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-map), and [DELETE Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-maps). - [#159](https://github.com/LayerManager/layman/issues/159) Object `layman_metadata` was added to [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer), [GET Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map), [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer), and [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) responses. Attribute `layman_metadata.publication_status` can be used for watching global state of publication (updating, complete, incomplete). -- [#331](https://github.com/LayerManager/layman/issues/331) Query parameter *full_text_filter* is also used for substring search in endpoints [GET Layers](doc/rest.md#get-layers), [GET Worksapce Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Maps](doc/rest.md#get-maps) and [GET Workspace Maps](doc/rest.md#get-workspace-maps). +- [#331](https://github.com/LayerManager/layman/issues/331) Query parameter *full_text_filter* is also used for substring search in endpoints [GET Layers](doc/rest.md#get-layers), [GET Worksapce Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Maps](doc/rest.md#get-maps) and [GET Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps). - Filesystem directory containing workspaces was renamed from `users` to `workspaces` - [#159](https://github.com/LayerManager/layman/issues/159) Bounding box is sent explicitly to GeoServer for every layer. - [#72](https://github.com/LayerManager/layman/issues/72) Pipenv upgraded to v2020.11.15 @@ -813,8 +814,8 @@ make timgen-build - [#302](https://github.com/LayerManager/layman/issues/302) Add URL parameter `LAYERS` to metadata properties [wms_url](doc/metadata.md#wms_url) and [wfs_url](doc/metadata.md#wfs_url) in existing metadata record of each layer. This non-standard parameter holds name of the layer at given WMS/WFS. - [#257](https://github.com/LayerManager/layman/issues/257) Fill column `bbox` in `publications` table. ### Changes -- [#257](https://github.com/LayerManager/layman/issues/257) Endpoints [GET Layers](doc/rest.md#get-layers), [GET Worksapce Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Maps](doc/rest.md#get-maps) and [GET Workspace Maps](doc/rest.md#get-workspace-maps) can filter, order, and paginate results according to new query parameters. All request parameters, response structure and response headers are described in [GET Layers](doc/rest.md#get-layers) documentation. -- [#257](https://github.com/LayerManager/layman/issues/257) Responses of [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer), [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer), [GET Maps](doc/rest.md#get-maps), [GET Workspace Maps](doc/rest.md#get-workspace-maps), [GET Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map), and [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) contains new attributes +- [#257](https://github.com/LayerManager/layman/issues/257) Endpoints [GET Layers](doc/rest.md#get-layers), [GET Worksapce Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Maps](doc/rest.md#get-maps) and [GET Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps) can filter, order, and paginate results according to new query parameters. All request parameters, response structure and response headers are described in [GET Layers](doc/rest.md#get-layers) documentation. +- [#257](https://github.com/LayerManager/layman/issues/257) Responses of [GET Layers](doc/rest.md#get-layers), [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer), [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer), [GET Maps](doc/rest.md#get-maps), [GET Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps), [GET Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map), and [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) contains new attributes - `updated_at` with date and time of last PATCH/POST request to given publication - `bounding_box` with bounding box coordinates in EPSG:3857 - [#302](https://github.com/LayerManager/layman/issues/302) Metadata properties [wms_url](doc/metadata.md#wms_url) and [wfs_url](doc/metadata.md#wfs_url) contain new URL parameter `LAYERS` whose value is name of the layer. It's non-standard way how to store name of the layer at given WMS/WFS instance within metadata record. @@ -828,7 +829,7 @@ make timgen-build ### Changes - [#273](https://github.com/LayerManager/layman/issues/273) New endpoints [GET Layers](doc/rest.md#get-layers) and [GET Layers](doc/rest.md#get-maps) to query publications in all [workspaces](doc/models.md#workspace). - [#273](https://github.com/LayerManager/layman/issues/273) All Layer(s) and Map(s) endpoints with `` in their URL were renamed to 'Workspace Layer...' and 'Workspace Map' in the documentation. -- [#273](https://github.com/LayerManager/layman/issues/273) Item **workspace** was added to response of [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers) and [GET Workspace Maps](doc/rest.md#get-workspace-maps) +- [#273](https://github.com/LayerManager/layman/issues/273) Item **workspace** was added to response of [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers) and [GET Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps) ## v1.10.1 2021-03-10 @@ -951,8 +952,8 @@ Data manipulations that automatically run at first start of Layman: - Error messages and data, as well as Layman Test Client, also distinguishes workspace and user/username. - Each workspace is now either [personal](doc/models.md#personal-workspace), or [public](doc/models.md#public-workspace). Personal workspace is automatically created when user reserves his username. Creation of and posting new publication to public workspaces is controlled by [GRANT_CREATE_PUBLIC_WORKSPACE](doc/env-settings.md#GRANT_CREATE_PUBLIC_WORKSPACE) and [GRANT_PUBLISH_IN_PUBLIC_WORKSPACE](doc/env-settings.md#GRANT_PUBLISH_IN_PUBLIC_WORKSPACE). - [#28](https://github.com/LayerManager/layman/issues/28) It is possible to control also [read access](doc/security.md#publication-access-rights) to any publication per user. - - New attribute `access_rights` added to [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer), [GET Workspace Maps](doc/rest.md#get-workspace-maps) and [GET Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map) responses. - - New parameters `access_rights.read` and `access_rights.write` added to [POST Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers), [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer), [POST Workspace Maps](doc/rest.md#post-workspace-maps) and [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) requests. These new parameters are added to Test Client GUI. + - New attribute `access_rights` added to [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers), [GET Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer), [GET Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps) and [GET Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map) responses. + - New parameters `access_rights.read` and `access_rights.write` added to [POST Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-layers), [PATCH Workspace Layer](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-layer), [POST Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-maps) and [PATCH Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#patch-workspace-map) requests. These new parameters are added to Test Client GUI. - Default values of access rights parameters (both read and write) of newly created publications are set to current authenticated user, or EVERYONE if published by anonymous. - [#28](https://github.com/LayerManager/layman/issues/28) At first start of Layman, access rights of existing publications are set in following way: - [everyone can read and only owner of the workspace can edit](doc/security.md#Authorization) publications in [personal workspaces](doc/models.md#personal-workspace) @@ -961,7 +962,7 @@ Data manipulations that automatically run at first start of Layman: - [#28](https://github.com/LayerManager/layman/issues/28) Only publications with [read access](doc/security.md#publication-access-rights) for EVERYONE are published to Micka as public. - [#28](https://github.com/LayerManager/layman/issues/28) New REST endpoint [GET Users](doc/rest.md#get-users) with list of all users registered in Layman. This new endpoint was added to Test Client into tab "Others". - [#28](https://github.com/LayerManager/layman/issues/28) [WMS endpoint](doc/endpoints.md#web-map-service) accepts same [authentication](doc/security.md#authentication) credentials (e.g. [OAuth2 headers](doc/oauth2/index.md#request-layman-rest-api)) as Layman REST API endpoints. It's implemented using Layman's WFS proxy. This proxy authenticates the user and send user identification to GeoServer. -- [#161](https://github.com/LayerManager/layman/issues/161) New method DELETE was implemented for endpoints [DELETE Workspace Maps](doc/rest.md#delete-workspace-maps) and [DELETE Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layers). +- [#161](https://github.com/LayerManager/layman/issues/161) New method DELETE was implemented for endpoints [DELETE Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-maps) and [DELETE Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#delete-workspace-layers). - [#178](https://github.com/LayerManager/layman/issues/178) New attribute `screen_name` is part of response for [GET Users](doc/rest.md#get-users) and [Get Current User](doc/rest.md#get-current-user). - [#178](https://github.com/LayerManager/layman/issues/178) LifeRay attribute `screen_name` is preferred for creating username in Layman. Previously it was first part of email. - Attribute `groups` is no longer returned in [GET Workspace Map File](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map-file) response. @@ -984,7 +985,7 @@ There is a critical bug in this release, posting new layer breaks Layman: https: ## v1.7.2 2020-11-09 ### Changes -- [#133](https://github.com/LayerManager/layman/issues/133) Attribute `url` of [GET Workspace Maps](doc/rest.md#get-workspace-maps) response was repaired. Previously, it incorrectly used map name instead of username in the URL path. +- [#133](https://github.com/LayerManager/layman/issues/133) Attribute `url` of [GET Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps) response was repaired. Previously, it incorrectly used map name instead of username in the URL path. ## v1.7.1 2020-09-30 @@ -1004,7 +1005,7 @@ There is a critical bug in this release, posting new layer breaks Layman: https: - If you are running Layman with development settings, run also `make client-build`. ### Changes - [#65](https://github.com/LayerManager/layman/issues/65) [WFS endpoint](doc/endpoints.md#web-feature-service) accepts same [authentication](doc/security.md#authentication) credentials (e.g. [OAuth2 headers](doc/oauth2/index.md#request-layman-rest-api)) as Layman REST API endpoints. It's implemented using Layman's WFS proxy. This proxy authenticates the user and send user identification to GeoServer. In combination with changes in v1.6.0, Layman's [`read-everyone-write-owner` authorization](doc/security.md#authorization) (when active) is propagated to GeoServer and user can change only hers layers. -- [#88](https://github.com/LayerManager/layman/issues/88) Attribute **title** was added to REST endpoints [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers) and [GET Workspace Maps](doc/rest.md#get-workspace-maps). +- [#88](https://github.com/LayerManager/layman/issues/88) Attribute **title** was added to REST endpoints [GET Workspace Layers](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layers) and [GET Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-maps). - [#95](https://github.com/LayerManager/layman/issues/95) When calling WFS Transaction, Layman will automatically create missing attributes in DB before redirecting request to GeoServer. Each missing attribute is created as `VARCHAR(1024)`. Works for WFS-T 1.0, 1.1 and 2.0, actions Insert, Update and Replace. If creating attribute fails for any reason, warning is logged and request is redirected nevertheless. - [#96](https://github.com/LayerManager/layman/issues/96) New REST API endpoint [GET Workspace Layer Style](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer-style) is created, which returns Layer default SLD. New attribute ```sld.url``` is added to [GET Workspace Layer endpoint](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer), where URL of Layer default SLD can be obtained. It points to above mentioned [GET Workspace Layer Style](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-layer-style). - [#101](https://github.com/LayerManager/layman/issues/101) Test Client has new page for WFS proxy and is capable to send authenticated queries. @@ -1120,7 +1121,7 @@ There is a critical bug in this release, posting new layer breaks Layman: https: - Add [MICKA_HOSTPORT](doc/env-settings.md#MICKA_HOSTPORT) for demo run. ### Changes -- Publish metadata record of [map](doc/models.md#map) to Micka on [POST Workspace Maps](doc/rest.md#post-workspace-maps). +- Publish metadata record of [map](doc/models.md#map) to Micka on [POST Workspace Maps](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#post-workspace-maps). - Add `metadata` info to [GET Workspace Map](https://github.com/LayerManager/layman/blob/v2.1.0/doc/rest.md#get-workspace-map) response. - Extend `metadata` info with `identitier` attribute in case of both layer and map. - Add documentation of [map metadata properties](doc/metadata.md) diff --git a/doc/async-tasks.md b/doc/async-tasks.md index 29e521fce..8d7e7600c 100644 --- a/doc/async-tasks.md +++ b/doc/async-tasks.md @@ -9,7 +9,7 @@ Asynchronous tasks are started by following requests: - [PATCH Layer](rest.md#patch-layer) - tasks related to patched layer - tasks related to each map that points to patched layer -- [POST Workspace Maps](rest.md#post-workspace-maps) +- [POST Maps](rest.md#post-maps) - tasks related to newly published map - [PATCH Map](rest.md#patch-map) - tasks related to patched map diff --git a/doc/client-proxy.md b/doc/client-proxy.md index b051a6ba2..4082f8b84 100644 --- a/doc/client-proxy.md +++ b/doc/client-proxy.md @@ -49,9 +49,9 @@ then response will change to ``` Currently, value of X-Forwarded headers affects following URLs: -* [GET Publications](rest.md#get-publications), [GET Layers](rest.md#get-layers), [GET Maps](rest.md#get-maps), and [GET Workspace Maps](rest.md#get-workspace-maps) +* [GET Publications](rest.md#get-publications), [GET Layers](rest.md#get-layers), and [GET Maps](rest.md#get-maps) * `url` key -* [POST Layers](rest.md#post-layers) and [POST Workspace Maps](rest.md#post-workspace-maps) +* [POST Layers](rest.md#post-layers) and [POST Maps](rest.md#post-maps) * `url` key * [GET Layer](rest.md#get-layer) and [PATCH Layer](rest.md#patch-layer) * `url` key @@ -72,7 +72,7 @@ Currently, value of X-Forwarded headers affects following URLs: * each `legends` key if its HTTP protocol and netloc corresponds with `url` or `protocol`.`url` * `style` key if its HTTP protocol and netloc corresponds with `url` or `protocol`.`url` * NOTE: If client proxy protocol, host, or URL path prefix was used in URLs in uploaded file, then such values are also replaced with values according to X-Forwarded header values. Default values are used for requests without X-Forwarded headers (protocol is the one from [LAYMAN_CLIENT_PUBLIC_URL](env-settings.md#layman_client_public_url), host is [LAYMAN_PROXY_SERVER_NAME](env-settings.md#layman_proxy_server_name), and path prefix is empty string). -* [DELETE Layers](rest.md#delete-layers), [DELETE Workspace Maps](rest.md#delete-workspace-maps), [DELETE Layer](rest.md#delete-layer) and [DELETE Map](rest.md#delete-map) +* [DELETE Layers](rest.md#delete-layers), [DELETE Maps](rest.md#delete-maps), [DELETE Layer](rest.md#delete-layer) and [DELETE Map](rest.md#delete-map) * `url` key * [OGC endpoints](endpoints.md) * Headers `X-Forwarded-For`, `X-Forwarded-Path`, `Forwarded` and `Host` are ignored diff --git a/doc/data-storage.md b/doc/data-storage.md index 195239980..b7e330afd 100644 --- a/doc/data-storage.md +++ b/doc/data-storage.md @@ -49,7 +49,7 @@ When user [patches existing layer](rest.md#patch-layer), data is saved in the sa ### Maps Information about [maps](models.md#map) includes JSON definition. -When user [publishes new map](rest.md#post-workspace-maps) +When user [publishes new map](rest.md#post-maps) - UUID and name is saved to [Redis](#redis), - UUID, name, title, description and access rights are saved to [PostgreSQL](#postgresql), - JSON file is saved to [filesystem](#filesystem), diff --git a/doc/publish-map.md b/doc/publish-map.md index 65f0fcd24..4492fc737 100644 --- a/doc/publish-map.md +++ b/doc/publish-map.md @@ -12,12 +12,12 @@ In QGIS, you need to implement following steps. First, compose JSON valid against [map-composition schema](https://github.com/hslayers/map-compositions). For Layman, especially `describedBy`, `name`, `title`, `abstract`, `layers`, `projection`, and `extent attributes are important. Each layer must have `className` attribute equal to `HSLayers.Layer.WMS` or `WMS`. -Then save the file to Layman using [POST Workspace Maps](rest.md#post-workspace-maps) endpoint. Well-known [requests](https://requests.readthedocs.io/en/latest/) module can be used for sending HTTP requests. See especially +Then save the file to Layman using [POST Maps](rest.md#post-maps) endpoint. Well-known [requests](https://requests.readthedocs.io/en/latest/) module can be used for sending HTTP requests. See especially - [More complicated POST requests](https://requests.readthedocs.io/en/latest/user/quickstart/#more-complicated-post-requests) - [POST a Multipart-Encoded File](https://requests.readthedocs.io/en/latest/user/quickstart/#post-a-multipart-encoded-file) - [POST Multiple Multipart-Encoded Files](https://requests.readthedocs.io/en/latest/user/advanced/#post-multiple-multipart-encoded-files) -In response of [POST Workspace Maps](rest.md#post-workspace-maps) you will obtain +In response of [POST Maps](rest.md#post-maps) you will obtain - `name` of the map unique within all maps in used [workspace](models.md#workspace) - `url` of the map pointing to [GET Map](rest.md#get-map) @@ -27,7 +27,7 @@ In response of [POST Workspace Maps](rest.md#post-workspace-maps) you will obtai - update the map using [PATCH Map](rest.md#patch-map) - delete the map using [DELETE Map](rest.md#delete-map) - Also, you can obtain list of all maps using [GET Workspace Maps](rest.md#get-workspace-maps). + Also, you can obtain list of all maps using [GET Maps](rest.md#get-maps). ## Maps composed from vector files diff --git a/doc/rest.md b/doc/rest.md index 9b366646d..c0080e4e7 100644 --- a/doc/rest.md +++ b/doc/rest.md @@ -10,12 +10,11 @@ |Layer Style|`/rest/layers//style`|[GET](#get-layer-style)| x | x | x | |Layer Chunk|`/rest/layers//chunk`|[GET](#get-layer-chunk)| [POST](#post-layer-chunk) | x | x | |Workspace Layer Metadata Comparison|`/rest/workspaces//layers//metadata-comparison`|[GET](#get-workspace-layer-metadata-comparison) | x | x | x | -|Maps|`/rest/maps`|[GET](#get-maps)| x | x | x | +|Maps|`/rest/maps`|[GET](#get-maps)| [POST](#post-maps) | x | [DELETE](#delete-maps) | |[Map](models.md#map)|`/rest/maps/`|[GET](#get-map)| x | [PATCH](#patch-map) | [DELETE](#delete-map) | |Map Thumbnail|`/rest/maps//thumbnail`|[GET](#get-map-thumbnail)| x | x | x | |Map File|`/rest/maps//file`|[GET](#get-map-file)| x | x | x | -|Workspace Maps|`/rest/workspaces//maps`|[GET](#get-workspace-maps)| [POST](#post-workspace-maps) | x | [DELETE](#delete-workspace-maps) | -|Workspace Map Metadata Comparison|`/rest/workspaces//layers//metadata-comparison`|[GET](#get-workspace-map-metadata-comparison) | x | x | x | +|Workspace Map Metadata Comparison|`/rest/workspaces//maps//metadata-comparison`|[GET](#get-workspace-map-metadata-comparison) | x | x | x | |Users|`/rest/users`|[GET](#get-users)| x | x | x | |User|`/rest/users/`| x | x | x | [DELETE](#delete-user) | |Current [User](models.md#user)|`/rest/current-user`|[GET](#get-current-user)| x | [PATCH](#patch-current-user) | [DELETE](#delete-current-user) | @@ -533,16 +532,12 @@ Get list of published maps (map compositions). Have the same request parameters and response structure and headers as [GET Publications](#get-publications), except only maps are returned. -## Workspace Maps -### URL -`/rest/workspaces//maps` - -### GET Workspace Maps -Get list of published maps (map compositions). - -Have the same request parameters and response structure and headers as [GET Maps](#get-maps). +Query parameters: +- *workspace*: String, optional + - workspace identifier + - if present, only maps from this workspace are returned -### POST Workspace Maps +### POST Maps Publish new map composition. Accepts JSON valid against [map-composition schema](https://github.com/hslayers/map-compositions) version 2 or 3 used by [Hslayers-ng](https://github.com/hslayers/hslayers-ng). Exact version of schema is defined by `describedBy` key of JSON data file. Processing chain consists of few steps: @@ -563,6 +558,8 @@ Response to this request may be returned sooner than the processing chain is fin Content-Type: `multipart/form-data` Body parameters: +- **workspace**, string `^[a-z][a-z0-9]*(_[a-z0-9]+)*$` + - workspace where the map will be published - *uuid*, string, e.g. `959c95fb-ab54-47a6-9694-402926b8fd29` - map primary key - used if specified, otherwise generated @@ -603,11 +600,13 @@ JSON array of objects representing posted maps with following structure: - **uuid**: String. UUID of the map. - **url**: String. URL of the map. It points to [GET Map](#get-map). -### DELETE Workspace Maps +### DELETE Maps Delete existing maps and all associated sources, including map-composition JSON file and map thumbnail for all maps in the workspace. The currently running [asynchronous tasks](async-tasks.md) of affected maps are aborted. Only maps on which user has [write access right](./security.md#access-to-multi-publication-endpoints) are deleted. #### Request -No action parameters. +Query parameters: +- **workspace**, string `^[a-z][a-z0-9]*(_[a-z0-9]+)*$` + - workspace whose maps will be deleted #### Response Content-Type: `application/json` @@ -674,7 +673,7 @@ JSON object with following structure: - **native_bounding_box**: List of 4 floats. Bounding box coordinates [minx, miny, maxx, maxy] in native CRS. ### PATCH Map -Update information about existing map. First, it deletes sources of the map, and then it publishes them again with new parameters. The processing chain is similar to [POST Workspace Maps](#post-workspace-maps), including [asynchronous tasks](async-tasks.md), +Update information about existing map. First, it deletes sources of the map, and then it publishes them again with new parameters. The processing chain is similar to [POST Maps](#post-maps), including [asynchronous tasks](async-tasks.md), Calling concurrent PATCH requests is not supported, as well as calling PATCH when [POST/PATCH async chain](async-tasks.md) is still running, is not allowed. In such cases, error is returned. @@ -683,7 +682,7 @@ Calling PATCH request when [WFS-T async chain](async-tasks.md) is still running #### Request Content-Type: `multipart/form-data`, `application/x-www-form-urlencoded` -Parameters have same meaning as in case of [POST Workspace Maps](#post-workspace-maps). +Parameters have same meaning as in case of [POST Maps](#post-maps). Body parameters: - *file*, JSON file @@ -702,7 +701,7 @@ Body parameters: #### Response Content-Type: `application/json` -JSON object, same as in case of [POST Workspace Maps](#post-workspace-maps). +JSON object, same as in case of [POST Maps](#post-maps). ### DELETE Map Delete existing map and all associated sources, including map-composition JSON file and map thumbnail. The currently running [asynchronous tasks](async-tasks.md) of affected map are aborted. @@ -727,8 +726,8 @@ Get JSON file describing the map valid against [map-composition schema](https:// Notice that some JSON properties are automatically updated by layman, so file obtained by this endpoint may be slightly different from file that was uploaded. Expected changes: - **name** set to the map's name -- **title** obtained from [POST Workspace Maps](#post-workspace-maps) or [PATCH Map](#patch-map) as `title` -- **abstract** obtained from [POST Workspace Maps](#post-workspace-maps) or [PATCH Map](#patch-map) as `description` +- **title** obtained from [POST Maps](#post-maps) or [PATCH Map](#patch-map) as `title` +- **abstract** obtained from [POST Maps](#post-maps) or [PATCH Map](#patch-map) as `description` - **user** updated on the fly during this request: - **name** set to `` in URL of this endpoint - **email** set to email of the owner, or empty string if not known diff --git a/src/layman/map/__init__.py b/src/layman/map/__init__.py index db4f751c6..dea1d9a7e 100644 --- a/src/layman/map/__init__.py +++ b/src/layman/map/__init__.py @@ -27,7 +27,6 @@ def get_map_patch_keys(): MAP_REST_PATH_NAME = f"{PUBLICATION_TYPE_NAME}s" -from .rest_workspace_maps import bp as workspace_maps_bp from .rest_map_thumbnail import bp as map_thumbnail_bp from .rest_map_file import bp as map_file_bp from .rest_workspace_map_metadata_comparison import bp as workspace_map_metadata_comparison_bp @@ -41,7 +40,6 @@ def get_map_patch_keys(): 'name': PUBLICATION_TYPE_NAME, 'rest_path_name': MAP_REST_PATH_NAME, 'workspace_blueprints': [ - workspace_maps_bp, workspace_map_metadata_comparison_bp, ], 'blueprints': [ diff --git a/src/layman/map/micka/csw_test.py b/src/layman/map/micka/csw_test.py index ecec6cb13..decdd66f9 100644 --- a/src/layman/map/micka/csw_test.py +++ b/src/layman/map/micka/csw_test.py @@ -57,7 +57,7 @@ def provide_map(client): with app.app_context(): workspace = TEST_WORKSPACE mapname = TEST_MAP - rest_path = url_for('rest_workspace_maps.post', workspace=workspace) + rest_path = url_for('rest_maps.post') file_paths = [ 'sample/layman.map/full.json', ] @@ -66,6 +66,7 @@ def provide_map(client): with ExitStack() as stack: files = [(stack.enter_context(open(fp, 'rb')), os.path.basename(fp)) for fp in file_paths] response = client.post(rest_path, data={ + 'workspace': workspace, 'file': files, 'name': mapname, }) diff --git a/src/layman/map/rest_maps.py b/src/layman/map/rest_maps.py index 36dbf8b1c..d92ea5635 100644 --- a/src/layman/map/rest_maps.py +++ b/src/layman/map/rest_maps.py @@ -1,10 +1,19 @@ -from flask import Blueprint, g, request, current_app as app +import json +import io -from layman import util as layman_util +from flask import Blueprint, jsonify, request, g +from flask import current_app as app +from werkzeug.datastructures import FileStorage + +from layman.http import LaymanError +from layman import authn, util as layman_util, uuid from layman.authn import authenticate, get_authn_username -from layman.authz import authorize_publications_decorator -from layman.common import rest as rest_common -from . import MAP_TYPE, MAP_REST_PATH_NAME +from layman.authz import authorize_publications_decorator, authorize +from layman.common import redis as redis_util, rest as rest_common +from layman.util import url_for +from layman.uuid import register_publication_uuid_to_redis +from . import util, MAP_TYPE, MAP_REST_PATH_NAME +from .filesystem import input_file bp = Blueprint('rest_maps', __name__) @@ -22,4 +31,129 @@ def get(): actor = get_authn_username() x_forwarded_items = layman_util.get_x_forwarded_items(request.headers) - return rest_common.get_publications(MAP_TYPE, actor, request_args=request.args, x_forwarded_items=x_forwarded_items) + workspace = layman_util.get_workspace_from_request(request.args, required=False) + if workspace: + authorize(workspace, MAP_TYPE, None, request.method, actor) + return rest_common.get_publications( + MAP_TYPE, + actor, + request_args=request.args, + workspace=workspace, + x_forwarded_items=x_forwarded_items, + ) + + +@bp.route(f"/{MAP_REST_PATH_NAME}", methods=['POST']) +def post(): + app.logger.info(f"POST Maps, actor={g.user}") + x_forwarded_items = layman_util.get_x_forwarded_items(request.headers) + + actor_name = authn.get_authn_username() + workspace = layman_util.get_workspace_from_request(request.form, required=True) + authorize(workspace, MAP_TYPE, None, request.method, actor_name) + + # UUID + input_uuid = request.form.get('uuid') + input_uuid = input_uuid if input_uuid else None + uuid.check_input_uuid(input_uuid) + + # FILE + if 'file' in request.files and not request.files['file'].filename == '': + file = request.files["file"] + else: + raise LaymanError(1, {'parameter': 'file'}) + file_json = util.check_file(file, x_forwarded_items=x_forwarded_items) + + # NAME + unsafe_mapname = request.form.get('name', '') + if len(unsafe_mapname) == 0: + unsafe_mapname = input_file.get_unsafe_mapname(file_json) + mapname = util.to_safe_map_name(unsafe_mapname) + util.check_mapname(mapname) + info = layman_util.get_publication_info(workspace, MAP_TYPE, mapname) + if info: + raise LaymanError(24, {'mapname': mapname}) + + # TITLE + if len(request.form.get('title', '')) > 0: + title = request.form['title'] + elif len(file_json.get('title', '')) > 0: + title = file_json['title'] + else: + title = mapname + + # DESCRIPTION + if len(request.form.get('description', '')) > 0: + description = request.form['description'] + else: + description = file_json.get('abstract', '') + + redis_util.create_lock(workspace, MAP_TYPE, mapname, request.method) + + try: + map_result = { + 'name': mapname, + } + + kwargs = { + 'title': title, + 'description': description, + 'actor_name': actor_name, + 'x_forwarded_headers': x_forwarded_items.headers, + } + + rest_common.setup_post_access_rights(request.form, kwargs, actor_name) + util.pre_publication_action_check(workspace, + mapname, + kwargs, + ) + # register map uuid + uuid_str = register_publication_uuid_to_redis(workspace, MAP_TYPE, mapname, input_uuid) + kwargs['uuid'] = uuid_str + + map_result.update({ + 'uuid': uuid_str, + 'url': url_for('rest_map.get', uuid=uuid_str, x_forwarded_items=x_forwarded_items), + }) + + file = FileStorage( + io.BytesIO(json.dumps(file_json).encode()), + file.filename + ) + input_file.save_map_files(uuid_str, [file]) + + util.post_map( + workspace, + mapname, + kwargs, + 'layman.map.filesystem.input_file' + ) + except Exception as exception: + try: + if util.is_map_chain_ready(workspace, mapname): + redis_util.unlock_publication(workspace, MAP_TYPE, mapname) + finally: + redis_util.unlock_publication(workspace, MAP_TYPE, mapname) + raise exception + + # app.logger.info('uploaded map '+mapname) + return jsonify([map_result]), 200 + + +@bp.route(f"/{MAP_REST_PATH_NAME}", methods=['DELETE']) +def delete(): + app.logger.info(f"DELETE Maps, actor={g.user}") + + actor_name = authn.get_authn_username() + workspace = layman_util.get_workspace_from_request(request.args, required=True) + authorize(workspace, MAP_TYPE, None, request.method, actor_name) + + x_forwarded_items = layman_util.get_x_forwarded_items(request.headers) + infos = layman_util.delete_publications( + workspace, + MAP_TYPE, + request.method, + x_forwarded_items=x_forwarded_items, + ) + + return infos, 200 diff --git a/src/layman/map/rest_workspace_maps.py b/src/layman/map/rest_workspace_maps.py deleted file mode 100644 index 87bf5ceba..000000000 --- a/src/layman/map/rest_workspace_maps.py +++ /dev/null @@ -1,143 +0,0 @@ -import json -import io - -from flask import Blueprint, jsonify, request, current_app as app, g -from werkzeug.datastructures import FileStorage - -from layman.http import LaymanError -from layman.util import check_workspace_name_decorator, url_for -from layman import authn, util as layman_util, uuid -from layman.authn import authenticate, get_authn_username -from layman.authz import authorize_workspace_publications_decorator -from layman.common import redis as redis_util, rest as rest_common -from . import util, MAP_TYPE, MAP_REST_PATH_NAME -from .filesystem import input_file -from ..uuid import register_publication_uuid_to_redis - -bp = Blueprint('rest_workspace_maps', __name__) - - -@bp.before_request -@check_workspace_name_decorator -@authenticate -@authorize_workspace_publications_decorator -def before_request(): - pass - - -@bp.route(f"/{MAP_REST_PATH_NAME}", methods=['GET']) -def get(workspace): - app.logger.info(f"GET Maps, actor={g.user}") - - actor = get_authn_username() - x_forwarded_items = layman_util.get_x_forwarded_items(request.headers) - return rest_common.get_publications(MAP_TYPE, actor, request_args=request.args, workspace=workspace, x_forwarded_items=x_forwarded_items) - - -@bp.route(f"/{MAP_REST_PATH_NAME}", methods=['POST']) -def post(workspace): - app.logger.info(f"POST Maps, actor={g.user}") - x_forwarded_items = layman_util.get_x_forwarded_items(request.headers) - - # UUID - input_uuid = request.form.get('uuid') - input_uuid = input_uuid if input_uuid else None - uuid.check_input_uuid(input_uuid) - - # FILE - if 'file' in request.files and not request.files['file'].filename == '': - file = request.files["file"] - else: - raise LaymanError(1, {'parameter': 'file'}) - file_json = util.check_file(file, x_forwarded_items=x_forwarded_items) - - # NAME - unsafe_mapname = request.form.get('name', '') - if len(unsafe_mapname) == 0: - unsafe_mapname = input_file.get_unsafe_mapname(file_json) - mapname = util.to_safe_map_name(unsafe_mapname) - util.check_mapname(mapname) - info = util.get_map_info(workspace, mapname) - if info: - raise LaymanError(24, {'mapname': mapname}) - - # TITLE - if len(request.form.get('title', '')) > 0: - title = request.form['title'] - elif len(file_json.get('title', '')) > 0: - title = file_json['title'] - else: - title = mapname - - # DESCRIPTION - if len(request.form.get('description', '')) > 0: - description = request.form['description'] - else: - description = file_json.get('abstract', '') - - redis_util.create_lock(workspace, MAP_TYPE, mapname, request.method) - - try: - map_result = { - 'name': mapname, - } - - actor_name = authn.get_authn_username() - - kwargs = { - 'title': title, - 'description': description, - 'actor_name': actor_name, - 'x_forwarded_headers': x_forwarded_items.headers, - } - - rest_common.setup_post_access_rights(request.form, kwargs, actor_name) - util.pre_publication_action_check(workspace, - mapname, - kwargs, - ) - # register map uuid - uuid_str = register_publication_uuid_to_redis(workspace, MAP_TYPE, mapname, input_uuid) - kwargs['uuid'] = uuid_str - - map_result.update({ - 'uuid': uuid_str, - 'url': url_for('rest_map.get', uuid=uuid_str, x_forwarded_items=x_forwarded_items), - }) - - file = FileStorage( - io.BytesIO(json.dumps(file_json).encode()), - file.filename - ) - input_file.save_map_files(uuid_str, [file]) - - util.post_map( - workspace, - mapname, - kwargs, - 'layman.map.filesystem.input_file' - ) - except Exception as exception: - try: - if util.is_map_chain_ready(workspace, mapname): - redis_util.unlock_publication(workspace, MAP_TYPE, mapname) - finally: - redis_util.unlock_publication(workspace, MAP_TYPE, mapname) - raise exception - - # app.logger.info('uploaded map '+mapname) - return jsonify([map_result]), 200 - - -@bp.route(f"/{MAP_REST_PATH_NAME}", methods=['DELETE']) -def delete(workspace): - app.logger.info(f"DELETE Maps, actor={g.user}") - - x_forwarded_items = layman_util.get_x_forwarded_items(request.headers) - infos = layman_util.delete_publications(workspace, - MAP_TYPE, - request.method, - x_forwarded_items=x_forwarded_items, - ) - - return infos, 200 diff --git a/src/layman/map/rest_workspace_maps_test.py b/src/layman/map/rest_workspace_maps_test.py index d85a6ac38..7f9d8c26a 100644 --- a/src/layman/map/rest_workspace_maps_test.py +++ b/src/layman/map/rest_workspace_maps_test.py @@ -3,7 +3,7 @@ import pytest del sys.modules['layman'] -from layman import app, settings +from layman import app, settings, LaymanError from test_tools import process_client from test_tools.util import url_for @@ -23,9 +23,9 @@ def test_get_map_title(): map_uuids.append(map_info['uuid']) with app.app_context(): - url_get = url_for('rest_workspace_maps.get', workspace=workspace) + url_get = url_for('rest_maps.get') # maps.GET - response = requests.get(url_get, timeout=settings.DEFAULT_CONNECTION_TIMEOUT) + response = requests.get(url_get, params={'workspace': workspace}, timeout=settings.DEFAULT_CONNECTION_TIMEOUT) assert response.status_code == 200, response.json() for i in range(0, len(sorted_maps) - 1): @@ -34,3 +34,84 @@ def test_get_map_title(): for uuid in map_uuids: process_client.delete_map(uuid=uuid) + + +@pytest.mark.usefixtures('ensure_layman') +def test_post_maps_requires_workspace(): + with pytest.raises(LaymanError) as exc_info: + process_client.publish_workspace_map( + workspace='', + name='map_requires_workspace', + file_paths=['sample/layman.map/small_map.json'], + ) + assert exc_info.value.http_code == 400 + assert exc_info.value.code == 2 + assert exc_info.value.data['parameter'] == 'workspace' + + +@pytest.mark.usefixtures('ensure_layman') +def test_delete_maps_requires_workspace(): + with pytest.raises(LaymanError) as exc_info: + process_client.delete_workspace_maps(workspace='') + assert exc_info.value.http_code == 400 + assert exc_info.value.code == 2 + assert exc_info.value.data['parameter'] == 'workspace' + + +@pytest.mark.usefixtures('ensure_layman') +def test_get_maps_nonexistent_workspace(): + with pytest.raises(LaymanError) as exc_info: + process_client.get_maps(workspace='workspace_that_does_not_exist_for_maps') + assert exc_info.value.http_code == 404 + assert exc_info.value.code == 40 + + +@pytest.mark.usefixtures('ensure_layman', 'oauth2_provider_mock') +def test_post_maps_to_foreign_personal_workspace(): + workspace_owner = 'test_owner' + workspace_editor = 'test_not_owner' + process_client.reserve_username(workspace_owner, actor_name=workspace_owner) + process_client.reserve_username(workspace_editor, actor_name=workspace_editor) + + with pytest.raises(LaymanError) as exc_info: + process_client.publish_workspace_map( + workspace=workspace_owner, + name='map_in_foreign_workspace', + file_paths=['sample/layman.map/small_map.json'], + actor_name=workspace_editor, + ) + assert exc_info.value.http_code == 403 + assert exc_info.value.code == 30 + + +@pytest.mark.usefixtures('ensure_layman', 'oauth2_provider_mock') +def test_get_maps_without_workspace_filters_by_read_access(): + workspace_owner = 'test_access_rights_application_owner' + workspace_reader = 'test_access_rights_application_reader_by_username' + process_client.reserve_username(workspace_owner, actor_name=workspace_owner) + process_client.reserve_username(workspace_reader, actor_name=workspace_reader) + + private_map_name = 'maps_visibility_private_map' + public_map_name = 'maps_visibility_public_map' + private_map = process_client.publish_workspace_map( + workspace_owner, + private_map_name, + file_paths=['sample/layman.map/small_map.json'], + actor_name=workspace_owner, + access_rights={'read': workspace_owner, 'write': workspace_owner}, + ) + public_map = process_client.publish_workspace_map( + workspace_owner, + public_map_name, + file_paths=['sample/layman.map/small_map.json'], + actor_name=workspace_owner, + access_rights={'read': 'EVERYONE', 'write': workspace_owner}, + ) + + maps_visible_to_reader = process_client.get_maps(actor_name=workspace_reader) + visible_names = {map_info['name'] for map_info in maps_visible_to_reader} + assert public_map_name in visible_names + assert private_map_name not in visible_names + + process_client.delete_map(private_map['uuid'], actor_name=workspace_owner) + process_client.delete_map(public_map['uuid'], actor_name=workspace_owner) diff --git a/src/layman/util_test.py b/src/layman/util_test.py index 86b46982b..71db39f81 100644 --- a/src/layman/util_test.py +++ b/src/layman/util_test.py @@ -112,8 +112,8 @@ def test_publication_interface_methods(): @pytest.mark.parametrize('endpoint, internal, params, expected_url', [ - ('rest_workspace_maps.get', False, {'workspace': 'workspace_name'}, - f'http://localhost:8000/rest/{settings.REST_WORKSPACES_PREFIX}/workspace_name/maps'), + ('rest_maps.get', False, {'workspace': 'workspace_name'}, + 'http://localhost:8000/rest/maps?workspace=workspace_name'), ('rest_layers.get', False, {'workspace': 'workspace_name'}, 'http://localhost:8000/rest/layers?workspace=workspace_name'), ('rest_about.get_version', True, {}, 'http://layman_test_run_1:8000/rest/about/version'), @@ -125,8 +125,8 @@ def test_url_for(endpoint, internal, params, expected_url): @pytest.mark.parametrize('endpoint, internal, x_forwarded_items, params, expected_url', [ - pytest.param('rest_workspace_maps.get', False, None, {'workspace': 'workspace_name'}, - f'http://enjoychallenge.tech/rest/{settings.REST_WORKSPACES_PREFIX}/workspace_name/maps', + pytest.param('rest_maps.get', False, None, {'workspace': 'workspace_name'}, + 'http://enjoychallenge.tech/rest/maps?workspace=workspace_name', id='get-workspace-maps-external'), pytest.param('rest_about.get_version', True, None, {}, 'http://layman:8000/rest/about/version', id='get-version-internal'), diff --git a/test_tools/process_client.py b/test_tools/process_client.py index e4a4fc56a..bac5b8e7e 100644 --- a/test_tools/process_client.py +++ b/test_tools/process_client.py @@ -57,13 +57,13 @@ ]) PUBLICATION_TYPES_DEF = {MAP_TYPE: PublicationTypeDef('mapname', 'rest_maps.get', - 'rest_workspace_maps.post', + 'rest_maps.post', 'rest_map.patch', - 'rest_workspace_maps.get', + 'rest_maps.get', 'rest_map.get', 'rest_map_thumbnail.get', 'rest_map.delete_map', - 'rest_workspace_maps.delete', + 'rest_maps.delete', map_keys_to_check, 'sample/layman.map/small_map.json', 'rest_workspace_map_metadata_comparison.get', @@ -270,10 +270,7 @@ def publish_publication(publication_type, headers.update(get_authz_headers(actor_name)) with app.app_context(): - if publication_type == LAYER_TYPE: - r_url = url_for('rest_layers.post') - else: - r_url = url_for(publication_type_def.post_workspace_publication_url, workspace=workspace) + r_url = url_for(publication_type_def.post_workspace_publication_url) temp_dir = None if compress: @@ -293,9 +290,7 @@ def publish_publication(publication_type, files = [] with ExitStack() as stack: - data = {} - if publication_type == LAYER_TYPE: - data['workspace'] = workspace + data = {'workspace': workspace} if uuid: data["uuid"] = uuid if not do_not_post_name: @@ -348,89 +343,13 @@ def publish_publication(publication_type, return result -def publish_workspace_publication(publication_type, - workspace, - name, - *, - uuid=None, - file_paths=None, - file_path_pattern=None, - external_table_uri=None, - headers=None, - actor_name=None, - access_rights=None, - title=None, - style_file=None, - description=None, - check_response_fn=None, - raise_if_not_complete=True, - with_chunks=False, - compress=False, - compress_settings=None, - crs=None, - map_layers=None, - native_extent=None, - overview_resampling=None, - do_not_upload_chunks=False, - time_regex=None, - time_regex_format=None, - do_not_post_name=False, - do_not_post_title=False, - ): - assert publication_type == MAP_TYPE, \ - f'publish_workspace_publication is map-only wrapper, use publish_publication for {publication_type}' - return publish_publication( - publication_type, - workspace, - name, - uuid=uuid, - file_paths=file_paths, - file_path_pattern=file_path_pattern, - external_table_uri=external_table_uri, - headers=headers, - actor_name=actor_name, - access_rights=access_rights, - title=title, - style_file=style_file, - description=description, - check_response_fn=check_response_fn, - raise_if_not_complete=raise_if_not_complete, - with_chunks=with_chunks, - compress=compress, - compress_settings=compress_settings, - crs=crs, - map_layers=map_layers, - native_extent=native_extent, - overview_resampling=overview_resampling, - do_not_upload_chunks=do_not_upload_chunks, - time_regex=time_regex, - time_regex_format=time_regex_format, - do_not_post_name=do_not_post_name, - do_not_post_title=do_not_post_title, - ) - - -publish_workspace_map = partial(publish_workspace_publication, MAP_TYPE) +publish_workspace_map = partial(publish_publication, MAP_TYPE) publish_workspace_layer = partial(publish_publication, LAYER_TYPE) GET_PUBLICATIONS_KNOWN_PARAMS = {'full_text_filter', 'bbox_filter', 'bbox_filter_crs', 'order_by', 'ordering_bbox', 'ordering_bbox_crs', 'limit', 'offset'} -def get_workspace_publications_response(publication_type, workspace, *, headers=None, query_params=None, ): - query_params = query_params or {} - assert set(query_params.keys()) <= GET_PUBLICATIONS_KNOWN_PARAMS, \ - f"Unknown params: {set(query_params.keys()) - GET_PUBLICATIONS_KNOWN_PARAMS}" - headers = headers or {} - publication_type_def = PUBLICATION_TYPES_DEF[publication_type] - - with app.app_context(): - r_url = url_for(publication_type_def.get_workspace_publications_url, workspace=workspace) - response = requests.get(r_url, headers=headers, params=query_params, timeout=HTTP_TIMEOUT) - raise_layman_error(response) - return response - - def get_publications_response(publication_type, *, workspace=None, headers=None, query_params=None): assert publication_type or not workspace query_params = query_params or {} @@ -440,12 +359,9 @@ def get_publications_response(publication_type, *, workspace=None, headers=None, publication_type_def = PUBLICATION_TYPES_DEF[publication_type] with app.app_context(): - if workspace is not None and publication_type == MAP_TYPE: - r_url = url_for(publication_type_def.get_workspace_publications_url, workspace=workspace) - else: - r_url = url_for(publication_type_def.get_publications_url) + r_url = url_for(publication_type_def.get_publications_url) - if workspace is not None and publication_type == LAYER_TYPE: + if workspace is not None: query_params = { **query_params, 'workspace': workspace, @@ -542,27 +458,17 @@ def delete_publications(publication_type, workspace, headers=None, *, actor_name publication_type_def = PUBLICATION_TYPES_DEF[publication_type] with app.app_context(): - if publication_type == LAYER_TYPE: - r_url = url_for('rest_layers.delete') - response = requests.delete(r_url, headers=headers, params={'workspace': workspace}, timeout=HTTP_TIMEOUT) - raise_layman_error(response) - wfs.clear_cache() - wms.clear_cache() - return response.json() - r_url = url_for(publication_type_def.delete_workspace_publications_url, - workspace=workspace, - ) - return finish_delete(r_url, headers) - - -def delete_workspace_publications(publication_type, workspace, headers=None, *, actor_name=None, ): - assert publication_type == MAP_TYPE, \ - f'delete_workspace_publications is map-only wrapper, use delete_publications for {publication_type}' - return delete_publications(publication_type, workspace, headers=headers, actor_name=actor_name) - - -delete_workspace_maps = partial(delete_workspace_publications, MAP_TYPE) -delete_workspace_layers = partial(delete_workspace_publications, LAYER_TYPE) + r_url = url_for(publication_type_def.delete_workspace_publications_url) + response = requests.delete(r_url, headers=headers, params={'workspace': workspace}, timeout=HTTP_TIMEOUT) + raise_layman_error(response) + if publication_type == LAYER_TYPE: + wfs.clear_cache() + wms.clear_cache() + return response.json() + + +delete_workspace_maps = partial(delete_publications, MAP_TYPE) +delete_workspace_layers = partial(delete_publications, LAYER_TYPE) def assert_workspace_publications(publication_type, workspace, expected_publication_names, headers=None): diff --git a/tests/static_data/data.py b/tests/static_data/data.py index 260de9d2f..4eba95085 100644 --- a/tests/static_data/data.py +++ b/tests/static_data/data.py @@ -72,7 +72,7 @@ def ensure_publication(workspace, publ_type, publication): uuid = None for idx, params in enumerate(data.PUBLICATIONS[(workspace, publ_type, publication)][data.DEFINITION]): if idx == 0: - write_method = process_client.publish_publication if publ_type == process_client.LAYER_TYPE else process_client.publish_workspace_publication + write_method = process_client.publish_publication resp = write_method( publ_type, workspace, @@ -114,7 +114,7 @@ def publish_publications_step(publications_set, step_num): raise_if_not_complete=False, ) else: - write_method = process_client.publish_publication if publ_type == process_client.LAYER_TYPE else process_client.publish_workspace_publication + write_method = process_client.publish_publication resp = write_method(publ_type, workspace, publication, **params, check_response_fn=empty_method_returns_true, raise_if_not_complete=False) uuid = resp['uuid']