diff --git a/CHANGELOG.md b/CHANGELOG.md index e6745d132..0264ca709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - [#154](https://github.com/jirik/layman/issues/154) All workspaces are checked, that their name did not end with '_wms'. With any of the workspaces ended with the suffix, startup process is stopped with error code 45. In that case, please downgrade to the previous minor release version and contact Layman contributors. ### Changes - [#154](https://github.com/jirik/layman/issues/154) [WMS](doc/endpoints.md#web-map-service) is available in dedicated [GeoServer workspace](doc/data-storage.md#geoserver) whose name is composed from Layman's [workspace](doc/models.md#workspace) name and suffix `_wms`. [WFS](doc/endpoints.md#web-feature-service) remains in GeoServer workspace whose name is equal to Layman's workspace name. +- [#154](https://github.com/jirik/layman/issues/154) SLD style published in dedicated WMS GeoServer workspace. - [#99](https://github.com/jirik/layman/issues/99) New endpoint [`/rest/about/version'](doc/rest.md#get-version). Also available in Layman Test Client. - [#154](https://github.com/jirik/layman/issues/154) Workspace name can not end with '_wms'. In such case, error with code 45 is raised. diff --git a/doc/data-storage.md b/doc/data-storage.md index 283423e66..670d0f576 100644 --- a/doc/data-storage.md +++ b/doc/data-storage.md @@ -98,7 +98,7 @@ PostgreSQL is used as persistent data store, so data survives Layman restart. Two **[workspaces](https://docs.geoserver.org/stable/en/user/data/webadmin/workspaces.html)** are created, each with one **[PostgreSQL datastore](https://docs.geoserver.org/latest/en/user/data/app-schema/data-stores.html#postgis)**, for every [workspace](models.md#workspace) (both personal and public). First workspace is meant for [WFS](endpoints.md#web-feature-service) and has the same name as the workspace on Layman. Second workspace is meant for [WMS](endpoints.md#web-map-service) and is suffixed with `_wms`. Name of the datastore is `postgresql` for both workspaces. Every workspace-related information (including PostgreSQL datastore) is saved inside workspace. -**[Feature type](https://docs.geoserver.org/stable/en/user/rest/api/featuretypes.html)** and **[layer](https://docs.geoserver.org/stable/en/user/data/webadmin/layers.html)** are registered in both workspaces and **[style](https://docs.geoserver.org/latest/en/user/styling/webadmin/index.html)** is created in workspace for each layer published on Layman. Name of these three models are the same as layername. Feature type points to appropriate PostgreSQL table through PostgreSQL datastore. Style contains visualization file. +**[Feature type](https://docs.geoserver.org/stable/en/user/rest/api/featuretypes.html)** and **[layer](https://docs.geoserver.org/stable/en/user/data/webadmin/layers.html)** are registered in both workspaces (WMS and WFS), and **[style](https://docs.geoserver.org/latest/en/user/styling/webadmin/index.html)** is created in WMS workspace for each layer published on Layman. Name of these three models are the same as layername. Feature type points to appropriate PostgreSQL table through PostgreSQL datastore. Style contains visualization file. Two **[access rules](https://docs.geoserver.org/stable/en/user/security/layer.html)** are created for each layer in each GeoServer workspace (WFS and WMS), one for [read access right](security.md#publication-access-rights), one for [write access right](security.md#publication-access-rights). Every username from Layman's access right is represented by user's role name (i.e. `USER_`). Role `EVERYONE` is represented as `ROLE_ANONYMOUS` on GeoServer. diff --git a/doc/dependencies.md b/doc/dependencies.md index ce548d325..e9c174671 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -56,6 +56,7 @@ | [autopep8](https://github.com/hhatto/autopep8) | MIT | Pipfile | test | bin | to automatically fix code style | | [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) | MPL | Pipfile | test | bin | to automatically rerun flaky tests | | [pytest-timeout](https://pypi.org/project/pytest-timeout/) | MIT | Pipfile | test | bin | to automatically stop tests after given timeout | +| [pillow](https://github.com/python-pillow/Pillow) | HPND | Pipfile | test | bin | to ensure similarity of images | ### Node.js dependencies | name | license | used by | env | bin or src | purpose | diff --git a/doc/models.md b/doc/models.md index ccbd1d23c..c4bdb52ad 100644 --- a/doc/models.md +++ b/doc/models.md @@ -17,7 +17,7 @@ - [REST API](rest.md): `/rest//layers/` - [filesystem](data-storage.md#filesystem): `/path/to/LAYMAN_DATA_DIR/users//layers/` - [PostgreSQL](data-storage.md#postgresql): `db=LAYMAN_PG_DBNAME, schema=, table=` - - [GeoServer WFS](data-storage.md#geoserver): `/geoserver//ows, layer=, style=` + - [GeoServer WFS](data-storage.md#geoserver): `/geoserver//ows, layer=` - [GeoServer WMS](data-storage.md#geoserver): `/geoserver/_wms/ows, layer=, style=` - or by UUID: - Micka: `/record/basic/m-` diff --git a/docker/Pipfile b/docker/Pipfile index 315e65e61..e44e8faff 100644 --- a/docker/Pipfile +++ b/docker/Pipfile @@ -12,6 +12,7 @@ pylint = "*" autopep8 = "*" pytest-rerunfailures = "*" pytest-timeout = "*" +pillow = "*" [packages] celery = {extras = ["redis"],version = "<5.0.0"} diff --git a/docker/Pipfile.lock b/docker/Pipfile.lock index 44d9d6763..827ec9a73 100644 --- a/docker/Pipfile.lock +++ b/docker/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c3d452ea0716f9b56e09a3042fe49eb3d556b2af38c4b05b703e1637f1c836bd" + "sha256": "8048391457d782acbe02c66daf595db191ffb083b412a60cdc51892203010b3f" }, "pipfile-spec": 6, "requires": { @@ -109,11 +109,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed", - "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450" + "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771", + "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d" ], "markers": "python_version < '3.8'", - "version": "==3.3.0" + "version": "==3.4.0" }, "itsdangerous": { "hashes": [ @@ -124,10 +124,10 @@ }, "jinja2": { "hashes": [ - "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", - "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" + "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", + "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" ], - "version": "==2.11.2" + "version": "==2.11.3" }, "jsonschema": { "hashes": [ @@ -152,8 +152,12 @@ "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", + "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", + "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", @@ -162,34 +166,49 @@ "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", + "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", + "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", + "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", + "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", + "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", + "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" ], "version": "==1.1.1" }, "owslib": { "hashes": [ - "sha256:1876ab0593f813c67260e88da8e92424fb807d596887756e9497272b0f129ce9", - "sha256:408d40b3a6a210bcb3f3609b607960eeedaa63ffd574dde7896906691c354814" + "sha256:23e19ca6b8c6dfe8c9a11387818cd0440d0ccf0c71f0fe4afd210ef63f2c386f", + "sha256:cccfea9bc05fcfad8b06d2d96137978a89f22590343bf87c8bf7c78ee79f06c2" ], "index": "pypi", - "version": "==0.21.0" + "version": "==0.22.0" }, "prometheus-client": { "hashes": [ @@ -290,28 +309,36 @@ }, "pytz": { "hashes": [ - "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4", - "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5" + "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", + "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" ], - "version": "==2020.5" + "version": "==2021.1" }, "pyyaml": { "hashes": [ - "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", - "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", - "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", - "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e", - "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", - "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", - "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", - "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", - "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", - "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a", - "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", - "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", - "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" - ], - "version": "==5.3.1" + "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", + "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", + "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", + "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", + "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", + "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", + "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", + "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", + "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", + "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", + "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", + "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", + "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", + "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", + "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", + "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", + "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", + "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", + "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", + "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", + "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc" + ], + "version": "==5.4.1" }, "redis": { "hashes": [ @@ -409,10 +436,10 @@ }, "urllib3": { "hashes": [ - "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08", - "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" + "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80", + "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73" ], - "version": "==1.26.2" + "version": "==1.26.3" }, "vine": { "hashes": [ @@ -475,11 +502,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed", - "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450" + "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771", + "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d" ], "markers": "python_version < '3.8'", - "version": "==3.3.0" + "version": "==3.4.0" }, "iniconfig": { "hashes": [ @@ -530,10 +557,48 @@ }, "packaging": { "hashes": [ - "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858", - "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093" + "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", + "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" + ], + "version": "==20.9" + }, + "pillow": { + "hashes": [ + "sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6", + "sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded", + "sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865", + "sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174", + "sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032", + "sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a", + "sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e", + "sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378", + "sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17", + "sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c", + "sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913", + "sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7", + "sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0", + "sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820", + "sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba", + "sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2", + "sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b", + "sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9", + "sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234", + "sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d", + "sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5", + "sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206", + "sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9", + "sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8", + "sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59", + "sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d", + "sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7", + "sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a", + "sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0", + "sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b", + "sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d", + "sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae" ], - "version": "==20.8" + "index": "pypi", + "version": "==8.1.0" }, "pluggy": { "hashes": [ @@ -581,11 +646,11 @@ }, "pytest": { "hashes": [ - "sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8", - "sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306" + "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9", + "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839" ], "index": "pypi", - "version": "==6.2.1" + "version": "==6.2.2" }, "pytest-rerunfailures": { "hashes": [ @@ -605,21 +670,29 @@ }, "pyyaml": { "hashes": [ - "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", - "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", - "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", - "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e", - "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", - "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", - "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", - "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", - "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", - "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a", - "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", - "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", - "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" - ], - "version": "==5.3.1" + "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf", + "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696", + "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393", + "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77", + "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922", + "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5", + "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8", + "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10", + "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc", + "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018", + "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e", + "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253", + "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183", + "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb", + "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185", + "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db", + "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46", + "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b", + "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63", + "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df", + "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc" + ], + "version": "==5.4.1" }, "six": { "hashes": [ @@ -708,8 +781,8 @@ }, "wrapt": { "hashes": [ - "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7", - "sha256:d7338d01fe6819103943ccbdeb6e9033c67cd604b187a822dcdfc7d20a664dff" + "sha256:25453bb76a27a5629f544d5d8cded1c4b0462093024aad9f6e89ca9d77b66fcd", + "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7" ], "version": "==1.12.1" }, diff --git a/pytest.ini b/pytest.ini index a4545fcf5..0c1783237 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,3 @@ [pytest] -timeout = 15 +timeout = 20 xfail_strict=true diff --git a/sample/layman.map/internal_url.json b/sample/layman.map/internal_url.json index 6776be2f4..9eedd8e08 100644 --- a/sample/layman.map/internal_url.json +++ b/sample/layman.map/internal_url.json @@ -39,7 +39,7 @@ "title": "Hranice", "className": "HSLayers.Layer.WMS", "singleTile": true, - "url": "http://localhost:8000/geoserver/testuser1/ows", + "url": "http://localhost:8000/geoserver/testuser1_wms/ows", "params": { "LAYERS": "hranice", "FORMAT": "image\/png" @@ -52,7 +52,7 @@ "title": "Mista", "className": "HSLayers.Layer.WMS", "singleTile": true, - "url": "http://localhost:8000/geoserver/testuser1/ows", + "url": "http://localhost:8000/geoserver/testuser1_wms/ows", "params": { "LAYERS": "mista", "FORMAT": "image\/png" diff --git a/sample/layman.map/internal_url_thumbnail.json b/sample/layman.map/internal_url_thumbnail.json new file mode 100644 index 000000000..702adc99a --- /dev/null +++ b/sample/layman.map/internal_url_thumbnail.json @@ -0,0 +1,49 @@ +{ + "abstract": "World places and boundaries abstract", + "title": "World places and boundaries", + "extent": [ + "-35.0", + "-48.5", + "179", + "81.5" + ], + "projection": "epsg:3857", + "layers": [ + { + "metadata": {}, + "visibility": true, + "opacity": 1, + "title": "Staty", + "className": "HSLayers.Layer.WMS", + "singleTile": true, + "url": "http://localhost:8000/geoserver/test_sld_style_applied_in_map_thumbnail_workspace_wms/ows", + "params": { + "LAYERS": "test_sld_style_applied_in_map_thumbnail_layer", + "FORMAT": "image\/png" + } + }, + { + "metadata": {}, + "visibility": true, + "opacity": 1, + "title": "Defini\u010dn\u00ed body administrativn\u00edch celk\u016f", + "className": "HSLayers.Layer.WMS", + "singleTile": true, + "wmsMaxScale": 0, + "legends": [ + "https%3A%2F%2Fgeoportal.kraj-lbc.cz%2Fcgi-bin%2Fmapserv%3Fmap%3D%2Fdata%2Fgis%2FMapServer%2Fprojects%2Fwms%2Fatlas%2Fadministrativni_cleneni.map%26version%3D1.3.0%26service%3DWMS%26request%3DGetLegendGraphic%26sld_version%3D1.1.0%26layer%3Ddefinicni_body_administrativnich_celku%26format%3Dimage%2Fpng%26STYLE%3Ddefault" + ], + "maxResolution": null, + "minResolution": 0, + "url": "https%3A%2F%2Fgeoportal.kraj-lbc.cz%2Fcgi-bin%2Fmapserv%3Fmap%3D%2Fdata%2Fgis%2FMapServer%2Fprojects%2Fwms%2Fatlas%2Fadministrativni_cleneni.map%26", + "params": { + "LAYERS": "definicni_body_administrativnich_celku", + "INFO_FORMAT": "application\/vnd.ogc.gml", + "FORMAT": "image\/png", + "FROMCRS": "EPSG:3857", + "VERSION": "1.3.0" + }, + "dimensions": {} + } + ] +} \ No newline at end of file diff --git a/sample/layman.map/internal_url_unauthorized_layer.json b/sample/layman.map/internal_url_unauthorized_layer.json index d0d89636f..db5afc801 100644 --- a/sample/layman.map/internal_url_unauthorized_layer.json +++ b/sample/layman.map/internal_url_unauthorized_layer.json @@ -39,7 +39,7 @@ "title": "Hranice", "className": "HSLayers.Layer.WMS", "singleTile": true, - "url": "http://localhost:8000/geoserver/test_map_with_unauthorized_layer_user1/ows", + "url": "http://localhost:8000/geoserver/test_map_with_unauthorized_layer_user1_wms/ows", "params": { "LAYERS": "test_map_with_unauthorized_layer_layer1", "FORMAT": "image\/png" @@ -52,7 +52,7 @@ "title": "Mista", "className": "HSLayers.Layer.WMS", "singleTile": true, - "url": "http://localhost:8000/geoserver/test_map_with_unauthorized_layer_user2/ows", + "url": "http://localhost:8000/geoserver/test_map_with_unauthorized_layer_user2_wms/ows", "params": { "LAYERS": "test_map_with_unauthorized_layer_layer2", "FORMAT": "image\/png" diff --git a/sample/style/countries_wms_blue.png b/sample/style/countries_wms_blue.png new file mode 100644 index 000000000..5cd8be791 Binary files /dev/null and b/sample/style/countries_wms_blue.png differ diff --git a/sample/style/test_sld_style_applied_in_map_thumbnail_map.png b/sample/style/test_sld_style_applied_in_map_thumbnail_map.png new file mode 100644 index 000000000..0945606e2 Binary files /dev/null and b/sample/style/test_sld_style_applied_in_map_thumbnail_map.png differ diff --git a/sample/style/test_sld_style_applied_in_thumbnail_layer.png b/sample/style/test_sld_style_applied_in_thumbnail_layer.png new file mode 100644 index 000000000..24324d0f3 Binary files /dev/null and b/sample/style/test_sld_style_applied_in_thumbnail_layer.png differ diff --git a/src/layman/authz/authz_test.py b/src/layman/authz/authz_test.py index c6bf73ebc..e1da12829 100644 --- a/src/layman/authz/authz_test.py +++ b/src/layman/authz/authz_test.py @@ -90,7 +90,6 @@ def has_single_map(r_json): def has_no_publication(r_json): return {li['name'] for li in r_json} == set() - @pytest.mark.timeout(20) @pytest.mark.parametrize( "rest_action, url_for_params, authz_status_code, authz_response, unauthz_status_code, unauthz_response", [ diff --git a/src/layman/common/geoserver/output_srs_list_test.py b/src/layman/common/geoserver/output_srs_list_test.py index 842d7b2f9..74a2571cc 100644 --- a/src/layman/common/geoserver/output_srs_list_test.py +++ b/src/layman/common/geoserver/output_srs_list_test.py @@ -31,7 +31,6 @@ def ensure_layer_internal(workspace, layername, file_paths=None): yield ensure_layer_internal -@pytest.mark.timeout(20) def test_custom_srs_list(ensure_layer): workspace = 'test_custom_srs_list_workspace' layername1 = 'test_custom_srs_list_layer1' diff --git a/src/layman/gs_wfs_proxy_test.py b/src/layman/gs_wfs_proxy_test.py index 5eeb4a8e8..a1e4845cf 100644 --- a/src/layman/gs_wfs_proxy_test.py +++ b/src/layman/gs_wfs_proxy_test.py @@ -12,7 +12,6 @@ from layman.common.geoserver import get_layer_thumbnail, get_layer_square_bbox -@pytest.mark.timeout(20) @pytest.mark.usefixtures('ensure_layman') def test_rest_get(): username = 'wfs_proxy_test' diff --git a/src/layman/layer/filesystem/thumbnail.py b/src/layman/layer/filesystem/thumbnail.py index 8118339f9..e3e8e4813 100644 --- a/src/layman/layer/filesystem/thumbnail.py +++ b/src/layman/layer/filesystem/thumbnail.py @@ -1,15 +1,12 @@ import os import pathlib -import requests -from urllib.parse import urljoin from layman import settings -from flask import current_app from layman import patch_mode from layman.util import url_for -from . import util +from . import util, input_file from layman.common.filesystem import util as common_util -from . import input_file +from ..geoserver import wms as geoserver_wms LAYER_SUBDIR = __name__.split('.')[-1] @@ -65,11 +62,11 @@ def get_layer_thumbnail_path(username, layername): return os.path.join(thumbnail_dir, layername + '.png') -def generate_layer_thumbnail(username, layername): +def generate_layer_thumbnail(workspace, layername): headers = { settings.LAYMAN_GS_AUTHN_HTTP_HEADER_ATTRIBUTE: settings.LAYMAN_GS_USER, } - wms_url = urljoin(settings.LAYMAN_GS_URL, username + '/ows') + wms_url = geoserver_wms.get_wms_url(workspace) from layman.layer.geoserver.util import wms_proxy from layman.common.geoserver import get_layer_thumbnail, get_layer_square_bbox wms = wms_proxy(wms_url, headers=headers) @@ -84,8 +81,8 @@ def generate_layer_thumbnail(username, layername): # format='image/png', # transparent=True, # ) - ensure_layer_thumbnail_dir(username, layername) - tn_path = get_layer_thumbnail_path(username, layername) + ensure_layer_thumbnail_dir(workspace, layername) + tn_path = get_layer_thumbnail_path(workspace, layername) # out = open(tn_path, 'wb') # out.write(tn_img.read()) # out.close() diff --git a/src/layman/layer/filesystem/thumbnail_test.py b/src/layman/layer/filesystem/thumbnail_test.py new file mode 100644 index 000000000..bbc1f8691 --- /dev/null +++ b/src/layman/layer/filesystem/thumbnail_test.py @@ -0,0 +1,34 @@ +import pytest +from layman import app +from . import thumbnail +from test import process_client, util + + +headers_sld = { + 'Accept': 'application/vnd.ogc.sld+xml', + 'Content-type': 'application/xml', +} + + +@pytest.mark.usefixtures('ensure_layman') +def test_sld_style_applied_in_thumbnail(): + workspace = 'test_sld_style_applied_in_thumbnail_workspace' + layer = 'test_sld_style_applied_in_thumbnail_layer' + geojson_file = ['/code/tmp/naturalearth/110m/cultural/ne_110m_admin_0_countries.geojson'] + style_file = 'sample/style/generic-blue.xml' + expected_file = 'sample/style/test_sld_style_applied_in_thumbnail_layer.png' + + process_client.publish_layer(workspace, + layer, + file_paths=geojson_file, + style_file=style_file) + + with app.app_context(): + thumbnail_path = thumbnail.get_layer_thumbnail_path(workspace, layer) + + diffs = util.compare_images(expected_file, thumbnail_path) + + assert diffs < 1000 + + process_client.delete_layer(workspace, + layer) diff --git a/src/layman/layer/geoserver/sld.py b/src/layman/layer/geoserver/sld.py index 1e9641265..ff67ebc43 100644 --- a/src/layman/layer/geoserver/sld.py +++ b/src/layman/layer/geoserver/sld.py @@ -1,17 +1,14 @@ import io import json -import traceback import re from urllib.parse import urljoin import xml.etree.ElementTree as ET import requests -from flask import g, current_app from layman.layer.filesystem.input_sld import get_layer_file from layman.http import LaymanError from layman import settings, patch_mode -from layman.common import util as layman_util from . import headers_json from . import wms from ...util import url_for @@ -24,6 +21,20 @@ } +def get_workspace_style_url(workspace, style=None): + style = style or '' + geoserver_workspace = wms.get_geoserver_workspace(workspace) + return urljoin(settings.LAYMAN_GS_REST_WORKSPACES, + geoserver_workspace + '/styles/' + style) + + +def get_workspace_layer_url(workspace, layer=None): + layer = layer or '' + geoserver_workspace = wms.get_geoserver_workspace(workspace) + return urljoin(settings.LAYMAN_GS_REST_WORKSPACES, + geoserver_workspace + '/layers/' + layer) + + def pre_publication_action_check(username, layername): pass @@ -36,9 +47,8 @@ def patch_layer(username, layername): pass -def delete_layer(username, layername): - style_url = urljoin(settings.LAYMAN_GS_REST_WORKSPACES, - username + '/styles/' + layername) +def delete_layer(workspace, layername): + style_url = get_workspace_style_url(workspace, layername) r = requests.get(style_url + '.sld', auth=settings.LAYMAN_GS_AUTH, timeout=5, @@ -62,7 +72,7 @@ def delete_layer(username, layername): return {} else: r.raise_for_status() - wms.clear_cache(username) + wms.clear_cache(workspace) return { 'sld': { 'file': sld_file @@ -94,8 +104,9 @@ def launder_attribute_name(attr_name): return re.sub(r"['\-#]", '_', attr_name.lower()) -def create_layer_style(username, layername): - sld_file = get_layer_file(username, layername) +def create_layer_style(workspace, layername): + geoserver_workspace = wms.get_geoserver_workspace(workspace) + sld_file = get_layer_file(workspace, layername) # print('create_layer_style', sld_file) if sld_file is None: r = requests.get( @@ -106,7 +117,7 @@ def create_layer_style(username, layername): r.raise_for_status() sld_file = io.BytesIO(r.content) r = requests.post( - urljoin(settings.LAYMAN_GS_REST_WORKSPACES, username + '/styles/'), + get_workspace_style_url(workspace), data=json.dumps( { "style": { @@ -149,8 +160,7 @@ def create_layer_style(username, layername): sld_file.seek(0) r = requests.put( - urljoin(settings.LAYMAN_GS_REST_WORKSPACES, - username + '/styles/' + layername), + get_workspace_style_url(workspace, layername), data=sld_file.read(), headers={ 'Accept': 'application/json', @@ -162,14 +172,13 @@ def create_layer_style(username, layername): if r.status_code == 400: raise LaymanError(14, data=r.text) r.raise_for_status() - r = requests.put(urljoin(settings.LAYMAN_GS_REST_WORKSPACES, - username + '/layers/' + layername), + r = requests.put(get_workspace_layer_url(workspace, layername), data=json.dumps( { "layer": { "defaultStyle": { - "name": username + ':' + layername, - "workspace": username, + "name": geoserver_workspace + ':' + layername, + "workspace": geoserver_workspace, }, } }), @@ -185,10 +194,10 @@ def get_metadata_comparison(username, layername): pass -def get_style_response(username, stylename, headers=None, auth=None): +def get_style_response(workspace, stylename, headers=None, auth=None): if headers is None: headers = headers_sld - url = settings.LAYMAN_GS_REST + f'workspaces/{username}/styles/{stylename}' + url = get_workspace_style_url(workspace, stylename) r = requests.get(url, auth=auth, diff --git a/src/layman/layer/geoserver/sld_test.py b/src/layman/layer/geoserver/sld_test.py new file mode 100644 index 000000000..15e55fbff --- /dev/null +++ b/src/layman/layer/geoserver/sld_test.py @@ -0,0 +1,72 @@ +import os +import pytest +import requests + +from urllib.parse import urljoin + +from layman import settings +from test import process_client, util + + +headers_sld = { + 'Accept': 'application/vnd.ogc.sld+xml', + 'Content-type': 'application/xml', +} + + +@pytest.mark.usefixtures('ensure_layman') +def test_sld_style_in_wms_workspace(): + workspace = 'test_sld_style_file_workspace' + layer = 'test_sld_style_file_layer' + geojson_file = ['/code/tmp/naturalearth/110m/cultural/ne_110m_admin_0_countries.geojson'] + style_file = 'sample/style/generic-blue.xml' + + process_client.publish_layer(workspace, + layer, + file_paths=geojson_file, + style_file=style_file) + + url = urljoin(settings.LAYMAN_GS_REST, f'workspaces/{workspace}_wms/styles/{layer}') + + r = requests.get(url, + auth=settings.LAYMAN_GS_AUTH, + headers=headers_sld, + timeout=5, + ) + r.raise_for_status() + process_client.delete_layer(workspace, + layer) + + +@pytest.mark.usefixtures('ensure_layman') +def test_sld_style_applied_in_wms(): + workspace = 'test_sld_style_wms_workspace' + layer = 'test_sld_style_wms_layer' + geojson_file = ['/code/tmp/naturalearth/110m/cultural/ne_110m_admin_0_countries.geojson'] + style_file = 'sample/style/generic-blue.xml' + expected_file = 'sample/style/countries_wms_blue.png' + obtained_file = 'tmp/artifacts/test_sld_style_applied_in_wms.png' + + process_client.publish_layer(workspace, + layer, + file_paths=geojson_file, + style_file=style_file) + + url = f"http://{settings.LAYMAN_SERVER_NAME}/geoserver/{workspace}_wms/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image/png&TRANSPARENT=true&STYLES=&LAYERS={workspace}:{layer}&SRS=EPSG:3857&WIDTH=768&HEIGHT=752&BBOX=-30022616.05686392,-30569903.32873383,30022616.05686392,28224386.44929134" + + r = requests.get(url, + timeout=5, + stream=True, + ) + r.raise_for_status() + with open(obtained_file, 'wb') as f: + for chunk in r: + f.write(chunk) + + diffs = util.compare_images(expected_file, obtained_file) + + assert diffs < 2000 + + os.remove(obtained_file) + process_client.delete_layer(workspace, + layer) diff --git a/src/layman/layer/rest_test.py b/src/layman/layer/rest_test.py index 19770a7cc..3464ea13f 100644 --- a/src/layman/layer/rest_test.py +++ b/src/layman/layer/rest_test.py @@ -2,7 +2,6 @@ import io import json import os -from multiprocessing import Process import requests import time import xml.etree.ElementTree as ET @@ -25,6 +24,7 @@ from layman.layer.filesystem.thumbnail import get_layer_thumbnail_path from layman import uuid from layman.layer import db +from layman.layer.geoserver import wms as geoserver_wms, sld as geoserver_sld from layman import celery as celery_util from .micka import csw from layman.common.micka import util as micka_common_util @@ -289,7 +289,7 @@ def test_post_layers_simple(client): assert isinstance(layer_info[key_to_check], str) \ or 'status' not in layer_info[key_to_check] - wms_url = urljoin(settings.LAYMAN_GS_URL, username + '/ows') + wms_url = geoserver_wms.get_wms_url(username) wms = wms_proxy(wms_url) assert layername in wms.contents @@ -455,7 +455,7 @@ def test_post_layers_shp(client): flask_client.wait_till_layer_ready(username, layername) # last_task['last'].get() - wms_url = urljoin(settings.LAYMAN_GS_URL, username + '/ows') + wms_url = geoserver_wms.get_wms_url(username) wms = wms_proxy(wms_url) assert 'ne_110m_admin_0_countries_shp' in wms.contents uuid.check_redis_consistency(expected_publ_num_by_type={ @@ -555,12 +555,12 @@ def test_post_layers_complex(client): # last_task['last'].get() assert celery_util.is_task_ready(last_task) - wms_url = urljoin(settings.LAYMAN_GS_URL, username + '/ows') + wms_url = geoserver_wms.get_wms_url(username) wms = wms_proxy(wms_url) assert 'countries' in wms.contents assert wms['countries'].title == 'staty' assert wms['countries'].abstract == 'popis států' - assert wms['countries'].styles[username + ':countries']['title'] == 'Generic Blue' + assert wms['countries'].styles[username + '_wms:countries']['title'] == 'Generic Blue' assert layername != '' rest_path = url_for('rest_layer.get', username=username, layername=layername) @@ -580,8 +580,7 @@ def test_post_layers_complex(client): ]: assert 'status' not in resp_json[source] - style_url = urljoin(settings.LAYMAN_GS_REST_WORKSPACES, - username + '/styles/' + layername) + style_url = geoserver_sld.get_workspace_style_url(username, layername) r = requests.get(style_url + '.sld', auth=settings.LAYMAN_GS_AUTH ) @@ -670,8 +669,7 @@ def test_uppercase_attr(client): ]: assert 'status' not in resp_json[source], f"{source}: {resp_json[source]}" - style_url = urljoin(settings.LAYMAN_GS_REST_WORKSPACES, - username + '/styles/' + layername) + style_url = geoserver_sld.get_workspace_style_url(username, layername) r = requests.get(style_url + '.sld', auth=settings.LAYMAN_GS_AUTH ) @@ -807,12 +805,12 @@ def test_patch_layer_style(client): resp_json = rv.get_json() assert resp_json['title'] == "countries in blue" - wms_url = urljoin(settings.LAYMAN_GS_URL, username + '/ows') + wms_url = geoserver_wms.get_wms_url(username) wms = wms_proxy(wms_url) assert layername in wms.contents assert wms[layername].title == 'countries in blue' assert wms[layername].styles[ - username + ':' + layername]['title'] == 'Generic Blue' + username + '_wms:' + layername]['title'] == 'Generic Blue' uuid.check_redis_consistency(expected_publ_num_by_type={ f'{LAYER_TYPE}': num_layers_before_test + 4 }) @@ -867,22 +865,17 @@ def test_post_layers_sld_1_1_0(client): fp[0].close() layer_info = util.get_layer_info(username, layername) - while 'status' in layer_info['wms'] and layer_info['wms']['status'] in ['PENDING', 'STARTED']: + while ('status' in layer_info['wms'] and layer_info['wms']['status'] in ['PENDING', 'STARTED'])\ + or ('status' in layer_info['sld'] and layer_info['sld']['status'] in ['PENDING', 'STARTED']): time.sleep(0.1) layer_info = util.get_layer_info(username, layername) - wms_url = urljoin(settings.LAYMAN_GS_URL, username + '/ows') + wms_url = geoserver_wms.get_wms_url(username) wms = wms_proxy(wms_url) assert layername in wms.contents assert wms[layername].title == 'countries_sld_1_1_0' - layer_info = util.get_layer_info(username, layername) - while 'status' in layer_info['sld'] and layer_info['sld']['status'] in ['PENDING', 'STARTED']: - time.sleep(0.1) - layer_info = util.get_layer_info(username, layername) - - style_url = urljoin(settings.LAYMAN_GS_REST_WORKSPACES, - username + '/styles/' + layername) + style_url = geoserver_sld.get_workspace_style_url(username, layername) r = requests.get(style_url + '.sld', auth=settings.LAYMAN_GS_AUTH ) diff --git a/src/layman/map/filesystem/thumbnail_test.py b/src/layman/map/filesystem/thumbnail_test.py new file mode 100644 index 000000000..7ed971e43 --- /dev/null +++ b/src/layman/map/filesystem/thumbnail_test.py @@ -0,0 +1,40 @@ +import pytest +from layman import app +from . import thumbnail +from test import process_client, util + + +headers_sld = { + 'Accept': 'application/vnd.ogc.sld+xml', + 'Content-type': 'application/xml', +} + + +@pytest.mark.usefixtures('ensure_layman') +def test_sld_style_applied_in_map_thumbnail(): + workspace = 'test_sld_style_applied_in_map_thumbnail_workspace' + layer = 'test_sld_style_applied_in_map_thumbnail_layer' + map = 'test_sld_style_applied_in_map_thumbnail_map' + geojson_file = ['/code/tmp/naturalearth/110m/cultural/ne_110m_admin_0_countries.geojson'] + map_file = ['sample/layman.map/internal_url_thumbnail.json'] + style_file = 'sample/style/generic-blue.xml' + expected_file = 'sample/style/test_sld_style_applied_in_map_thumbnail_map.png' + + process_client.publish_layer(workspace, + layer, + file_paths=geojson_file, + style_file=style_file) + + process_client.publish_map(workspace, + map, + file_paths=map_file) + + with app.app_context(): + thumbnail_path = thumbnail.get_map_thumbnail_path(workspace, map) + + diffs = util.compare_images(expected_file, thumbnail_path) + + assert diffs < 1000 + + process_client.delete_map(workspace, map) + process_client.delete_layer(workspace, layer) diff --git a/src/layman/map/micka/csw.py b/src/layman/map/micka/csw.py index 4cde6bab3..17422cf77 100644 --- a/src/layman/map/micka/csw.py +++ b/src/layman/map/micka/csw.py @@ -132,14 +132,15 @@ def map_json_to_operates_on(map_json, operates_on_muuids_filter=None, editor=Non unquote_urls(map_json) gs_url = get_gs_proxy_base_url() gs_url = gs_url if gs_url.endswith('/') else f"{gs_url}/" - gs_url_pattern = r'^' + re.escape(gs_url) + r'(' + USERNAME_ONLY_PATTERN + r')' + r'/(?:ows|wms|wfs).*$' + gs_wms_url_pattern = r'^' + re.escape(gs_url) + r'(' + USERNAME_ONLY_PATTERN + r')' + \ + settings.LAYMAN_GS_WMS_WORKSPACE_POSTFIX + r'/(?:ows|wms|wfs).*$' layman_layer_names = [] for map_layer in map_json['layers']: layer_url = map_layer.get('url', None) if not layer_url: continue # print(f"layer_url={layer_url}") - match = re.match(gs_url_pattern, layer_url) + match = re.match(gs_wms_url_pattern, layer_url) if not match: continue layer_username = match.group(1) diff --git a/test/process_client.py b/test/process_client.py index 4b752b064..eaa240366 100644 --- a/test/process_client.py +++ b/test/process_client.py @@ -177,11 +177,15 @@ def publish_publication(publication_type, headers=None, access_rights=None, title=None, + style_file=None, + description=None, ): title = title or name headers = headers or {} publication_type_def = PUBLICATION_TYPES_DEF[publication_type] file_paths = file_paths or [publication_type_def.source_path, ] + if style_file: + assert publication_type == LAYER_TYPE with app.app_context(): r_url = url_for(publication_type_def.post_url, username=username) @@ -198,6 +202,10 @@ def publish_publication(publication_type, data["access_rights.read"] = access_rights['read'] if access_rights and access_rights.get('write'): data["access_rights.write"] = access_rights['write'] + if style_file: + files.append(('sld', (os.path.basename(style_file), open(style_file, 'rb')))) + if description: + data['description'] = description r = requests.post(r_url, files=files, data=data, diff --git a/test/util.py b/test/util.py index 70ce27783..d6952688c 100644 --- a/test/util.py +++ b/test/util.py @@ -1,6 +1,7 @@ import requests import time from requests.exceptions import ConnectionError +from PIL import Image, ImageChops # utils @@ -18,3 +19,21 @@ def wait_for_url(url, max_attempts, sleeping_time): raise e attempt += 1 time.sleep(sleeping_time) + + +def compare_images(image1, image2): + expected_image = Image.open(image1) + current_image = Image.open(image2) + + diff_image = ImageChops.difference(expected_image, current_image) + + diffs = 0 + + for x in range(diff_image.width): + for y in range(diff_image.height): + pixel_diff = diff_image.getpixel((x, y)) + if pixel_diff != (0, 0, 0, 0) and \ + (expected_image.getpixel((x, y))[3] > 0 or current_image.getpixel((x, y))[3] > 0): + diffs += 1 + + return diffs