From 560b725b6ce4085affd716f54e2988ae326925b8 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Mon, 3 Jun 2024 21:36:10 +0000 Subject: [PATCH] Hotfix mapping --- dbrepo-analyse-service/Pipfile.lock | 385 ++++---- dbrepo-analyse-service/determine_dt.py | 3 +- .../lib/dbrepo-1.4.4-py3-none-any.whl | Bin 27369 -> 27387 bytes .../lib/dbrepo-1.4.4.tar.gz | Bin 37614 -> 37662 bytes dbrepo-data-service/Dockerfile | 2 +- .../at/tuwien/endpoints/TableEndpoint.java | 58 +- .../endpoint/TableEndpointUnitTest.java | 56 +- .../tuwien/mvc/PrometheusEndpointMvcTest.java | 9 +- .../java/at/tuwien/mapper/MariaDbMapper.java | 6 +- dbrepo-metadata-service/Dockerfile | 2 +- .../api/database/table/columns/ColumnDto.java | 2 +- .../constraints/foreign/ForeignKeyDto.java | 3 - .../constraints/primary/PrimaryKeyDto.java | 6 +- .../table/constraints/unique/UniqueDto.java | 2 +- .../constraints/foreignKey/ForeignKey.java | 5 +- .../constraints/primaryKey/PrimaryKey.java | 4 +- .../table/constraints/unique/Unique.java | 4 +- dbrepo-metadata-service/pom.xml | 25 +- .../java/at/tuwien/mapper/AccessMapper.java | 14 - .../tuwien/mapper/AuthenticationMapper.java | 19 - .../at/tuwien/mapper/BannerMessageMapper.java | 24 - .../at/tuwien/mapper/ContainerMapper.java | 45 - .../java/at/tuwien/mapper/DataCiteMapper.java | 133 --- .../java/at/tuwien/mapper/DatabaseMapper.java | 62 -- .../java/at/tuwien/mapper/DocumentMapper.java | 15 - .../java/at/tuwien/mapper/ExternalMapper.java | 76 -- .../at/tuwien/mapper/IdentifierMapper.java | 157 ---- .../java/at/tuwien/mapper/ImageMapper.java | 26 - .../java/at/tuwien/mapper/LicenseMapper.java | 12 - .../java/at/tuwien/mapper/MetadataMapper.java | 868 +++++++++++++++++- .../java/at/tuwien/mapper/SemanticMapper.java | 18 - ...{OntologyMapper.java => SparqlMapper.java} | 42 +- .../java/at/tuwien/mapper/TableMapper.java | 213 ----- .../java/at/tuwien/mapper/UserMapper.java | 113 --- .../java/at/tuwien/mapper/ViewMapper.java | 48 - .../at/tuwien/endpoints/AccessEndpoint.java | 6 +- .../at/tuwien/endpoints/ConceptEndpoint.java | 11 +- .../tuwien/endpoints/ContainerEndpoint.java | 14 +- .../at/tuwien/endpoints/DatabaseEndpoint.java | 20 +- .../tuwien/endpoints/IdentifierEndpoint.java | 28 +- .../at/tuwien/endpoints/ImageEndpoint.java | 16 +- .../at/tuwien/endpoints/LicenseEndpoint.java | 10 +- .../at/tuwien/endpoints/MessageEndpoint.java | 18 +- .../at/tuwien/endpoints/OntologyEndpoint.java | 22 +- .../at/tuwien/endpoints/TableEndpoint.java | 18 +- .../at/tuwien/endpoints/UnitEndpoint.java | 11 +- .../at/tuwien/endpoints/UserEndpoint.java | 9 +- .../at/tuwien/endpoints/ViewEndpoint.java | 16 +- .../endpoints/AccessEndpointUnitTest.java | 8 +- .../at/tuwien/mapper/ContainerMapperTest.java | 43 - .../tuwien/mapper/DatabaseMapperUnitTest.java | 64 -- .../mapper/IdentifierMapperUnitTest.java | 84 -- .../tuwien/mapper/MetadataMapperUnitTest.java | 475 ++++++++++ .../at/tuwien/mapper/StoreMapperUnitTest.java | 32 - .../at/tuwien/mapper/TableMapperUnitTest.java | 46 - .../at/tuwien/mapper/UserMapperUnitTest.java | 63 -- .../at/tuwien/mapper/ViewMapperUnitTest.java | 50 - .../DatabaseServicePersistenceTest.java | 3 - .../tuwien/service/TableServiceUnitTest.java | 2 +- .../java/at/tuwien/config/JacksonConfig.java | 2 + .../gateway/impl/KeycloakGatewayImpl.java | 12 +- .../impl/SearchServiceGatewayImpl.java | 53 +- .../service/impl/AccessServiceImpl.java | 12 +- .../impl/AuthenticationServiceImpl.java | 10 +- .../impl/BannerMessageServiceImpl.java | 12 +- .../service/impl/ContainerServiceImpl.java | 14 +- .../impl/DataCiteIdentifierServiceImpl.java | 11 +- .../service/impl/DatabaseServiceImpl.java | 25 +- .../service/impl/EntityServiceImpl.java | 8 +- .../service/impl/IdentifierServiceImpl.java | 38 +- .../tuwien/service/impl/ImageServiceImpl.java | 10 +- .../service/impl/MetadataServiceImpl.java | 28 +- .../service/impl/OntologyServiceImpl.java | 14 +- .../tuwien/service/impl/TableServiceImpl.java | 30 +- .../tuwien/service/impl/ViewServiceImpl.java | 10 +- .../java/at/tuwien/test/AbstractUnitTest.java | 4 + .../main/java/at/tuwien/test/BaseTest.java | 197 +++- dbrepo-search-service/Pipfile.lock | 328 +++---- dbrepo-search-service/app.py | 3 +- dbrepo-search-service/init/database.json | 32 +- .../lib/dbrepo-1.4.4-py3-none-any.whl | Bin 27369 -> 27387 bytes dbrepo-search-service/lib/dbrepo-1.4.4.tar.gz | Bin 37614 -> 37662 bytes .../test/test_opensearch_client.py | 9 +- .../components/database/DatabaseCreate.vue | 6 +- dbrepo-ui/components/dialogs/DropTable.vue | 3 +- dbrepo-ui/components/dialogs/EditAccess.vue | 9 +- dbrepo-ui/components/dialogs/EditTuple.vue | 15 +- dbrepo-ui/components/dialogs/Semantics.vue | 3 +- dbrepo-ui/components/identifier/Creators.vue | 5 +- dbrepo-ui/components/identifier/Persist.vue | 25 +- dbrepo-ui/components/subset/Builder.vue | 19 +- dbrepo-ui/components/subset/Results.vue | 12 +- dbrepo-ui/components/subset/SubsetList.vue | 14 +- dbrepo-ui/components/table/BlobUpload.vue | 5 +- dbrepo-ui/components/table/TableImport.vue | 21 +- dbrepo-ui/components/view/ViewToolbar.vue | 8 +- dbrepo-ui/composables/database-service.ts | 8 +- dbrepo-ui/composables/toast-instance.ts | 25 + dbrepo-ui/locales/de-AT.json | 5 +- dbrepo-ui/locales/en-US.json | 5 +- dbrepo-ui/nuxt.config.ts | 2 +- .../pages/database/[database_id]/settings.vue | 35 +- .../[database_id]/table/[table_id]/data.vue | 30 +- .../[database_id]/table/[table_id]/schema.vue | 5 +- .../database/[database_id]/table/create.vue | 5 +- .../database/[database_id]/table/import.vue | 13 +- dbrepo-ui/pages/login.vue | 13 +- .../semantic/ontology/_ontology_id/index.vue | 3 +- dbrepo-ui/pages/signup.vue | 13 +- dbrepo-ui/pages/user/authentication.vue | 3 +- dbrepo-ui/pages/user/info.vue | 6 +- dbrepo-ui/plugins/backend.ts | 10 - dbrepo-ui/stores/cache.js | 24 +- helm/dbrepo/values.prod.yaml | 514 +++++++++++ lib/python/Makefile | 11 +- lib/python/dbrepo/RestClient.py | 5 +- lib/python/dbrepo/api/dto.py | 21 +- lib/python/tests/test_database.py | 15 +- 118 files changed, 3006 insertions(+), 2298 deletions(-) delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/AccessMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/AuthenticationMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/BannerMessageMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ContainerMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DataCiteMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DatabaseMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DocumentMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ExternalMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/IdentifierMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ImageMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/LicenseMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/SemanticMapper.java rename dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/{OntologyMapper.java => SparqlMapper.java} (66%) delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/UserMapper.java delete mode 100644 dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ViewMapper.java delete mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/ContainerMapperTest.java delete mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/DatabaseMapperUnitTest.java delete mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/IdentifierMapperUnitTest.java create mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/MetadataMapperUnitTest.java delete mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/StoreMapperUnitTest.java delete mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/TableMapperUnitTest.java delete mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/UserMapperUnitTest.java delete mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/ViewMapperUnitTest.java create mode 100644 dbrepo-ui/composables/toast-instance.ts delete mode 100644 dbrepo-ui/plugins/backend.ts create mode 100644 helm/dbrepo/values.prod.yaml diff --git a/dbrepo-analyse-service/Pipfile.lock b/dbrepo-analyse-service/Pipfile.lock index d1f47348f8..8a14b2d86f 100644 --- a/dbrepo-analyse-service/Pipfile.lock +++ b/dbrepo-analyse-service/Pipfile.lock @@ -167,27 +167,28 @@ }, "boto3": { "hashes": [ - "sha256:009cd143509f2ff4c37582c3f45d50f28c95eed68e8a5c36641206bdb597a9ea", - "sha256:7e59f0a848be477a4c98a90e7a18a0e284adfb643f7879d2b303c5f493661b7a" + "sha256:4eb8019421cb664a6fcbbee6152aa95a28ce8bbc1c4ee263871c09cdd58bf8ee", + "sha256:e9edaf979fbe59737e158f2f0f3f0861ff1d61233f18f6be8ebb483905f24587" ], "index": "pypi", - "version": "==1.34.113" + "markers": "python_version >= '3.8'", + "version": "==1.34.118" }, "botocore": { "hashes": [ - "sha256:449912ba3c4ded64f21d09d428146dd9c05337b2a112e15511bf2c4888faae79", - "sha256:8ca87776450ef41dd25c327eb6e504294230a5756940d68bcfdedc4a7cdeca97" + "sha256:0a3d1ec0186f8b516deb39474de3d226d531f77f92a0f56ad79b80219db3ae9e", + "sha256:e3f6c5636a4394768e81e33a16f5c6ae7f364f512415d423f9b9dc67fc638df4" ], "markers": "python_version >= '3.8'", - "version": "==1.34.113" + "version": "==1.34.118" }, "certifi": { "hashes": [ - "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", - "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" + "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516", + "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56" ], "markers": "python_version >= '3.6'", - "version": "==2024.2.2" + "version": "==2024.6.2" }, "cffi": { "hashes": [ @@ -391,10 +392,15 @@ }, "dbrepo": { "hashes": [ - "sha256:ceab260cf76c050e118ce0f0589fec66059396751e03f2ec41fa489cfacc4e7b" + "sha256:110db9e4e70f5656a6351409d4b022656abf7de0bd72d5e061a25685f708d9a4" + ], + "path": "./lib/dbrepo-1.4.4.tar.gz" + }, + "events": { + "hashes": [ + "sha256:a7286af378ba3e46640ac9825156c93bdba7502174dd696090fdfcd4d80a1abd" ], - "path": "./lib/dbrepo-1.4.4.tar.gz", - "version": "==1.4.4" + "version": "==0.5" }, "exceptiongroup": { "hashes": [ @@ -402,6 +408,7 @@ "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==1.2.1" }, "flasgger": { @@ -417,6 +424,7 @@ "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==3.0.3" }, "flask-cors": { @@ -441,6 +449,7 @@ "sha256:9215d05a9413d3855764bcd67035e75819d23af2fafb6b55197eb5a3313fdfb2" ], "index": "pypi", + "markers": "python_version >= '3.7' and python_version < '4'", "version": "==4.6.0" }, "frozenlist": { @@ -571,6 +580,7 @@ "sha256:fbfdce91239fe306772faab57597186710d5699213f4df099d1612da7320d682" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==24.2.1" }, "greenlet": { @@ -635,6 +645,7 @@ "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==3.0.3" }, "gunicorn": { @@ -643,6 +654,7 @@ "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==22.0.0" }, "idna": { @@ -698,6 +710,7 @@ "sha256:61c9170f92e736b530655e75374681d4fcca9cfa8763ab42be57353b2b203494" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==1.3.1" }, "markupsafe": { @@ -918,15 +931,17 @@ "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f" ], "index": "pypi", + "markers": "python_version >= '3.9'", "version": "==1.26.4" }, "opensearch-py": { "hashes": [ - "sha256:0dde4ac7158a717d92a8cd81964cb99705a4b80bcf9258ba195b9a9f23f5226d", - "sha256:cf093a40e272b60663f20417fc1264ac724dcf1e03c1a4542a6b44835b1e6c49" + "sha256:0b7c27e8ed84c03c99558406927b6161f186a72502ca6d0325413d8e5523ba96", + "sha256:b6e78b685dd4e9c016d7a4299cf1de69e299c88322e3f81c716e6e23fe5683c1" ], "index": "pypi", - "version": "==2.5.0" + "markers": "python_version >= '3.8' and python_version < '4'", + "version": "==2.6.0" }, "packaging": { "hashes": [ @@ -969,6 +984,7 @@ "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad" ], "index": "pypi", + "markers": "python_version >= '3.9'", "version": "==2.2.2" }, "pika": { @@ -1043,96 +1059,97 @@ }, "pydantic": { "hashes": [ - "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5", - "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc" + "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e", + "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4" ], "index": "pypi", - "version": "==2.7.1" + "markers": "python_version >= '3.8'", + "version": "==2.7.3" }, "pydantic-core": { "hashes": [ - "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b", - "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a", - "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90", - "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d", - "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e", - "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d", - "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027", - "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804", - "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347", - "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400", - "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3", - "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399", - "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349", - "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd", - "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c", - "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e", - "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413", - "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3", - "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e", - "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3", - "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91", - "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce", - "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c", - "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb", - "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664", - "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6", - "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd", - "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3", - "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af", - "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043", - "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350", - "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7", - "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0", - "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563", - "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761", - "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72", - "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3", - "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb", - "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788", - "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b", - "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c", - "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038", - "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250", - "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec", - "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c", - "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74", - "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81", - "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439", - "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75", - "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0", - "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8", - "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150", - "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438", - "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae", - "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857", - "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038", - "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374", - "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f", - "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241", - "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592", - "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4", - "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d", - "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b", - "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b", - "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182", - "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e", - "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641", - "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70", - "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9", - "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a", - "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543", - "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b", - "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f", - "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38", - "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845", - "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2", - "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0", - "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4", - "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242" + "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3", + "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8", + "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8", + "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30", + "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a", + "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8", + "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d", + "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc", + "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2", + "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab", + "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077", + "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e", + "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9", + "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9", + "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef", + "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1", + "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507", + "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528", + "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558", + "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b", + "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154", + "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724", + "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695", + "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9", + "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851", + "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805", + "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a", + "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5", + "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94", + "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c", + "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d", + "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef", + "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26", + "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2", + "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c", + "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0", + "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2", + "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4", + "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d", + "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2", + "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce", + "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34", + "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f", + "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d", + "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b", + "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07", + "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312", + "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057", + "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d", + "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af", + "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb", + "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd", + "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78", + "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b", + "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223", + "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a", + "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4", + "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5", + "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23", + "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a", + "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4", + "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8", + "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d", + "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443", + "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e", + "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f", + "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e", + "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d", + "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc", + "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443", + "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be", + "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2", + "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee", + "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f", + "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae", + "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864", + "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4", + "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951", + "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc" ], "markers": "python_version >= '3.8'", - "version": "==2.18.2" + "version": "==2.18.4" }, "pyjwt": { "hashes": [ @@ -1224,11 +1241,12 @@ }, "requests": { "hashes": [ - "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289", - "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c" + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], "index": "pypi", - "version": "==2.32.2" + "markers": "python_version >= '3.8'", + "version": "==2.32.3" }, "rpds-py": { "hashes": [ @@ -1377,11 +1395,11 @@ }, "typing-extensions": { "hashes": [ - "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8", - "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594" + "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a", + "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1" ], "markers": "python_version >= '3.8'", - "version": "==4.12.0" + "version": "==4.12.1" }, "tzdata": { "hashes": [ @@ -1393,11 +1411,11 @@ }, "urllib3": { "hashes": [ - "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07", - "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0" + "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", + "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.18" + "markers": "python_version >= '3.8'", + "version": "==2.2.1" }, "werkzeug": { "hashes": [ @@ -1592,11 +1610,11 @@ }, "certifi": { "hashes": [ - "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", - "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" + "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516", + "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56" ], "markers": "python_version >= '3.6'", - "version": "==2024.2.2" + "version": "==2024.6.2" }, "cffi": { "hashes": [ @@ -1754,61 +1772,62 @@ }, "coverage": { "hashes": [ - "sha256:06d96b9b19bbe7f049c2be3c4f9e06737ec6d8ef8933c7c3a4c557ef07936e46", - "sha256:13017a63b0e499c59b5ba94a8542fb62864ba3016127d1e4ef30d354fc2b00e9", - "sha256:1acc2e2ef098a1d4bf535758085f508097316d738101a97c3f996bccba963ea5", - "sha256:1aef719b6559b521ae913ddeb38f5048c6d1a3d366865e8b320270b7bc4693c2", - "sha256:1e4225990a87df898e40ca31c9e830c15c2c53b1d33df592bc8ef314d71f0281", - "sha256:1f11f98753800eb1ec872562a398081f6695f91cd01ce39819e36621003ec52a", - "sha256:1f29bf497d51a5077994b265e976d78b09d9d0dff6ca5763dbb4804534a5d380", - "sha256:1f96aa94739593ae0707eda9813ce363a0a0374a810ae0eced383340fc4a1f73", - "sha256:20e611fc36e1a0fc7bbf957ef9c635c8807d71fbe5643e51b2769b3cc0fb0b51", - "sha256:23f2f16958b16152b43a39a5ecf4705757ddd284b3b17a77da3a62aef9c057ef", - "sha256:24bb4c7859a3f757a116521d4d3a8a82befad56ea1bdacd17d6aafd113b0071e", - "sha256:26716a1118c6ce2188283b4b60a898c3be29b480acbd0a91446ced4fe4e780d8", - "sha256:29da75ce20cb0a26d60e22658dd3230713c6c05a3465dd8ad040ffc991aea318", - "sha256:2b144d142ec9987276aeff1326edbc0df8ba4afbd7232f0ca10ad57a115e95b6", - "sha256:2c79f058e7bec26b5295d53b8c39ecb623448c74ccc8378631f5cb5c16a7e02c", - "sha256:3bb5b92a0ab3d22dfdbfe845e2fef92717b067bdf41a5b68c7e3e857c0cff1a4", - "sha256:3d3f7744b8a8079d69af69d512e5abed4fb473057625588ce126088e50d05493", - "sha256:3d9c62cff2ffb4c2a95328488fd7aa96a7a4b34873150650fe76b19c08c9c792", - "sha256:3e12536446ad4527ac8ed91d8a607813085683bcce27af69e3b31cd72b3c5960", - "sha256:40dbb8e7727560fe8ab65efcddfec1ae25f30ef02e2f2e5d78cfb52a66781ec5", - "sha256:431a3917e32223fcdb90b79fe60185864a9109631ebc05f6c5aa03781a00b513", - "sha256:448ec61ea9ea7916d5579939362509145caaecf03161f6f13e366aebb692a631", - "sha256:482df956b055d3009d10fce81af6ffab28215d7ed6ad4a15e5c8e67cb7c5251c", - "sha256:4a00bd5ba8f1a4114720bef283cf31583d6cb1c510ce890a6da6c4268f0070b7", - "sha256:51b6cee539168a912b4b3b040e4042b9e2c9a7ad9c8546c09e4eaeff3eacba6b", - "sha256:554c7327bf0fd688050348e22db7c8e163fb7219f3ecdd4732d7ed606b417263", - "sha256:5662bf0f6fb6757f5c2d6279c541a5af55a39772c2362ed0920b27e3ce0e21f7", - "sha256:5997d418c219dcd4dcba64e50671cca849aaf0dac3d7a2eeeb7d651a5bd735b8", - "sha256:59a75e6aa5c25b50b5a1499f9718f2edff54257f545718c4fb100f48d570ead4", - "sha256:60b66b0363c5a2a79fba3d1cd7430c25bbd92c923d031cae906bdcb6e054d9a2", - "sha256:6e34680049eecb30b6498784c9637c1c74277dcb1db75649a152f8004fbd6646", - "sha256:74eeaa13e8200ad72fca9c5f37395fb310915cec6f1682b21375e84fd9770e84", - "sha256:7c5c5b7ae2763533152880d5b5b451acbc1089ade2336b710a24b2b0f5239d20", - "sha256:829fb55ad437d757c70d5b1c51cfda9377f31506a0a3f3ac282bc6a387d6a5f1", - "sha256:878243e1206828908a6b4a9ca7b1aa8bee9eb129bf7186fc381d2646f4524ce9", - "sha256:8809c0ea0e8454f756e3bd5c36d04dddf222989216788a25bfd6724bfcee342c", - "sha256:8941e35a0e991a7a20a1fa3e3182f82abe357211f2c335a9e6007067c3392fcf", - "sha256:894b1acded706f1407a662d08e026bfd0ff1e59e9bd32062fea9d862564cfb65", - "sha256:900532713115ac58bc3491b9d2b52704a05ed408ba0918d57fd72c94bc47fba1", - "sha256:976cd92d9420e6e2aa6ce6a9d61f2b490e07cb468968adf371546b33b829284b", - "sha256:97de509043d3f0f2b2cd171bdccf408f175c7f7a99d36d566b1ae4dd84107985", - "sha256:9a42970ce74c88bdf144df11c52c5cf4ad610d860de87c0883385a1c9d9fa4ab", - "sha256:9e41c94035e5cdb362beed681b58a707e8dc29ea446ea1713d92afeded9d1ddd", - "sha256:9f805481d5eff2a96bac4da1570ef662bf970f9a16580dc2c169c8c3183fa02b", - "sha256:a35c97af60a5492e9e89f8b7153fe24eadfd61cb3a2fb600df1a25b5dab34b7e", - "sha256:a7c6574225f34ce45466f04751d957b5c5e6b69fca9351db017c9249786172ce", - "sha256:c7ebf2a37e4f5fea3c1a11e1f47cea7d75d0f2d8ef69635ddbd5c927083211fc", - "sha256:d0305e02e40c7cfea5d08d6368576537a74c0eea62b77633179748d3519d6705", - "sha256:e1046aab24c48c694f0793f669ac49ea68acde6a0798ac5388abe0a5615b5ec8", - "sha256:e5d22eba19273b2069e4efeff88c897a26bdc64633cbe0357a198f92dca94268", - "sha256:ec27e93bbf5976f0465e8936f02eb5add99bbe4e4e7b233607e4d7622912d68d", - "sha256:fe76d6dee5e4febefa83998b17926df3a04e5089e3d2b1688c74a9157798d7a2" + "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523", + "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f", + "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d", + "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb", + "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0", + "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c", + "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98", + "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83", + "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8", + "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7", + "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac", + "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84", + "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb", + "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3", + "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884", + "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614", + "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd", + "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807", + "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd", + "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8", + "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc", + "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db", + "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0", + "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08", + "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232", + "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d", + "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a", + "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1", + "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286", + "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303", + "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341", + "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84", + "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45", + "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc", + "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec", + "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd", + "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155", + "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52", + "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d", + "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485", + "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31", + "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d", + "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d", + "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d", + "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85", + "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce", + "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb", + "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974", + "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24", + "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56", + "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9", + "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35" ], "index": "pypi", - "version": "==7.5.2" + "markers": "python_version >= '3.8'", + "version": "==7.5.3" }, "docker": { "hashes": [ @@ -1818,6 +1837,12 @@ "markers": "python_version >= '3.8'", "version": "==7.1.0" }, + "events": { + "hashes": [ + "sha256:a7286af378ba3e46640ac9825156c93bdba7502174dd696090fdfcd4d80a1abd" + ], + "version": "==0.5" + }, "idna": { "hashes": [ "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", @@ -1844,11 +1869,12 @@ }, "opensearch-py": { "hashes": [ - "sha256:0dde4ac7158a717d92a8cd81964cb99705a4b80bcf9258ba195b9a9f23f5226d", - "sha256:cf093a40e272b60663f20417fc1264ac724dcf1e03c1a4542a6b44835b1e6c49" + "sha256:0b7c27e8ed84c03c99558406927b6161f186a72502ca6d0325413d8e5523ba96", + "sha256:b6e78b685dd4e9c016d7a4299cf1de69e299c88322e3f81c716e6e23fe5683c1" ], "index": "pypi", - "version": "==2.5.0" + "markers": "python_version >= '3.8' and python_version < '4'", + "version": "==2.6.0" }, "packaging": { "hashes": [ @@ -1918,6 +1944,7 @@ "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==8.2.1" }, "python-dateutil": { @@ -1930,11 +1957,12 @@ }, "requests": { "hashes": [ - "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289", - "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c" + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], "index": "pypi", - "version": "==2.32.2" + "markers": "python_version >= '3.8'", + "version": "==2.32.3" }, "requests-mock": { "hashes": [ @@ -1942,6 +1970,7 @@ "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401" ], "index": "pypi", + "markers": "python_version >= '3.5'", "version": "==1.12.1" }, "six": { @@ -1964,6 +1993,7 @@ "sha256:54d330d085c0a11fc5da0b001af87aec4dd3e814104376bf7513e8646c77442a" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==0.0.1rc1" }, "testcontainers-opensearch": { @@ -1971,23 +2001,24 @@ "sha256:0bdf270b5b7f53915832f7c31dd2bd3ffdc20b534ea6b32231cc7003049bd0e1" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==0.0.1rc1" }, "typing-extensions": { "hashes": [ - "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8", - "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594" + "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a", + "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1" ], "markers": "python_version >= '3.8'", - "version": "==4.12.0" + "version": "==4.12.1" }, "urllib3": { "hashes": [ - "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07", - "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0" + "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", + "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.18" + "markers": "python_version >= '3.8'", + "version": "==2.2.1" }, "wrapt": { "hashes": [ diff --git a/dbrepo-analyse-service/determine_dt.py b/dbrepo-analyse-service/determine_dt.py index 5aa34240e4..7c5401a20c 100644 --- a/dbrepo-analyse-service/determine_dt.py +++ b/dbrepo-analyse-service/determine_dt.py @@ -9,7 +9,6 @@ import pandas from numpy import dtype, max, min from flask import current_app -from pandas._libs.tslibs.parsing import DateParseError from clients.s3_client import S3Client @@ -65,7 +64,7 @@ def determine_datatypes(filename, enum=False, enum_tol=0.0001, separator=None) - pandas.to_datetime(df[name], format='mixed') r[name] = 'timestamp' continue - except DateParseError: + except ValueError: pass max_size = max(df[name].astype(str).map(len)) if max_size <= 1: diff --git a/dbrepo-analyse-service/lib/dbrepo-1.4.4-py3-none-any.whl b/dbrepo-analyse-service/lib/dbrepo-1.4.4-py3-none-any.whl index 4acaa2f242aa1a17081c6b649879afacfa191dad..f58e17a58e747e35bbd37f43efe6b460ba31530f 100644 GIT binary patch delta 14068 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 delta 13983 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 Zq!5&&Ca=p9V>$p4zn>+>W}gny0RZN02Au!^ diff --git a/dbrepo-analyse-service/lib/dbrepo-1.4.4.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.4.4.tar.gz index e0bdb5adee8536ec4455e215913a6a769237b1e9..5463f6b170c24fff05d29a434562104553292fea 100644 GIT binary patch literal 37662 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 b!VYJdM*{5#Cj0rv{~3Q@`|HRMz{&ssMI%h+ literal 37614 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 Z98u&e>~Kafo1cIDZyxgh6GH$i0{}N68*cyr diff --git a/dbrepo-data-service/Dockerfile b/dbrepo-data-service/Dockerfile index 0d278d8a01..69edbda315 100644 --- a/dbrepo-data-service/Dockerfile +++ b/dbrepo-data-service/Dockerfile @@ -21,7 +21,7 @@ COPY ./services ./services RUN mvn clean package -DskipTests ###### THIRD STAGE ###### -FROM eclipse-temurin:17-jdk as runtime +FROM amazoncorretto:17-alpine3.19 as runtime MAINTAINER Martin Weise <martin.weise@tuwien.ac.at> WORKDIR /app diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index d000896870..b93ff32264 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -212,7 +212,9 @@ public class TableEndpoint { @PostMapping("/{tableId}/data") @PreAuthorize("hasAuthority('insert-table-data')") @Observed(name = "dbrepo_table_data_create") - @Operation(summary = "Create table data", security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")}) + @Operation(summary = "Insert a raw data tuple", + description = "Inserts a raw data tuple into a table with at least WRITE_OWN access. Then update the table statistics.", + security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")}) @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Created table data"), @@ -237,13 +239,13 @@ public class TableEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<Void> createTuple(@NotBlank @PathVariable("databaseId") Long databaseId, - @NotBlank @PathVariable("tableId") Long tableId, - @Valid @RequestBody TupleDto data, - @NotNull Principal principal) + public ResponseEntity<Void> insertRawTuple(@NotBlank @PathVariable("databaseId") Long databaseId, + @NotBlank @PathVariable("tableId") Long tableId, + @Valid @RequestBody TupleDto data, + @NotNull Principal principal) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, TableMalformedException, QueryMalformedException, NotAllowedException { - log.debug("endpoint create table data, databaseId={}, tableId={}", databaseId, tableId); + log.debug("endpoint insert raw table data, databaseId={}, tableId={}", databaseId, tableId); final PrivilegedTableDto table = metadataServiceGateway.getTableById(databaseId, tableId); final DatabaseAccessDto access = metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), UserUtil.getId(principal)); @@ -262,7 +264,9 @@ public class TableEndpoint { @PutMapping("/{tableId}/data") @PreAuthorize("hasAuthority('insert-table-data')") @Observed(name = "dbrepo_table_data_update") - @Operation(summary = "Update table data", security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")}) + @Operation(summary = "Update a raw data tuple", + description = "Updates a raw data tuple in a table with at least WRITE_OWN access. Then update the table statistics.", + security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")}) @ApiResponses(value = { @ApiResponse(responseCode = "202", description = "Updated table data"), @@ -287,13 +291,13 @@ public class TableEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<Void> updateTuple(@NotBlank @PathVariable("databaseId") Long databaseId, - @NotBlank @PathVariable("tableId") Long tableId, - @Valid @RequestBody TupleUpdateDto data, - @NotNull Principal principal) + public ResponseEntity<Void> updateRawTuple(@NotBlank @PathVariable("databaseId") Long databaseId, + @NotBlank @PathVariable("tableId") Long tableId, + @Valid @RequestBody TupleUpdateDto data, + @NotNull Principal principal) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, TableMalformedException, QueryMalformedException, NotAllowedException { - log.debug("endpoint update table data, databaseId={}, tableId={}, data.keys={}", databaseId, tableId, + log.debug("endpoint update raw table data, databaseId={}, tableId={}, data.keys={}", databaseId, tableId, data.getKeys()); final PrivilegedTableDto table = metadataServiceGateway.getTableById(databaseId, tableId); final DatabaseAccessDto access = metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); @@ -313,7 +317,9 @@ public class TableEndpoint { @DeleteMapping("/{tableId}/data") @PreAuthorize("hasAuthority('delete-table-data')") @Observed(name = "dbrepo_table_data_delete") - @Operation(summary = "Delete table data", security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")}) + @Operation(summary = "Delete table data", + description = "Deletes a raw data tuple in a table with at least WRITE_OWN access. Then update the table statistics.", + security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")}) @ApiResponses(value = { @ApiResponse(responseCode = "202", description = "Deleted table data"), @@ -338,13 +344,13 @@ public class TableEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<Void> deleteTuple(@NotBlank @PathVariable("databaseId") Long databaseId, - @NotBlank @PathVariable("tableId") Long tableId, - @Valid @RequestBody TupleDeleteDto data, - @NotNull Principal principal) + public ResponseEntity<Void> deleteRawTuple(@NotBlank @PathVariable("databaseId") Long databaseId, + @NotBlank @PathVariable("tableId") Long tableId, + @Valid @RequestBody TupleDeleteDto data, + @NotNull Principal principal) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, TableMalformedException, QueryMalformedException, NotAllowedException { - log.debug("endpoint update table data, databaseId={}, tableId={}, data.keys={}", databaseId, tableId, + log.debug("endpoint delete raw table data, databaseId={}, tableId={}, data.keys={}", databaseId, tableId, data.getKeys()); final PrivilegedTableDto table = metadataServiceGateway.getTableById(databaseId, tableId); final DatabaseAccessDto access = metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); @@ -524,12 +530,14 @@ public class TableEndpoint { @PostMapping("/{tableId}/data/import") @Observed(name = "dbrepo_table_data_import") @PreAuthorize("hasAuthority('insert-table-data')") - @Operation(summary = "Import dataset", security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")}) + @Operation(summary = "Import data from a dataset", + description = "Deletes a raw data tuple in a table with at least WRITE_OWN access. Then update the table statistics.", + security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")}) @ApiResponses(value = { @ApiResponse(responseCode = "202", - description = "Import dataset successfully"), + description = "Imported dataset successfully"), @ApiResponse(responseCode = "400", - description = "Import dataset query is malformed", + description = "Dataset query is malformed", content = {@Content( mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), @@ -549,10 +557,10 @@ public class TableEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<Void> importData(@NotBlank @PathVariable("databaseId") Long databaseId, - @NotBlank @PathVariable("tableId") Long tableId, - @Valid @RequestBody ImportCsvDto data, - @NotNull Principal principal) + public ResponseEntity<Void> importDataset(@NotBlank @PathVariable("databaseId") Long databaseId, + @NotBlank @PathVariable("tableId") Long tableId, + @Valid @RequestBody ImportCsvDto data, + @NotNull Principal principal) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, QueryMalformedException, StorageNotFoundException, SidecarImportException, NotAllowedException { log.debug("endpoint insert table data, databaseId={}, tableId={}, data.location={}", databaseId, tableId, data.getLocation()); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java index fd93a3ae96..73760a5a2a 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java @@ -205,7 +205,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TABLE_8_STATISTIC_DTO); /* test */ - final ResponseEntity<Void> response = tableEndpoint.createTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); assertEquals(HttpStatus.CREATED, response.getStatusCode()); } @@ -221,7 +221,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { - tableEndpoint.createTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -242,7 +242,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - tableEndpoint.createTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -265,7 +265,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.createTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -287,7 +287,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); /* test */ - tableEndpoint.createTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); } @Test @@ -309,7 +309,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.createTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -331,7 +331,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); /* test */ - tableEndpoint.createTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); } @Test @@ -363,7 +363,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TABLE_8_STATISTIC_DTO); /* test */ - final ResponseEntity<Void> response = tableEndpoint.updateTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -382,7 +382,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { - tableEndpoint.updateTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -406,7 +406,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - tableEndpoint.updateTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -432,7 +432,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.updateTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -466,7 +466,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TABLE_8_STATISTIC_DTO); /* test */ - final ResponseEntity<Void> response = tableEndpoint.updateTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -492,7 +492,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.updateTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -526,7 +526,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TABLE_8_STATISTIC_DTO); /* test */ - final ResponseEntity<Void> response = tableEndpoint.updateTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -555,7 +555,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TABLE_8_STATISTIC_DTO); /* test */ - final ResponseEntity<Void> response = tableEndpoint.deleteTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -570,7 +570,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { - tableEndpoint.deleteTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -590,7 +590,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - tableEndpoint.deleteTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -612,7 +612,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.deleteTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -642,7 +642,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TABLE_8_STATISTIC_DTO); /* test */ - final ResponseEntity<Void> response = tableEndpoint.deleteTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -664,7 +664,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.deleteTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -694,7 +694,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TABLE_8_STATISTIC_DTO); /* test */ - final ResponseEntity<Void> response = tableEndpoint.deleteTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -828,7 +828,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TABLE_8_STATISTIC_DTO); /* test */ - final ResponseEntity<Void> response = tableEndpoint.importData(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -843,7 +843,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { - tableEndpoint.importData(DATABASE_3_ID, TABLE_8_ID, request, USER_4_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_4_PRINCIPAL); }); } @@ -863,7 +863,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - tableEndpoint.importData(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -885,7 +885,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.importData(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -907,7 +907,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); /* test */ - tableEndpoint.importData(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); } @Test @@ -928,7 +928,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.importData(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); }); } @@ -950,7 +950,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); /* test */ - tableEndpoint.importData(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); } } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java index 1b51d3072c..a49b264a03 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java @@ -34,7 +34,6 @@ import org.springframework.test.web.servlet.MockMvc; import java.io.File; import java.io.IOException; -import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.*; @@ -177,17 +176,17 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* ignore */ } try { - tableEndpoint.createTuple(DATABASE_1_ID, TABLE_1_ID, TupleDto.builder().build(), USER_1_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleDto.builder().build(), USER_1_PRINCIPAL); } catch (Exception e) { /* ignore */ } try { - tableEndpoint.updateTuple(DATABASE_1_ID, TABLE_1_ID, TupleUpdateDto.builder().build(), USER_1_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleUpdateDto.builder().build(), USER_1_PRINCIPAL); } catch (Exception e) { /* ignore */ } try { - tableEndpoint.deleteTuple(DATABASE_1_ID, TABLE_1_ID, TupleDeleteDto.builder().build(), USER_1_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleDeleteDto.builder().build(), USER_1_PRINCIPAL); } catch (Exception e) { /* ignore */ } @@ -207,7 +206,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* ignore */ } try { - tableEndpoint.importData(DATABASE_1_ID, TABLE_1_ID, ImportCsvDto.builder().build(), USER_1_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, ImportCsvDto.builder().build(), USER_1_PRINCIPAL); } catch (Exception e) { /* ignore */ } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java index b379c01cdf..e28d05929b 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java @@ -8,6 +8,7 @@ import at.tuwien.api.database.query.ImportCsvDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.query.QueryResultDto; import at.tuwien.api.database.table.*; +import at.tuwien.api.database.table.columns.ColumnBriefDto; import at.tuwien.api.database.table.columns.ColumnCreateDto; import at.tuwien.api.database.table.columns.ColumnDto; import at.tuwien.api.database.table.columns.ColumnTypeDto; @@ -27,6 +28,7 @@ import org.jetbrains.annotations.NotNull; import org.mapstruct.Mapper; import org.mapstruct.Named; +import javax.swing.table.TableColumn; import java.io.*; import java.math.BigInteger; import java.nio.charset.StandardCharsets; @@ -583,6 +585,8 @@ public interface MariaDbMapper { TableBriefDto tableDtoToTableBriefDto(TableDto data); + ColumnBriefDto columnDtoToColumnBriefDto(ColumnDto data); + default TableDto resultSetToTable(ResultSet resultSet, TableDto table, QueryConfig queryConfig) throws SQLException { final ColumnDto column = ColumnDto.builder() .ordinalPosition(resultSet.getInt(1) - 1) /* start at zero */ @@ -601,7 +605,7 @@ public interface MariaDbMapper { if (resultSet.getString(9) != null && resultSet.getString(9).equals("PRI")) { table.getConstraints().getPrimaryKey().add(PrimaryKeyDto.builder() .table(tableDtoToTableBriefDto(table)) - .column(column) + .column(columnDtoToColumnBriefDto(column)) .build()); } /* fix boolean and set size for others */ diff --git a/dbrepo-metadata-service/Dockerfile b/dbrepo-metadata-service/Dockerfile index b66fad2759..5fd1138f24 100644 --- a/dbrepo-metadata-service/Dockerfile +++ b/dbrepo-metadata-service/Dockerfile @@ -27,7 +27,7 @@ COPY ./test ./test RUN mvn clean install -DskipTests ###### SECOND STAGE ###### -FROM eclipse-temurin:17-jdk as runtime +FROM amazoncorretto:17-alpine3.19 as runtime MAINTAINER Martin Weise <martin.weise@tuwien.ac.at> WORKDIR /app diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java index 430bdba270..740b2184e7 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnDto.java @@ -122,7 +122,7 @@ public class ColumnDto { @ToString.Exclude @JsonIgnore - private transient TableDto table; + private TableDto table; @ToString.Exclude @JsonIgnore diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyDto.java index 68ee2e56da..9fe7c68382 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyDto.java @@ -23,9 +23,6 @@ public class ForeignKeyDto { @NotNull private String name; - @NotNull - private ColumnDto column; - @NotNull private List<ForeignKeyReferenceDto> references; diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/primary/PrimaryKeyDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/primary/PrimaryKeyDto.java index 19c25371b2..25ad3f5d17 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/primary/PrimaryKeyDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/primary/PrimaryKeyDto.java @@ -1,7 +1,7 @@ package at.tuwien.api.database.table.constraints.primary; import at.tuwien.api.database.table.TableBriefDto; -import at.tuwien.api.database.table.columns.ColumnDto; +import at.tuwien.api.database.table.columns.ColumnBriefDto; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -15,7 +15,7 @@ import lombok.extern.jackson.Jacksonized; @ToString public class PrimaryKeyDto { - private Long pkid; + private Long id; @NotNull @ToString.Exclude @@ -23,5 +23,5 @@ public class PrimaryKeyDto { @NotNull @ToString.Exclude - private ColumnDto column; + private ColumnBriefDto column; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/unique/UniqueDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/unique/UniqueDto.java index 10f97e8390..05de537844 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/unique/UniqueDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/unique/UniqueDto.java @@ -21,7 +21,7 @@ import java.util.List; public class UniqueDto { @NotNull - private Long uid; + private Long id; @NotNull private String name; diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/foreignKey/ForeignKey.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/foreignKey/ForeignKey.java index 83e0d35656..e5a58f5d0d 100644 --- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/foreignKey/ForeignKey.java +++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/foreignKey/ForeignKey.java @@ -6,6 +6,7 @@ import org.hibernate.annotations.GenericGenerator; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import jakarta.persistence.*; + import java.util.List; @Data @@ -23,8 +24,8 @@ public class ForeignKey { @EqualsAndHashCode.Include @GeneratedValue(generator = "foreign-key-sequence") @GenericGenerator(name = "foreign-key-sequence", strategy = "increment") - @Column(updatable = false, nullable = false) - private Long fkid; + @Column(name = "fkid", updatable = false, nullable = false) + private Long id; @Column(updatable = false, nullable = false) private String name; diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/primaryKey/PrimaryKey.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/primaryKey/PrimaryKey.java index 8a30122286..c4ccc379c5 100644 --- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/primaryKey/PrimaryKey.java +++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/primaryKey/PrimaryKey.java @@ -22,8 +22,8 @@ public class PrimaryKey { @EqualsAndHashCode.Include @GeneratedValue(generator = "foreign-key-sequence") @GenericGenerator(name = "foreign-key-sequence", strategy = "increment") - @Column(updatable = false, nullable = false) - private Long pkid; + @Column(name = "pkid", updatable = false, nullable = false) + private Long id; @ToString.Exclude @org.springframework.data.annotation.Transient diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/unique/Unique.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/unique/Unique.java index 269410a8c8..25ed2eae5d 100644 --- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/unique/Unique.java +++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/constraints/unique/Unique.java @@ -25,8 +25,8 @@ public class Unique { @EqualsAndHashCode.Include @GeneratedValue(generator = "constraints-unique-sequence") @GenericGenerator(name = "constraints-unique-sequence", strategy = "increment") - @Column(updatable = false, nullable = false) - private Long uid; + @Column(name = "uid", updatable = false, nullable = false) + private Long id; @Column(updatable = false, nullable = false) private String name; diff --git a/dbrepo-metadata-service/pom.xml b/dbrepo-metadata-service/pom.xml index 2e6651d9cf..464cf28ed9 100644 --- a/dbrepo-metadata-service/pom.xml +++ b/dbrepo-metadata-service/pom.xml @@ -34,26 +34,6 @@ <email>martin.weise@tuwien.ac.at</email> <organization>TU Wien</organization> </developer> - <developer> - <name>Moritz Staudinger</name> - <email>moritz.staudinger@tuwien.ac.at</email> - <organization>TU Wien</organization> - </developer> - <developer> - <name>Tobias Grantner</name> - <email>tobias.grantner@tuwien.ac.at</email> - <organization>TU Wien</organization> - </developer> - <developer> - <name>Sotirios Tsepelakis</name> - <email>sotirios.tsepelakis@tuwien.ac.at</email> - <organization>TU Wien</organization> - </developer> - <developer> - <name>Geoffrey Karnbach</name> - <email>geoffrey.karnbach@tuwien.ac.at</email> - <organization>TU Wien</organization> - </developer> </developers> <properties> @@ -192,6 +172,11 @@ <artifactId>commons-validator</artifactId> <version>${commons-validator.version}</version> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.datatype</groupId> + <artifactId>jackson-datatype-hibernate6</artifactId> + <version>${jackson-datatype.version}</version> + </dependency> <!-- Authentication --> <dependency> <groupId>org.keycloak</groupId> diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/AccessMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/AccessMapper.java deleted file mode 100644 index 9ce8f74cce..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/AccessMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.database.AccessTypeDto; -import at.tuwien.entities.database.AccessType; -import org.mapstruct.Mapper; - -@Mapper(componentModel = "spring") -public interface AccessMapper { - - AccessTypeDto accessType(AccessType data); - - AccessType accessType(AccessTypeDto data); - -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/AuthenticationMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/AuthenticationMapper.java deleted file mode 100644 index 9d00f7e159..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/AuthenticationMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package at.tuwien.mapper; - -import org.mapstruct.Mapper; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; - -import java.util.Collections; - -@Mapper(componentModel = "spring", uses = {UserMapper.class}) -public interface AuthenticationMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AuthenticationMapper.class); - - default MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { - final MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); - mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_FORM_URLENCODED)); - return mappingJackson2HttpMessageConverter; - } -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/BannerMessageMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/BannerMessageMapper.java deleted file mode 100644 index 8fd2b7dc51..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/BannerMessageMapper.java +++ /dev/null @@ -1,24 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.maintenance.BannerMessageBriefDto; -import at.tuwien.api.maintenance.BannerMessageCreateDto; -import at.tuwien.api.maintenance.BannerMessageDto; -import at.tuwien.api.maintenance.BannerMessageTypeDto; -import at.tuwien.entities.maintenance.BannerMessage; -import at.tuwien.entities.maintenance.BannerMessageType; -import org.mapstruct.Mapper; - -@Mapper(componentModel = "spring") -public interface BannerMessageMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BannerMessageMapper.class); - - BannerMessageDto bannerMessageToBannerMessageDto(BannerMessage data); - - BannerMessageBriefDto bannerMessageToBannerMessageBriefDto(BannerMessage data); - - BannerMessage bannerMessageCreateDtoToBannerMessage(BannerMessageCreateDto data); - - BannerMessageType bannerMessageTypeDtoToBannerMessageType(BannerMessageTypeDto data); - -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ContainerMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ContainerMapper.java deleted file mode 100644 index 3f6cf3c302..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ContainerMapper.java +++ /dev/null @@ -1,45 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.container.ContainerBriefDto; -import at.tuwien.api.container.ContainerCreateDto; -import at.tuwien.api.container.ContainerDto; -import at.tuwien.entities.container.Container; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; -import org.mapstruct.Named; - -import java.text.Normalizer; -import java.util.Locale; -import java.util.regex.Pattern; - -@Mapper(componentModel = "spring", uses = {ImageMapper.class, DatabaseMapper.class, UserMapper.class}) -public interface ContainerMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ContainerMapper.class); - - @Mappings({ - @Mapping(target = "internalName", source = "name", qualifiedByName = "internalNameMapping") - }) - Container containerCreateRequestDtoToContainer(ContainerCreateDto data); - - ContainerDto containerToContainerDto(Container data); - - @Mappings({ - @Mapping(target = "id", source = "id") - }) - ContainerBriefDto containerToDatabaseContainerBriefDto(Container data); - - // https://stackoverflow.com/questions/1657193/java-code-library-for-generating-slugs-for-use-in-pretty-urls#answer-1657250 - @Named("internalNameMapping") - default String containerToInternalContainerName(String containerName) { - final Pattern NONLATIN = Pattern.compile("[^\\w-]"); - final Pattern WHITESPACE = Pattern.compile("[\\s]"); - String nowhitespace = WHITESPACE.matcher(containerName).replaceAll("-"); - String normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD); - String slug = NONLATIN.matcher(normalized).replaceAll(""); - final String name = "dbrepo-userdb-" + slug.toLowerCase(Locale.ENGLISH); - log.trace("mapped container name {} to internal name {}", containerName, name); - return name; - } -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DataCiteMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DataCiteMapper.java deleted file mode 100644 index 749f086fcb..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DataCiteMapper.java +++ /dev/null @@ -1,133 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.datacite.doi.*; -import at.tuwien.entities.database.License; -import at.tuwien.entities.identifier.*; -import at.tuwien.utils.EnumToStringConverter; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.Mappings; -import org.springframework.context.annotation.Profile; - -import java.util.LinkedList; -import java.util.List; - -@Profile("doi") -@Mapper(componentModel = "spring", uses = EnumToStringConverter.class) -public interface DataCiteMapper { - - default <T> List<T> list(T t) { - if (t == null) return null; - return List.of(t); - } - - @Mappings({ - @Mapping(target = "titles", source = "."), - @Mapping(target = "publisher", source = "publisher"), - @Mapping(target = "publicationYear", source = "publicationYear"), - @Mapping(target = "publicationMonth", source = "publicationMonth"), - @Mapping(target = "publicationDay", source = "publicationDay"), - @Mapping(target = "language", source = "language"), - @Mapping(target = "creators", source = "creators"), - }) - DataCiteCreateDoi identifierToDataCiteCreateDoi(Identifier identifier); - - default DataCiteCreateDoi identifierToDataCiteCreateDoi(Identifier identifier, String url, String prefix, - DataCiteDoiEvent event) { - return addParametersToCreateDoi( - identifierToDataCiteCreateDoi(identifier), - url, - prefix, - DataCiteDoiTypes.DATASET, - event - ); - } - - DataCiteDoiTitle identifierTitleToDataCiteDoiTitle(IdentifierTitle data); - - default DataCiteDoiTitle.Type titleTypeToDataCiteDoiTitleType(TitleType data) { - if (data == null) { - return null; - } - return switch (data) { - case OTHER -> DataCiteDoiTitle.Type.OTHER; - case TRANSLATED_TITLE -> DataCiteDoiTitle.Type.TRANSLATED_TITLE; - case SUBTITLE -> DataCiteDoiTitle.Type.SUBTITLE; - case ALTERNATIVE_TITLE -> DataCiteDoiTitle.Type.ALTERNATIVE_TITLE; - }; - } - - default List<DataCiteDoiTitle> identifierToDataCiteDoiTitleList(Identifier data) { - if (data.getTitles() == null) { - return new LinkedList<>(); - } - return data.getTitles() - .stream() - .map(this::identifierTitleToDataCiteDoiTitle) - .toList(); - } - - DataCiteCreateDoi addParametersToCreateDoi(@MappingTarget DataCiteCreateDoi target, String url, String prefix, - DataCiteDoiTypes types, DataCiteDoiEvent event); - - @Mappings({ - @Mapping(target = "rights", source = "identifier"), - @Mapping(target = "rightsUri", source = "uri"), - }) - DataCiteDoiRights licenseToDoiRights(License license); - - @Mappings({ - @Mapping(target = "name", expression = "java(data.getLastname() + \", \" + data.getFirstname())"), - @Mapping(target = "givenName", source = "firstname"), - @Mapping(target = "familyName", source = "lastname"), - @Mapping(target = "nameType", expression = "java(nameTypeToDataCiteNameType(data.getNameType()))"), - @Mapping(target = "affiliation", expression = "java(list(creatorToDoiCreatorAffiliation(data)))"), - @Mapping(target = "nameIdentifier", expression = "java(list(creatorToDataCiteDoiCreatorNameIdentifier(data)))"), - }) - DataCiteDoiCreator creatorToDoiCreator(Creator data); - - DataCiteDoiCreatorNameIdentifier creatorToDataCiteDoiCreatorNameIdentifier(Creator data); - - /* keep */ - default String nameIdentifierSchemeTypeToUri(NameIdentifierSchemeType data) { - switch (data) { - case ROR -> { - return "https://ror.org/"; - } - case ORCID -> { - return "https://orcid.org/"; - } - case ISNI -> { - return "https://isni.org/isni/"; - } - case GRID -> { - return "https://www.grid.ac/"; - } - } - return null; - } - - /* keep */ - default DataCiteNameType nameTypeToDataCiteNameType(NameType data) { - if (data == null) { - return null; - } - return DataCiteNameType.valueOf(data.toString()); - } - - @Mappings({ - @Mapping(target = "name", source = "affiliation"), - @Mapping(target = "affiliationIdentifier", source = "affiliationIdentifier"), - @Mapping(target = "affiliationScheme", source = "affiliationIdentifierScheme"), - @Mapping(target = "schemeUri", source = "affiliationIdentifierSchemeUri"), - }) - DataCiteDoiCreatorAffiliation creatorToDoiCreatorAffiliation(Creator data); - - @Mappings({ - @Mapping(target = "relatedIdentifier", source = "value"), - @Mapping(target = "relatedIdentifierType", source = "type"), - @Mapping(target = "relationType", source = "relation"), - }) - DataCiteDoiRelatedIdentifier relatedIdentifierToDoiRelatedIdentifier(RelatedIdentifier relatedIdentifier); -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DatabaseMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DatabaseMapper.java deleted file mode 100644 index c2428f5f2a..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DatabaseMapper.java +++ /dev/null @@ -1,62 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.database.*; -import at.tuwien.entities.container.image.ContainerImage; -import at.tuwien.entities.database.*; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; -import org.mapstruct.Named; - -import java.text.Normalizer; -import java.util.Locale; -import java.util.regex.Pattern; - -@Mapper(componentModel = "spring", uses = {ContainerMapper.class, UserMapper.class, ImageMapper.class, UserMapper.class, - TableMapper.class, IdentifierMapper.class, ViewMapper.class}) -public interface DatabaseMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DatabaseMapper.class); - - /* keep */ - @Named("internalMapping") - default String nameToInternalName(String data) { - if (data == null || data.isEmpty()) { - return data; - } - final Pattern NONLATIN = Pattern.compile("[^\\w-]"); - final Pattern WHITESPACE = Pattern.compile("[\\s]"); - String nowhitespace = WHITESPACE.matcher(data).replaceAll("_"); - String normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD); - String slug = NONLATIN.matcher(normalized).replaceAll(""); - final String name = slug.toLowerCase(Locale.ENGLISH); - log.debug("mapping name {} to internal name {}", data, name); - return name; - } - - /* keep */ - @Named("languageMapping") - LanguageType languageTypeDtoToLanguageType(LanguageTypeDto data); - - @Named("engineMapping") - default String containerImageToEngine(ContainerImage data) { - return data.getName() + ":" + data.getVersion(); - } - - @Mappings({ - @Mapping(target = "created", source = "created", dateFormat = "dd-MM-yyyy HH:mm"), - }) - DatabaseDto databaseToDatabaseDto(Database data); - - @Mappings({ - @Mapping(target = "created", source = "created", dateFormat = "dd-MM-yyyy HH:mm"), - }) - DatabaseBriefDto databaseToDatabaseBriefDto(Database data); - - AccessType accessTypeDtoToAccessType(AccessTypeDto data); - - AccessTypeDto accessTypeToAccessTypeDto(AccessType data); - - DatabaseAccessDto databaseAccessToDatabaseAccessDto(DatabaseAccess data); - -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DocumentMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DocumentMapper.java deleted file mode 100644 index 9a4fc4840d..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/DocumentMapper.java +++ /dev/null @@ -1,15 +0,0 @@ -package at.tuwien.mapper; - -import org.mapstruct.Mapper; - -import java.time.Instant; -import java.util.Date; - -@Mapper(componentModel = "spring") -public interface DocumentMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DocumentMapper.class); - - Date instantToDate(Instant data); - -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ExternalMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ExternalMapper.java deleted file mode 100644 index 8ab5797770..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ExternalMapper.java +++ /dev/null @@ -1,76 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.crossref.CrossrefDto; -import at.tuwien.api.orcid.OrcidDto; -import at.tuwien.api.orcid.activities.employments.affiliation.OrcidAffiliationGroupDto; -import at.tuwien.api.orcid.activities.employments.affiliation.group.OrcidEmploymentSummaryDto; -import at.tuwien.api.orcid.activities.employments.affiliation.group.summary.organization.disambiguated.OrcidDisambiguatedDto; -import at.tuwien.api.orcid.activities.employments.affiliation.group.summary.organization.disambiguated.OrcidDisambiguatedSourceTypeDto; -import at.tuwien.api.ror.RorDto; -import at.tuwien.api.user.external.ExternalMetadataDto; -import at.tuwien.api.user.external.ExternalResultType; -import at.tuwien.api.user.external.affiliation.ExternalAffiliationDto; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; - -@Mapper(componentModel = "spring", imports = {ExternalResultType.class}) -public interface ExternalMapper { - - - @Mappings({ - @Mapping(target = "givenNames", source = "person.name.givenNames.value"), - @Mapping(target = "familyName", source = "person.name.familyName.value"), - @Mapping(target = "type", expression = "java(ExternalResultType.PERSONAL)"), - @Mapping(target = "affiliations", source = "activitiesSummary.employments.affiliationGroup"), - }) - ExternalMetadataDto orcidDtoToExternalMetadataDto(OrcidDto data); - - @Mappings({ - @Mapping(target = "organizationName", source = "employmentSummary.organization.name"), - @Mapping(target = "ringgoldId", expression = "java(disambiguatedOrganizationToRinggoldId(data.getEmploymentSummary().getOrganization().getDisambiguatedOrganization()))"), - }) - ExternalAffiliationDto orcidEmploymentSummaryDtoToExternalAffiliationDto(OrcidEmploymentSummaryDto data); - - default ExternalAffiliationDto orcidAffiliationGroupDtoToExternalAffiliationDto(OrcidAffiliationGroupDto data) { - if (data == null || data.getSummaries() == null || data.getSummaries().length == 0) { - return null; - } - return ExternalAffiliationDto.builder() - .organizationName(data.getSummaries()[0].getEmploymentSummary().getOrganization().getName()) - .build(); - } - - default Long disambiguatedOrganizationToRinggoldId(OrcidDisambiguatedDto data) { - if (data.getSource().equals(OrcidDisambiguatedSourceTypeDto.RINGGOLD)) { - return Long.parseLong(data.getIdentifier()); - } - return null; - } - - default ExternalMetadataDto rorDtoToExternalMetadataDto(RorDto data) { - return ExternalMetadataDto.builder() - .affiliations(new ExternalAffiliationDto[]{ - ExternalAffiliationDto.builder() - .organizationName(data.getName()) - .build()}) - .type(ExternalResultType.ORGANIZATIONAL) - .build(); - } - - default ExternalMetadataDto crossrefDtoToExternalMetadataDto(CrossrefDto data) { - return ExternalMetadataDto.builder() - .affiliations(new ExternalAffiliationDto[]{ - ExternalAffiliationDto.builder() - .crossrefFunderId(data.getId()) - .organizationName(data.getPrefLabel().getLabel().getLiteralForm().getContent()) - .build()}) - .type(ExternalResultType.ORGANIZATIONAL) - .build(); - } - - @Mappings({ - @Mapping(target = "organizationName", source = "name"), - }) - ExternalAffiliationDto rorDtoToExternalAffiliationDto(RorDto data); -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/IdentifierMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/IdentifierMapper.java deleted file mode 100644 index a4b7d28fa3..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/IdentifierMapper.java +++ /dev/null @@ -1,157 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.database.LanguageTypeDto; -import at.tuwien.api.database.LicenseDto; -import at.tuwien.api.identifier.*; -import at.tuwien.api.identifier.ld.LdCreatorDto; -import at.tuwien.api.identifier.ld.LdDatasetDto; -import at.tuwien.entities.database.LanguageType; -import at.tuwien.entities.database.License; -import at.tuwien.entities.identifier.*; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; -import org.mapstruct.Named; - -import java.util.List; -import java.util.Optional; - -@Mapper(componentModel = "spring", uses = {DatabaseMapper.class}) -public interface IdentifierMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(IdentifierMapper.class); - - Identifier identifierDtoToIdentifier(IdentifierDto data); - - @Mappings({ - @Mapping(target = "databaseId", source = "database.id"), - }) - IdentifierDto identifierToIdentifierDto(Identifier data); - - IdentifierBriefDto identifierToIdentifierBriefDto(Identifier data); - - default IdentifierTitle identifierToIdentifierTitle(Identifier data, String lang) { - final Optional<IdentifierTitle> optional = data.getTitles() - .stream() - .filter(t -> lang == null || t.getLanguage().getName().equals(lang)) - .findFirst(); - if (optional.isEmpty()) { - log.warn("no title with language {} found", lang); - return identifierToIdentifierTitle(data, "en"); - } - return optional.get(); - } - - default IdentifierDescription identifierToIdentifierDescription(Identifier data, String lang) { - final Optional<IdentifierDescription> optional = data.getDescriptions() - .stream() - .filter(t -> lang == null || t.getLanguage().getName().equals(lang)) - .findFirst(); - if (optional.isEmpty()) { - log.warn("no description with language {} found", lang); - return identifierToIdentifierDescription(data, "en"); - } - return optional.get(); - } - - @Mappings({ - @Mapping(target = "givenName", source = "firstname"), - @Mapping(target = "familyName", source = "lastname"), - @Mapping(target = "type", expression = "java(data.getNameType().equals(NameType.PERSONAL) ? \"Person\" : \"Organization\")"), - @Mapping(target = "sameAs", source = "nameIdentifier"), - @Mapping(target = "name", source = "creatorName"), - }) - LdCreatorDto creatorToLdCreatorDto(Creator data); - - default LdDatasetDto identifierToLdDatasetDto(Identifier data, String baseUrl) { - return LdDatasetDto.builder() - .context("https://schema.org/") - .type("Dataset") - .name(identifierToIdentifierTitle(data, null) - .getTitle()) - .description(identifierToIdentifierDescription(data, null) - .getDescription()) - .url(identifierToLocationUrl(baseUrl, data)) - .identifier(List.of()) - .creator(data.getCreators() - .stream() - .map(this::creatorToLdCreatorDto) - .toList()) - .citation(identifierToLocationUrl(baseUrl, data)) - .hasPart(List.of()) - .license(data.getLicenses().isEmpty() ? null : data.getLicenses().get(0).getUri()) - .temporalCoverage(null) - .version(data.getCreated()) - .build(); - } - - Identifier identifierCreateDtoToIdentifier(IdentifierCreateDto data); - - Identifier identifierUpdateDtoToIdentifier(IdentifierSaveDto data); - - LanguageType languageTypeDtoToLanguageType(LanguageTypeDto data); - - License licenseDtoToLicense(LicenseDto data); - - IdentifierTitle identifierCreateTitleDtoToIdentifierTitle(IdentifierSaveTitleDto data); - - IdentifierDescription identifierCreateDescriptionDtoToIdentifierDescription(IdentifierSaveDescriptionDto data); - - IdentifierFunder identifierFunderSaveDtoToIdentifierFunder(IdentifierFunderSaveDto data); - - IdentifierSaveDto identifierCreateDtoToIdentifierSaveDto(IdentifierCreateDto data); - - RelatedIdentifierDto relatedIdentifierToRelatedIdentifierDto(RelatedIdentifier data); - - Creator creatorDtoToCreator(CreatorDto data); - - @Mappings({ - @Mapping(target = "nameIdentifierSchemeUri", source = "nameIdentifierScheme", qualifiedByName = "nameSchemaMapper"), - @Mapping(target = "affiliationIdentifierSchemeUri", source = "affiliationIdentifierScheme", qualifiedByName = "affiliationSchemaMapper"), - }) - Creator creatorCreateDtoToCreator(CreatorSaveDto data); - - RelatedIdentifier relatedIdentifierCreateDtoToRelatedIdentifier(RelatedIdentifierSaveDto data); - - IdentifierType identifierTypeDtoToIdentifierType(IdentifierTypeDto data); - - default String identifierToLocationUrl(String baseUrl, Identifier data) { - if (data.getType().equals(IdentifierType.SUBSET)) { - return baseUrl + "/database/" + data.getDatabase().getId() + "/subset/" + data.getQueryId() + "/info?pid=" + data.getId(); - } else if (data.getType().equals(IdentifierType.DATABASE)) { - return baseUrl + "/database/" + data.getDatabase().getId() + "/info?pid=" + data.getId(); - } else if (data.getType().equals(IdentifierType.VIEW)) { - return baseUrl + "/database/" + data.getDatabase().getId() + "/view/" + data.getViewId() + "/info?pid=" + data.getId(); - } else if (data.getType().equals(IdentifierType.TABLE)) { - return baseUrl + "/database/" + data.getDatabase().getId() + "/table/" + data.getTableId() + "/info?pid=" + data.getId(); - } else { - return null; - } - } - - @Named("nameSchemaMapper") - default String nameIdentifierSchemeToNameIdentifierSchemeUri(NameIdentifierSchemeTypeDto data) { - if (data == null) { - return null; - } - return switch (data) { - case ROR -> "https://ror.org/"; - case ORCID -> "https://orcid.org/"; - case GRID -> "https://grid.ac/"; - case ISNI -> "https://grid.ac/institutes/"; - }; - } - - @Named("affiliationSchemaMapper") - default String affiliationIdentifierSchemeTypeToAffiliationIdentifier(AffiliationIdentifierSchemeTypeDto data) { - if (data == null) { - return null; - } - return switch (data) { - case ROR -> "https://ror.org/"; - case GRID -> "https://grid.ac/institutes/"; - case ISNI -> "https://isni.org/"; - }; - } - -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ImageMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ImageMapper.java deleted file mode 100644 index 8ea6609f16..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ImageMapper.java +++ /dev/null @@ -1,26 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.container.image.ImageBriefDto; -import at.tuwien.api.container.image.ImageCreateDto; -import at.tuwien.api.container.image.ImageDto; -import at.tuwien.entities.container.image.ContainerImage; -import org.mapstruct.Mapper; - -import java.time.Instant; - -@Mapper(componentModel = "spring") -public interface ImageMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ImageMapper.class); - - ContainerImage createImageDtoToContainerImage(ImageCreateDto data); - - ImageBriefDto containerImageToImageBriefDto(ContainerImage data); - - ImageDto containerImageToImageDto(ContainerImage data); - - default Instant dateToInstant(String date) { - return Instant.parse(date); - } - -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/LicenseMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/LicenseMapper.java deleted file mode 100644 index 1124c4239e..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/LicenseMapper.java +++ /dev/null @@ -1,12 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.database.LicenseDto; -import at.tuwien.entities.database.License; -import org.mapstruct.Mapper; - -@Mapper(componentModel = "spring") -public interface LicenseMapper { - - LicenseDto licenseToLicenseDto(License data); - -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java index 2df2d893f7..1a41226977 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/MetadataMapper.java @@ -1,17 +1,428 @@ package at.tuwien.mapper; -import org.mapstruct.Mapper; +import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.container.ContainerBriefDto; +import at.tuwien.api.container.ContainerCreateDto; +import at.tuwien.api.container.ContainerDto; +import at.tuwien.api.container.image.ImageBriefDto; +import at.tuwien.api.container.image.ImageCreateDto; +import at.tuwien.api.container.image.ImageDto; +import at.tuwien.api.crossref.CrossrefDto; +import at.tuwien.api.database.*; +import at.tuwien.api.database.table.TableBriefDto; +import at.tuwien.api.database.table.TableDto; +import at.tuwien.api.database.table.columns.ColumnCreateDto; +import at.tuwien.api.database.table.columns.ColumnDto; +import at.tuwien.api.database.table.columns.concepts.ConceptDto; +import at.tuwien.api.database.table.columns.concepts.ConceptSaveDto; +import at.tuwien.api.database.table.columns.concepts.UnitDto; +import at.tuwien.api.database.table.columns.concepts.UnitSaveDto; +import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; +import at.tuwien.api.database.table.constraints.ConstraintsDto; +import at.tuwien.api.database.table.constraints.foreign.ForeignKeyDto; +import at.tuwien.api.database.table.constraints.foreign.ForeignKeyReferenceDto; +import at.tuwien.api.database.table.constraints.primary.PrimaryKeyDto; +import at.tuwien.api.database.table.constraints.unique.UniqueDto; +import at.tuwien.api.datacite.doi.*; +import at.tuwien.api.identifier.*; +import at.tuwien.api.identifier.ld.LdCreatorDto; +import at.tuwien.api.identifier.ld.LdDatasetDto; +import at.tuwien.api.keycloak.CredentialDto; +import at.tuwien.api.keycloak.CredentialTypeDto; +import at.tuwien.api.keycloak.UpdateCredentialsDto; +import at.tuwien.api.keycloak.UserCreateDto; +import at.tuwien.api.maintenance.BannerMessageBriefDto; +import at.tuwien.api.maintenance.BannerMessageCreateDto; +import at.tuwien.api.maintenance.BannerMessageDto; +import at.tuwien.api.maintenance.BannerMessageTypeDto; +import at.tuwien.api.orcid.OrcidDto; +import at.tuwien.api.orcid.activities.employments.affiliation.OrcidAffiliationGroupDto; +import at.tuwien.api.orcid.activities.employments.affiliation.group.OrcidEmploymentSummaryDto; +import at.tuwien.api.orcid.activities.employments.affiliation.group.summary.organization.disambiguated.OrcidDisambiguatedDto; +import at.tuwien.api.orcid.activities.employments.affiliation.group.summary.organization.disambiguated.OrcidDisambiguatedSourceTypeDto; +import at.tuwien.api.ror.RorDto; +import at.tuwien.api.semantics.EntityDto; +import at.tuwien.api.semantics.OntologyBriefDto; +import at.tuwien.api.semantics.OntologyCreateDto; +import at.tuwien.api.semantics.OntologyDto; +import at.tuwien.api.user.UserBriefDto; +import at.tuwien.api.user.UserDetailsDto; +import at.tuwien.api.user.UserDto; +import at.tuwien.api.user.external.ExternalMetadataDto; +import at.tuwien.api.user.external.ExternalResultType; +import at.tuwien.api.user.external.affiliation.ExternalAffiliationDto; +import at.tuwien.entities.container.Container; +import at.tuwien.entities.container.image.ContainerImage; +import at.tuwien.entities.database.*; +import at.tuwien.entities.database.table.Table; +import at.tuwien.entities.database.table.columns.TableColumn; +import at.tuwien.entities.database.table.columns.TableColumnConcept; +import at.tuwien.entities.database.table.columns.TableColumnUnit; +import at.tuwien.entities.database.table.constraints.Constraints; +import at.tuwien.entities.database.table.constraints.foreignKey.ForeignKey; +import at.tuwien.entities.database.table.constraints.foreignKey.ForeignKeyReference; +import at.tuwien.entities.database.table.constraints.foreignKey.ReferenceType; +import at.tuwien.entities.database.table.constraints.primaryKey.PrimaryKey; +import at.tuwien.entities.database.table.constraints.unique.Unique; +import at.tuwien.entities.identifier.*; +import at.tuwien.entities.maintenance.BannerMessage; +import at.tuwien.entities.maintenance.BannerMessageType; +import at.tuwien.entities.semantics.Ontology; +import at.tuwien.entities.user.User; +import org.mapstruct.*; +import java.text.Normalizer; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; - -@Mapper(componentModel = "spring") +@Mapper(componentModel = "spring", imports = {LinkedList.class, ExternalResultType.class}) public interface MetadataMapper { org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MetadataMapper.class); + BannerMessageDto bannerMessageToBannerMessageDto(BannerMessage data); + + BannerMessageBriefDto bannerMessageToBannerMessageBriefDto(BannerMessage data); + + BannerMessage bannerMessageCreateDtoToBannerMessage(BannerMessageCreateDto data); + + BannerMessageType bannerMessageTypeDtoToBannerMessageType(BannerMessageTypeDto data); + + @Mappings({ + @Mapping(target = "internalName", source = "name", qualifiedByName = "internalMapping") + }) + Container containerCreateRequestDtoToContainer(ContainerCreateDto data); + + ContainerDto containerToContainerDto(Container data); + + @Mappings({ + @Mapping(target = "id", source = "id") + }) + ContainerBriefDto containerToDatabaseContainerBriefDto(Container data); + + @Mappings({ + @Mapping(target = "titles", source = "."), + @Mapping(target = "publisher", source = "publisher"), + @Mapping(target = "publicationYear", source = "publicationYear"), + @Mapping(target = "publicationMonth", source = "publicationMonth"), + @Mapping(target = "publicationDay", source = "publicationDay"), + @Mapping(target = "language", source = "language"), + @Mapping(target = "creators", source = "creators"), + }) + DataCiteCreateDoi identifierToDataCiteCreateDoi(Identifier identifier); + + default DataCiteCreateDoi identifierToDataCiteCreateDoi(Identifier identifier, String url, String prefix, + DataCiteDoiEvent event) { + return addParametersToCreateDoi( + identifierToDataCiteCreateDoi(identifier), + url, + prefix, + DataCiteDoiTypes.DATASET, + event + ); + } + + DataCiteDoiTitle identifierTitleToDataCiteDoiTitle(IdentifierTitle data); + + default DataCiteDoiTitle.Type titleTypeToDataCiteDoiTitleType(TitleType data) { + if (data == null) { + return null; + } + return switch (data) { + case OTHER -> DataCiteDoiTitle.Type.OTHER; + case TRANSLATED_TITLE -> DataCiteDoiTitle.Type.TRANSLATED_TITLE; + case SUBTITLE -> DataCiteDoiTitle.Type.SUBTITLE; + case ALTERNATIVE_TITLE -> DataCiteDoiTitle.Type.ALTERNATIVE_TITLE; + }; + } + + default List<DataCiteDoiTitle> identifierToDataCiteDoiTitleList(Identifier data) { + if (data.getTitles() == null) { + return new LinkedList<>(); + } + return data.getTitles() + .stream() + .map(this::identifierTitleToDataCiteDoiTitle) + .toList(); + } + + DataCiteCreateDoi addParametersToCreateDoi(@MappingTarget DataCiteCreateDoi target, String url, String prefix, + DataCiteDoiTypes types, DataCiteDoiEvent event); + + @Mappings({ + @Mapping(target = "rights", source = "identifier"), + @Mapping(target = "rightsUri", source = "uri"), + }) + DataCiteDoiRights licenseToDoiRights(License license); + + default <T> List<T> list(T t) { + if (t == null) return null; + return List.of(t); + } + + @Mappings({ + @Mapping(target = "name", expression = "java(data.getLastname() + \", \" + data.getFirstname())"), + @Mapping(target = "givenName", source = "firstname"), + @Mapping(target = "familyName", source = "lastname"), + @Mapping(target = "nameType", expression = "java(nameTypeToDataCiteNameType(data.getNameType()))"), + @Mapping(target = "affiliation", expression = "java(list(creatorToDoiCreatorAffiliation(data)))"), + @Mapping(target = "nameIdentifier", expression = "java(list(creatorToDataCiteDoiCreatorNameIdentifier(data)))"), + }) + DataCiteDoiCreator creatorToDoiCreator(Creator data); + + DataCiteDoiCreatorNameIdentifier creatorToDataCiteDoiCreatorNameIdentifier(Creator data); + + /* keep */ + default String nameIdentifierSchemeTypeToUri(NameIdentifierSchemeType data) { + switch (data) { + case ROR -> { + return "https://ror.org/"; + } + case ORCID -> { + return "https://orcid.org/"; + } + case ISNI -> { + return "https://isni.org/isni/"; + } + case GRID -> { + return "https://www.grid.ac/"; + } + } + return null; + } + + /* keep */ + default DataCiteNameType nameTypeToDataCiteNameType(NameType data) { + if (data == null) { + return null; + } + return DataCiteNameType.valueOf(data.toString()); + } + + @Mappings({ + @Mapping(target = "name", source = "affiliation"), + @Mapping(target = "affiliationIdentifier", source = "affiliationIdentifier"), + @Mapping(target = "affiliationScheme", source = "affiliationIdentifierScheme"), + @Mapping(target = "schemeUri", source = "affiliationIdentifierSchemeUri"), + }) + DataCiteDoiCreatorAffiliation creatorToDoiCreatorAffiliation(Creator data); + + @Mappings({ + @Mapping(target = "relatedIdentifier", source = "value"), + @Mapping(target = "relatedIdentifierType", source = "type"), + @Mapping(target = "relationType", source = "relation"), + }) + DataCiteDoiRelatedIdentifier relatedIdentifierToDoiRelatedIdentifier(RelatedIdentifier relatedIdentifier); + + Date instantToDate(Instant data); + + @Mappings({ + @Mapping(target = "givenNames", source = "person.name.givenNames.value"), + @Mapping(target = "familyName", source = "person.name.familyName.value"), + @Mapping(target = "type", expression = "java(ExternalResultType.PERSONAL)"), + @Mapping(target = "affiliations", source = "activitiesSummary.employments.affiliationGroup"), + }) + ExternalMetadataDto orcidDtoToExternalMetadataDto(OrcidDto data); + + @Mappings({ + @Mapping(target = "organizationName", source = "employmentSummary.organization.name"), + @Mapping(target = "ringgoldId", expression = "java(disambiguatedOrganizationToRinggoldId(data.getEmploymentSummary().getOrganization().getDisambiguatedOrganization()))"), + }) + ExternalAffiliationDto orcidEmploymentSummaryDtoToExternalAffiliationDto(OrcidEmploymentSummaryDto data); + + default ExternalAffiliationDto orcidAffiliationGroupDtoToExternalAffiliationDto(OrcidAffiliationGroupDto data) { + if (data == null || data.getSummaries() == null || data.getSummaries().length == 0) { + return null; + } + return ExternalAffiliationDto.builder() + .organizationName(data.getSummaries()[0].getEmploymentSummary().getOrganization().getName()) + .build(); + } + + default Long disambiguatedOrganizationToRinggoldId(OrcidDisambiguatedDto data) { + if (data.getSource().equals(OrcidDisambiguatedSourceTypeDto.RINGGOLD)) { + return Long.parseLong(data.getIdentifier()); + } + return null; + } + + default ExternalMetadataDto rorDtoToExternalMetadataDto(RorDto data) { + return ExternalMetadataDto.builder() + .affiliations(new ExternalAffiliationDto[]{ + ExternalAffiliationDto.builder() + .organizationName(data.getName()) + .build()}) + .type(ExternalResultType.ORGANIZATIONAL) + .build(); + } + + default ExternalMetadataDto crossrefDtoToExternalMetadataDto(CrossrefDto data) { + return ExternalMetadataDto.builder() + .affiliations(new ExternalAffiliationDto[]{ + ExternalAffiliationDto.builder() + .crossrefFunderId(data.getId()) + .organizationName(data.getPrefLabel().getLabel().getLiteralForm().getContent()) + .build()}) + .type(ExternalResultType.ORGANIZATIONAL) + .build(); + } + + @Mappings({ + @Mapping(target = "organizationName", source = "name"), + }) + ExternalAffiliationDto rorDtoToExternalAffiliationDto(RorDto data); + + Identifier identifierDtoToIdentifier(IdentifierDto data); + + @Mappings({ + @Mapping(target = "databaseId", source = "database.id"), + }) + IdentifierDto identifierToIdentifierDto(Identifier data); + + IdentifierBriefDto identifierToIdentifierBriefDto(Identifier data); + + default IdentifierTitle identifierToIdentifierTitle(Identifier data, String lang) { + final Optional<IdentifierTitle> optional = data.getTitles() + .stream() + .filter(t -> lang == null || t.getLanguage().getName().equals(lang)) + .findFirst(); + if (optional.isEmpty()) { + log.warn("no title with language {} found", lang); + return identifierToIdentifierTitle(data, "en"); + } + return optional.get(); + } + + default IdentifierDescription identifierToIdentifierDescription(Identifier data, String lang) { + final Optional<IdentifierDescription> optional = data.getDescriptions() + .stream() + .filter(t -> lang == null || t.getLanguage().getName().equals(lang)) + .findFirst(); + if (optional.isEmpty()) { + log.warn("no description with language {} found", lang); + return identifierToIdentifierDescription(data, "en"); + } + return optional.get(); + } + + @Mappings({ + @Mapping(target = "givenName", source = "firstname"), + @Mapping(target = "familyName", source = "lastname"), + @Mapping(target = "type", expression = "java(data.getNameType().equals(NameType.PERSONAL) ? \"Person\" : \"Organization\")"), + @Mapping(target = "sameAs", source = "nameIdentifier"), + @Mapping(target = "name", source = "creatorName"), + }) + LdCreatorDto creatorToLdCreatorDto(Creator data); + + default LdDatasetDto identifierToLdDatasetDto(Identifier data, String baseUrl) { + return LdDatasetDto.builder() + .context("https://schema.org/") + .type("Dataset") + .name(identifierToIdentifierTitle(data, null) + .getTitle()) + .description(identifierToIdentifierDescription(data, null) + .getDescription()) + .url(identifierToLocationUrl(baseUrl, data)) + .identifier(List.of()) + .creator(data.getCreators() + .stream() + .map(this::creatorToLdCreatorDto) + .toList()) + .citation(identifierToLocationUrl(baseUrl, data)) + .hasPart(List.of()) + .license(data.getLicenses().isEmpty() ? null : data.getLicenses().get(0).getUri()) + .temporalCoverage(null) + .version(data.getCreated()) + .build(); + } + + Identifier identifierCreateDtoToIdentifier(IdentifierCreateDto data); + + Identifier identifierUpdateDtoToIdentifier(IdentifierSaveDto data); + + License licenseDtoToLicense(LicenseDto data); + + IdentifierTitle identifierCreateTitleDtoToIdentifierTitle(IdentifierSaveTitleDto data); + + IdentifierDescription identifierCreateDescriptionDtoToIdentifierDescription(IdentifierSaveDescriptionDto data); + + IdentifierFunder identifierFunderSaveDtoToIdentifierFunder(IdentifierFunderSaveDto data); + + IdentifierSaveDto identifierCreateDtoToIdentifierSaveDto(IdentifierCreateDto data); + + RelatedIdentifierDto relatedIdentifierToRelatedIdentifierDto(RelatedIdentifier data); + + Creator creatorDtoToCreator(CreatorDto data); + + NameIdentifierSchemeTypeDto nameIdentifierSchemeTypeToNameIdentifierSchemeTypeDto(NameIdentifierSchemeType data); + + CreatorDto creatorToCreatorDto(Creator data); + + @Mappings({ + @Mapping(target = "nameIdentifierSchemeUri", source = "nameIdentifierScheme", qualifiedByName = "nameSchemaMapper"), + @Mapping(target = "affiliationIdentifierSchemeUri", source = "affiliationIdentifierScheme", qualifiedByName = "affiliationSchemaMapper"), + }) + Creator creatorCreateDtoToCreator(CreatorSaveDto data); + + RelatedIdentifier relatedIdentifierCreateDtoToRelatedIdentifier(RelatedIdentifierSaveDto data); + + IdentifierType identifierTypeDtoToIdentifierType(IdentifierTypeDto data); + + default String identifierToLocationUrl(String baseUrl, Identifier data) { + if (data.getType().equals(IdentifierType.SUBSET)) { + return baseUrl + "/database/" + data.getDatabase().getId() + "/subset/" + data.getQueryId() + "/info?pid=" + data.getId(); + } else if (data.getType().equals(IdentifierType.DATABASE)) { + return baseUrl + "/database/" + data.getDatabase().getId() + "/info?pid=" + data.getId(); + } else if (data.getType().equals(IdentifierType.VIEW)) { + return baseUrl + "/database/" + data.getDatabase().getId() + "/view/" + data.getViewId() + "/info?pid=" + data.getId(); + } else if (data.getType().equals(IdentifierType.TABLE)) { + return baseUrl + "/database/" + data.getDatabase().getId() + "/table/" + data.getTableId() + "/info?pid=" + data.getId(); + } else { + return null; + } + } + + @Named("nameSchemaMapper") + default String nameIdentifierSchemeToNameIdentifierSchemeUri(NameIdentifierSchemeTypeDto data) { + if (data == null) { + return null; + } + return switch (data) { + case ROR -> "https://ror.org/"; + case ORCID -> "https://orcid.org/"; + case GRID -> "https://grid.ac/"; + case ISNI -> "https://grid.ac/institutes/"; + }; + } + + @Named("affiliationSchemaMapper") + default String affiliationIdentifierSchemeTypeToAffiliationIdentifier(AffiliationIdentifierSchemeTypeDto data) { + if (data == null) { + return null; + } + return switch (data) { + case ROR -> "https://ror.org/"; + case GRID -> "https://grid.ac/institutes/"; + case ISNI -> "https://isni.org/"; + }; + } + + ContainerImage createImageDtoToContainerImage(ImageCreateDto data); + + ImageBriefDto containerImageToImageBriefDto(ContainerImage data); + + ImageDto containerImageToImageDto(ContainerImage data); + + default Instant dateToInstant(String date) { + return Instant.parse(date); + } + + LicenseDto licenseToLicenseDto(License data); + default String instantToDatestamp(Instant data) { final String datestamp = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'") .withZone(ZoneId.systemDefault()) @@ -20,4 +431,455 @@ public interface MetadataMapper { return datestamp; } + @Mappings({ + @Mapping(target = "rdf", expression = "java(data.getRdfPath() != null)"), + @Mapping(target = "sparql", expression = "java(data.getSparqlEndpoint() != null)") + }) + OntologyDto ontologyToOntologyDto(Ontology data); + + @Mappings({ + @Mapping(target = "rdf", expression = "java(data.getRdfPath() != null)"), + @Mapping(target = "sparql", expression = "java(data.getSparqlEndpoint() != null)") + }) + OntologyBriefDto ontologyToOntologyBriefDto(Ontology data); + + Ontology ontologyCreateDtoToOntology(OntologyCreateDto data); + + ConceptDto tableColumnConceptToConceptDto(TableColumnConcept data); + + UnitDto tableColumnUnitToUnitDto(TableColumnUnit data); + + TableColumnUnit unitSaveDtoToTableColumnUnit(UnitSaveDto data); + + TableColumnUnit entityDtoToTableColumnUnit(EntityDto data); + + TableColumnConcept entityDtoToTableColumnConcept(EntityDto data); + + TableColumnConcept conceptSaveDtoToTableColumnConcept(ConceptSaveDto data); + + @Mappings({ + @Mapping(source = "id", target = "id"), + @Mapping(target = "name", expression = "java(data.getName())"), + @Mapping(target = "internalName", expression = "java(data.getInternalName())") + }) + TableBriefDto tableToTableBriefDto(Table data); + + default UniqueDto uniqueToUniqueDto(Unique data) { + data.getTable().setOwner(null); /* loop */ + data.getTable().setCreator(null); /* loop */ + return UniqueDto.builder() + .id(data.getId()) + .name(data.getName()) + .columns(data.getColumns() + .stream() + .map(this::tableColumnToColumnDto) + .toList()) + .table(tableToTableBriefDto(data.getTable())) + .build(); + } + + @Mappings({ + @Mapping(target = "table.owner", ignore = true), + @Mapping(target = "table.creator", ignore = true), + @Mapping(target = "table.constraints", ignore = true), + @Mapping(target = "referencedTable.owner", ignore = true), + @Mapping(target = "referencedTable.creator", ignore = true), + @Mapping(target = "referencedTable.constraints", ignore = true), + }) + ForeignKeyDto foreignKeyToForeignKeyDto(ForeignKey data); + + default ConstraintsDto constraintsToConstraintsDto(Constraints data) { + if (data == null) { + return null; + } + return ConstraintsDto.builder() + .checks(data.getChecks()) + .uniques(data.getUniques() + .stream() + .map(this::uniqueToUniqueDto) + .toList()) + .foreignKeys(data.getForeignKeys() + .stream() + .map(this::foreignKeyToForeignKeyDto) + .toList()) + .primaryKey(data.getPrimaryKey() + .stream() + .map(this::primaryKeyToPrimaryKeyDto) + .collect(Collectors.toSet())) + .build(); + } + + default TableDto customTableToTableDto(Table data) { + final TableDto table = TableDto.builder() + .id(data.getId()) + .name(data.getName()) + .internalName(data.getInternalName()) + .owner(userToUserDto(data.getOwner())) + .createdBy(data.getCreatedBy()) + .creator(userToUserDto(data.getCreator())) + .tdbid(data.getTdbid()) + .routingKey("dbrepo." + data.getTdbid() + "." + data.getId()) + .queueName(data.getQueueName()) + .isPublic(data.getDatabase().getIsPublic()) + .isVersioned(true) + .avgRowLength(data.getAvgRowLength()) + .maxDataLength(data.getMaxDataLength()) + .dataLength(data.getDataLength()) + .numRows(data.getNumRows()) + .description(data.getDescription()) + .identifiers(new LinkedList<>()) + .columns(new LinkedList<>()) + .created(data.getCreated()) + .constraints(constraintsToConstraintsDto(data.getConstraints())) + .build(); + table.getConstraints() + .getPrimaryKey() + .forEach(pk -> { + pk.getTable().setDatabaseId(data.getDatabase().getId()); + pk.getColumn().setTableId(data.getId()); + pk.getColumn().setDatabaseId(data.getDatabase().getId()); + }); + table.getConstraints() + .getUniques() + .forEach(uk -> { + uk.getTable().setDatabaseId(data.getDatabase().getId()); + uk.getColumns() + .forEach(column -> { + column.setTableId(data.getId()); + column.setDatabaseId(data.getDatabase().getId()); + }); + }); + if (data.getIdentifiers() != null) { + table.setIdentifiers(new LinkedList<>(data.getIdentifiers() + .stream() + .map(this::identifierToIdentifierDto) + .toList())); + } + if (data.getColumns() != null) { + table.setColumns(new LinkedList<>(data.getColumns() + .stream() + .map(this::tableColumnToColumnDto) + .toList())); + } + return table; + } + + @Mappings({ + @Mapping(target = "foreignKey", ignore = true), + }) + ForeignKeyReferenceDto foreignKeyReferenceToForeignKeyReferenceDto(ForeignKeyReference foreignKeyReference); + + @Mappings({ + @Mapping(target = "table", ignore = true) + }) + TableColumn columnDtoToTableColumn(ColumnDto columnDto); + + @Mappings({ + @Mapping(target = "table", ignore = true) + }) + Unique uniqueDtoToUnique(UniqueDto data); + + @Mappings({ + @Mapping(target = "ownedBy", source = "owner.id"), + }) + Table tableDtoToTable(TableDto data); + + @Mappings({ + @Mapping(target = "table.owner", ignore = true), + @Mapping(target = "table.columns", ignore = true) + }) + PrimaryKeyDto primaryKeyToPrimaryKeyDto(PrimaryKey data); + + /* keep */ + default Constraints constraintsCreateDtoToConstraints(ConstraintsCreateDto data, Database database, Table table) { + final int[] idx = new int[]{0, 0}; + final Constraints constrains = Constraints.builder() + .checks(data.getChecks()) + .uniques(data.getUniques() + .stream() + .map(uniqueList -> Unique.builder() + .name("uk_" + table.getInternalName() + "_" + idx[0]++) + .table(table) + .columns(table.getColumns() + .stream() + .filter(ukColumn -> uniqueList.stream().map(this::nameToInternalName).toList().contains(nameToInternalName(ukColumn.getInternalName()))) + .toList()) + .build()) + .toList()) + .foreignKeys(data.getForeignKeys() + .stream() + .map(fk -> { + final Optional<Table> optional = database.getTables() + .stream() + .filter(t -> t.getInternalName().equals(fk.getReferencedTable())) + .findFirst(); + if (optional.isEmpty()) { + log.error("Failed to find foreign key referenced table {} in tables: {}", fk.getReferencedTable(), database.getTables().stream().map(Table::getInternalName).toList()); + throw new IllegalArgumentException("Failed to find foreign key referenced table"); + } + return ForeignKey.builder() + .name("fk_" + table.getInternalName() + "_" + idx[1]++) + .referencedTable(optional.get()) + .references(fk.getReferencedColumns() + .stream() + .map(c -> { + final Optional<TableColumn> column = table.getColumns() + .stream() + .filter(cc -> cc.getInternalName().equals(c)) + .findFirst(); + if (column.isEmpty()) { + log.error("Failed to find foreign key column {} in columns: {}", c, table.getColumns().stream().map(TableColumn::getInternalName).toList()); + throw new IllegalArgumentException("Failed to find foreign key column"); + } + final Optional<TableColumn> referencedColumn = database.getTables() + .stream() + .filter(t -> t.getInternalName().equals(fk.getReferencedTable())) + .map(Table::getColumns) + .flatMap(List::stream) + .filter(cc -> cc.getInternalName().equals(c)) + .findFirst(); + if (referencedColumn.isEmpty()) { + log.error("Failed to find foreign key referenced column {} in referenced columns: {}", c, database.getTables().stream().filter(t -> t.getInternalName().equals(fk.getReferencedTable())).map(Table::getColumns).flatMap(List::stream).map(TableColumn::getInternalName).toList()); + throw new IllegalArgumentException("Failed to find foreign key referenced column"); + } + return ForeignKeyReference.builder() + .column(column.get()) + .referencedColumn(referencedColumn.get()) + .foreignKey(null) // set later + .build(); + }) + .toList()) + .onDelete(ReferenceType.CASCADE) + .onUpdate(ReferenceType.CASCADE) + .build(); + }) + .toList()) + .primaryKey(data.getPrimaryKey() + .stream() + .map(pk -> { + final Optional<TableColumn> optional = table.getColumns() + .stream() + .filter(c -> c.getInternalName().equals(nameToInternalName(pk))) + .findFirst(); + if (optional.isEmpty()) { + log.error("Failed to find primary key column '{}' in columns: {}", pk, table.getColumns().stream().map(TableColumn::getInternalName).toList()); + throw new IllegalArgumentException("Failed to find primary key column"); + } + return PrimaryKey.builder() + .table(table) + .column(optional.get()) + .build(); + }) + .toList()) + .build(); + constrains.getForeignKeys() + .forEach(fk -> fk.getReferences() + .forEach(r -> r.setForeignKey(fk))); + return constrains; + } + + /* keep */ + @Mappings({ + @Mapping(target = "tableId", source = "table.id"), + @Mapping(target = "databaseId", source = "table.database.id"), + @Mapping(target = "isPublic", source = "table.database.isPublic"), + @Mapping(target = "description", source = "description"), + @Mapping(target = "table", ignore = true), + @Mapping(target = "views", ignore = true) + }) + ColumnDto tableColumnToColumnDto(TableColumn data); + + @Mappings({ + @Mapping(target = "id", expression = "java(null)"), + @Mapping(target = "columnType", source = "data.type"), + @Mapping(target = "isNullAllowed", source = "data.nullAllowed"), + @Mapping(target = "name", source = "data.name"), + @Mapping(target = "autoGenerated", expression = "java(false)"), + @Mapping(target = "internalName", expression = "java(nameToInternalName(data.getName()))"), + }) + TableColumn columnCreateDtoToTableColumn(ColumnCreateDto data, ContainerImage image); + + default UpdateCredentialsDto passwordToUpdateCredentialsDto(String password) { + return UpdateCredentialsDto.builder() + .credentials(List.of(CredentialDto.builder() + .temporary(false) + .type(CredentialTypeDto.PASSWORD) + .value(password) + .build())) + .build(); + } + + default UserCreateDto signupRequestDtoToUserCreateDto(SignupRequestDto data) { + return UserCreateDto.builder() + .username(data.getUsername()) + .email(data.getEmail()) + .credentials(List.of(CredentialDto.builder() + .type(CredentialTypeDto.PASSWORD) + .temporary(false) + .value(data.getPassword()) + .build())) + .enabled(true) + .build(); + } + + /* keep */ + UserBriefDto keycloakUserDtoToUserBriefDto(at.tuwien.api.keycloak.UserDto data); + + /* keep */ + @Mappings({ + @Mapping(target = "id", expression = "java(data.getId().toString())") + }) + UserDetailsDto userDtoToUserDetailsDto(UserDto data); + + /* keep */ + @Mappings({ + @Mapping(target = "name", expression = "java(userToFullName(data))"), + @Mapping(target = "qualifiedName", expression = "java(userToQualifiedName(data))"), + }) + UserBriefDto userToUserBriefDto(User data); + + UserBriefDto userDtoToUserBriefDto(UserDto data); + + /* keep */ + @Mappings({ + @Mapping(target = "attributes.language", source = "language"), + @Mapping(target = "attributes.orcid", source = "orcid"), + @Mapping(target = "attributes.affiliation", source = "affiliation"), + @Mapping(target = "attributes.theme", source = "theme"), + @Mapping(target = "name", expression = "java(userToFullName(data))"), + @Mapping(target = "qualifiedName", expression = "java(userToQualifiedName(data))"), + }) + UserDto userToUserDto(User data); + + /* keep */ + User userDtoToUserDto(UserDto data); + + /* keep */ + @Named("userToFullName") + default String userToFullName(User data) { + final StringBuilder name = new StringBuilder(); + if (data.getFirstname() != null) { + name.append(data.getFirstname()); + } + if (data.getLastname() != null) { + name.append(!name.isEmpty() ? " " : null) + .append(data.getLastname()); + } + return name.isEmpty() ? null : name.toString() + .trim(); + } + + /* keep */ + @Named("userToQualifiedName") + default String userToQualifiedName(User data) { + final String fullname = userToFullName(data); + final StringBuilder name = new StringBuilder(); + if (fullname != null) { + name.append(fullname); + } + if (!name.isEmpty()) { + name.append(" — "); + } + name.append("@") + .append(data.getUsername()); + return name.toString() + .trim(); + } + + @Mappings({ + @Mapping(target = "database.views", ignore = true) + }) + ViewDto viewToViewDto(View data); + + ViewBriefDto viewToViewBriefDto(View data); + + @Mappings({ + @Mapping(target = "createdBy", source = "creator.id"), + }) + View viewDtoToView(ViewDto data); + + /* keep */ + @Named("internalMapping") + default String nameToInternalName(String data) { + if (data == null || data.isEmpty()) { + return data; + } + final Pattern NONLATIN = Pattern.compile("[^\\w-]"); + final Pattern WHITESPACE = Pattern.compile("[\\s]"); + String nowhitespace = WHITESPACE.matcher(data).replaceAll("_"); + String normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD); + String slug = NONLATIN.matcher(normalized).replaceAll(""); + final String name = slug.toLowerCase(Locale.ENGLISH); + log.debug("mapping name {} to internal name {}", data, name); + return name; + } + + LanguageType languageTypeDtoToLanguageType(LanguageTypeDto data); + + default DatabaseDto customDatabaseToDatabaseDto(Database data) { + if (data == null) { + return null; + } + final DatabaseDto database = DatabaseDto.builder() + .id(data.getId()) + .name(data.getName()) + .internalName(data.getInternalName()) + .description(data.getDescription()) + .exchangeName(data.getExchangeName()) + .image(data.getImage()) + .isPublic(data.getIsPublic()) + .container(containerToContainerDto(data.getContainer())) + .creator(userToUserDto(data.getCreator())) + .owner(userToUserDto(data.getOwner())) + .created(data.getCreated()) + .contact(userToUserDto(data.getContact())) + .subsets(new LinkedList<>()) + .accesses(new LinkedList<>()) + .tables(new LinkedList<>()) + .identifiers(new LinkedList<>()) + .build(); + if (data.getSubsets() != null) { + database.setSubsets(new LinkedList<>(data.getSubsets() + .stream() + .map(this::identifierToIdentifierDto) + .toList())); + } + if (data.getTables() != null) { + database.setTables(new LinkedList<>(data.getTables() + .stream() + .map(this::customTableToTableDto) + .toList())); + } + if (data.getViews() != null) { + database.setViews(new LinkedList<>(data.getViews() + .stream() + .map(this::viewToViewDto) + .toList())); + } + if (data.getAccesses() != null) { + database.setAccesses(new LinkedList<>(data.getAccesses() + .stream() + .map(this::databaseAccessToDatabaseAccessDto) + .toList())); + } + if (data.getIdentifiers() != null) { + database.setIdentifiers(new LinkedList<>(data.getIdentifiers() + .stream() + .map(this::identifierToIdentifierDto) + .toList())); + } + return database; + } + + @Mappings({ + @Mapping(target = "created", source = "created", dateFormat = "dd-MM-yyyy HH:mm"), + }) + DatabaseBriefDto databaseToDatabaseBriefDto(Database data); + + AccessType accessTypeDtoToAccessType(AccessTypeDto data); + + AccessTypeDto accessTypeToAccessTypeDto(AccessType data); + + DatabaseAccessDto databaseAccessToDatabaseAccessDto(DatabaseAccess data); + } diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/SemanticMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/SemanticMapper.java deleted file mode 100644 index 95c56e0a5c..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/SemanticMapper.java +++ /dev/null @@ -1,18 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.database.table.columns.concepts.ConceptDto; -import at.tuwien.api.database.table.columns.concepts.UnitDto; -import at.tuwien.entities.database.table.columns.TableColumnConcept; -import at.tuwien.entities.database.table.columns.TableColumnUnit; -import org.mapstruct.Mapper; - - -@Mapper(componentModel = "spring", uses = {TableMapper.class}) -public interface SemanticMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SemanticMapper.class); - - ConceptDto tableColumnConceptToConceptDto(TableColumnConcept data); - - UnitDto tableColumnUnitToUnitDto(TableColumnUnit data); -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/OntologyMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/SparqlMapper.java similarity index 66% rename from dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/OntologyMapper.java rename to dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/SparqlMapper.java index afb3265fdf..dff867970f 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/OntologyMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/SparqlMapper.java @@ -1,53 +1,15 @@ package at.tuwien.mapper; -import at.tuwien.api.database.table.columns.concepts.ConceptDto; -import at.tuwien.api.database.table.columns.concepts.ConceptSaveDto; -import at.tuwien.api.database.table.columns.concepts.UnitDto; -import at.tuwien.api.database.table.columns.concepts.UnitSaveDto; -import at.tuwien.api.semantics.EntityDto; -import at.tuwien.api.semantics.OntologyBriefDto; -import at.tuwien.api.semantics.OntologyCreateDto; -import at.tuwien.api.semantics.OntologyDto; -import at.tuwien.entities.database.table.columns.TableColumnConcept; -import at.tuwien.entities.database.table.columns.TableColumnUnit; import at.tuwien.entities.semantics.Ontology; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; import java.util.List; @Mapper(componentModel = "spring") -public interface OntologyMapper { +public interface SparqlMapper { - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OntologyMapper.class); - - @Mappings({ - @Mapping(target = "rdf", expression = "java(data.getRdfPath() != null)"), - @Mapping(target = "sparql", expression = "java(data.getSparqlEndpoint() != null)") - }) - OntologyDto ontologyToOntologyDto(Ontology data); - - @Mappings({ - @Mapping(target = "rdf", expression = "java(data.getRdfPath() != null)"), - @Mapping(target = "sparql", expression = "java(data.getSparqlEndpoint() != null)") - }) - OntologyBriefDto ontologyToOntologyBriefDto(Ontology data); - - Ontology ontologyCreateDtoToOntology(OntologyCreateDto data); - - ConceptDto tableColumnConceptToConceptDto(TableColumnConcept data); - - UnitDto tableColumnUnitToUnitDto(TableColumnUnit data); - - TableColumnUnit unitSaveDtoToTableColumnUnit(UnitSaveDto data); - - TableColumnUnit entityDtoToTableColumnUnit(EntityDto data); - - TableColumnConcept entityDtoToTableColumnConcept(EntityDto data); - - TableColumnConcept conceptSaveDtoToTableColumnConcept(ConceptSaveDto data); + org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SparqlMapper.class); default String defaultNamespaces(List<Ontology> data) { return String.join("\n", diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java deleted file mode 100644 index a63bc5c945..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java +++ /dev/null @@ -1,213 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.database.table.TableBriefDto; -import at.tuwien.api.database.table.TableDto; -import at.tuwien.api.database.table.columns.ColumnCreateDto; -import at.tuwien.api.database.table.columns.ColumnDto; -import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; -import at.tuwien.api.database.table.constraints.ConstraintsDto; -import at.tuwien.api.database.table.constraints.foreign.ForeignKeyDto; -import at.tuwien.api.database.table.constraints.foreign.ForeignKeyReferenceDto; -import at.tuwien.api.database.table.constraints.unique.UniqueDto; -import at.tuwien.entities.container.image.ContainerImage; -import at.tuwien.entities.database.Database; -import at.tuwien.entities.database.table.Table; -import at.tuwien.entities.database.table.columns.TableColumn; -import at.tuwien.entities.database.table.constraints.Constraints; -import at.tuwien.entities.database.table.constraints.foreignKey.ForeignKey; -import at.tuwien.entities.database.table.constraints.foreignKey.ForeignKeyReference; -import at.tuwien.entities.database.table.constraints.foreignKey.ReferenceType; -import at.tuwien.entities.database.table.constraints.primaryKey.PrimaryKey; -import at.tuwien.entities.database.table.constraints.unique.Unique; -import org.mapstruct.*; - -import java.text.Normalizer; -import java.util.*; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -@Mapper(componentModel = "spring", uses = {IdentifierMapper.class, UserMapper.class}, imports = {Collectors.class, LinkedList.class}) -public interface TableMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TableMapper.class); - - @Mappings({ - @Mapping(source = "id", target = "id"), - @Mapping(target = "name", expression = "java(data.getName())"), - @Mapping(target = "internalName", expression = "java(data.getInternalName())") - }) - TableBriefDto tableToTableBriefDto(Table data); - - TableBriefDto tableDtoToTableBriefDto(TableDto data); - - /* keep */ - default UniqueDto uniqueToUniqueDto(Unique data) { - return UniqueDto.builder() - .uid(data.getUid()) - .name("uk") - .columns(data.getColumns().stream().map(this::tableColumnToColumnDto).toList()) - .table(tableToTableBriefDto(data.getTable())) - .build(); - } - - @Mappings({ - @Mapping(target = "name", expression = "java(data.getName())"), - @Mapping(target = "internalName", expression = "java(data.getInternalName())"), - @Mapping(target = "queueName", expression = "java(data.getQueueName())"), - @Mapping(target = "routingKey", expression = "java(\"dbrepo.\" + data.getTdbid() + \".\" + data.getId())"), - @Mapping(target = "isPublic", source = "database.isPublic"), - }) - TableDto tableToTableDto(Table data); - - @Mappings({ - @Mapping(target = "table", ignore = true), - @Mapping(target = "referencedTable", ignore = true), - }) - ForeignKeyDto foreignKeyToForeignKeyDto(ForeignKey foreignKey); - - @Mappings({ - @Mapping(target = "foreignKey", ignore = true), - }) - ForeignKeyReferenceDto foreignKeyReferenceToForeignKeyReferenceDto(ForeignKeyReference foreignKeyReference); - - @Mappings({ - @Mapping(target = "table", ignore = true) - }) - TableColumn columnDtoToTableColumn(ColumnDto columnDto); - - @Mappings({ - @Mapping(target = "table", ignore = true) - }) - Unique uniqueDtoToUnique(UniqueDto data); - - @Mappings({ - @Mapping(target = "constraints.primaryKey", expression = "java(new LinkedList<>())"), - @Mapping(target = "ownedBy", source = "owner.id"), - }) - Table tableDtoToTable(TableDto data); - - /* keep */ - default Constraints constraintsCreateDtoToConstraints(ConstraintsCreateDto data, Database database, Table table) { - final int[] idx = new int[]{0, 0}; - final Constraints constrains = Constraints.builder() - .checks(data.getChecks()) - .uniques(data.getUniques() - .stream() - .map(uniqueList -> Unique.builder() - .name("uk_" + table.getInternalName() + "_" + idx[0]++) - .table(table) - .columns(table.getColumns() - .stream() - .filter(ukColumn -> uniqueList.stream().map(this::nameToInternalName).toList().contains(nameToInternalName(ukColumn.getInternalName()))) - .toList()) - .build()) - .toList()) - .foreignKeys(data.getForeignKeys() - .stream() - .map(fk -> { - final Optional<Table> optional = database.getTables() - .stream() - .filter(t -> t.getInternalName().equals(fk.getReferencedTable())) - .findFirst(); - if (optional.isEmpty()) { - log.error("Failed to find foreign key referenced table {} in tables: {}", fk.getReferencedTable(), database.getTables().stream().map(Table::getInternalName).toList()); - throw new IllegalArgumentException("Failed to find foreign key referenced table"); - } - return ForeignKey.builder() - .name("fk_" + table.getInternalName() + "_" + idx[1]++) - .referencedTable(optional.get()) - .references(fk.getReferencedColumns() - .stream() - .map(c -> { - final Optional<TableColumn> column = table.getColumns() - .stream() - .filter(cc -> cc.getInternalName().equals(c)) - .findFirst(); - if (column.isEmpty()) { - log.error("Failed to find foreign key column {} in columns: {}", c, table.getColumns().stream().map(TableColumn::getInternalName).toList()); - throw new IllegalArgumentException("Failed to find foreign key column"); - } - final Optional<TableColumn> referencedColumn = database.getTables() - .stream() - .filter(t -> t.getInternalName().equals(fk.getReferencedTable())) - .map(Table::getColumns) - .flatMap(List::stream) - .filter(cc -> cc.getInternalName().equals(c)) - .findFirst(); - if (referencedColumn.isEmpty()) { - log.error("Failed to find foreign key referenced column {} in referenced columns: {}", c, database.getTables().stream().filter(t -> t.getInternalName().equals(fk.getReferencedTable())).map(Table::getColumns).flatMap(List::stream).map(TableColumn::getInternalName).toList()); - throw new IllegalArgumentException("Failed to find foreign key referenced column"); - } - return ForeignKeyReference.builder() - .column(column.get()) - .referencedColumn(referencedColumn.get()) - .foreignKey(null) // set later - .build(); - }) - .toList()) - .onDelete(ReferenceType.CASCADE) - .onUpdate(ReferenceType.CASCADE) - .build(); - }) - .toList()) - .primaryKey(data.getPrimaryKey() - .stream() - .map(pk -> { - final Optional<TableColumn> optional = table.getColumns() - .stream() - .filter(c -> c.getInternalName().equals(nameToInternalName(pk))) - .findFirst(); - if (optional.isEmpty()) { - log.error("Failed to find primary key column '{}' in columns: {}", pk, table.getColumns().stream().map(TableColumn::getInternalName).toList()); - throw new IllegalArgumentException("Failed to find primary key column"); - } - return PrimaryKey.builder() - .table(table) - .column(optional.get()) - .build(); - }) - .toList()) - .build(); - constrains.getForeignKeys() - .forEach(fk -> fk.getReferences() - .forEach(r -> r.setForeignKey(fk))); - return constrains; - } - - /* keep */ - @Mappings({ - @Mapping(target = "tableId", source = "table.id"), - @Mapping(target = "databaseId", source = "table.database.id"), - @Mapping(target = "isPublic", source = "table.database.isPublic"), - @Mapping(target = "description", source = "description"), - @Mapping(target = "table.columns", ignore = true), - @Mapping(target = "table.constraints", ignore = true), - @Mapping(target = "views", ignore = true) - }) - ColumnDto tableColumnToColumnDto(TableColumn data); - - @Named("internalMapping") - default String nameToInternalName(String data) { - if (data == null || data.isEmpty()) { - return data; - } - final Pattern NONLATIN = Pattern.compile("[^\\w-]"); - final Pattern WHITESPACE = Pattern.compile("[\\s]"); - String nowhitespace = WHITESPACE.matcher(data).replaceAll("_"); - String normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD); - String slug = NONLATIN.matcher(normalized).replaceAll("_") - .replaceAll("-", "_"); - return slug.toLowerCase(Locale.ENGLISH); - } - - @Mappings({ - @Mapping(target = "id", expression = "java(null)"), - @Mapping(target = "columnType", source = "data.type"), - @Mapping(target = "isNullAllowed", source = "data.nullAllowed"), - @Mapping(target = "name", source = "data.name"), - @Mapping(target = "autoGenerated", expression = "java(false)"), - @Mapping(target = "internalName", expression = "java(nameToInternalName(data.getName()))"), - }) - TableColumn columnCreateDtoToTableColumn(ColumnCreateDto data, ContainerImage image); - -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/UserMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/UserMapper.java deleted file mode 100644 index cf1d2d2ac7..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/UserMapper.java +++ /dev/null @@ -1,113 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.keycloak.*; -import at.tuwien.api.user.*; -import at.tuwien.api.user.UserDto; -import at.tuwien.entities.user.User; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; -import org.mapstruct.Named; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -@Mapper(componentModel = "spring") -public interface UserMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(UserMapper.class); - - default UpdateCredentialsDto passwordToUpdateCredentialsDto(String password) { - return UpdateCredentialsDto.builder() - .credentials(List.of(CredentialDto.builder() - .temporary(false) - .type(CredentialTypeDto.PASSWORD) - .value(password) - .build())) - .build(); - } - - default UserCreateDto signupRequestDtoToUserCreateDto(SignupRequestDto data) { - return UserCreateDto.builder() - .username(data.getUsername()) - .email(data.getEmail()) - .credentials(List.of(CredentialDto.builder() - .type(CredentialTypeDto.PASSWORD) - .temporary(false) - .value(data.getPassword()) - .build())) - .enabled(true) - .build(); - } - - /* keep */ - UserBriefDto keycloakUserDtoToUserBriefDto(at.tuwien.api.keycloak.UserDto data); - - /* keep */ - @Mappings({ - @Mapping(target = "id", expression = "java(data.getId().toString())") - }) - UserDetailsDto userDtoToUserDetailsDto(UserDto data); - - /* keep */ - @Mappings({ - @Mapping(target = "name", expression = "java(userToFullName(data))"), - @Mapping(target = "qualifiedName", expression = "java(userToQualifiedName(data))"), - }) - UserBriefDto userToUserBriefDto(User data); - - UserBriefDto userDtoToUserBriefDto(UserDto data); - - /* keep */ - @Mappings({ - @Mapping(target = "attributes.language", source = "language"), - @Mapping(target = "attributes.orcid", source = "orcid"), - @Mapping(target = "attributes.affiliation", source = "affiliation"), - @Mapping(target = "attributes.theme", source = "theme"), - @Mapping(target = "name", expression = "java(userToFullName(data))"), - @Mapping(target = "qualifiedName", expression = "java(userToQualifiedName(data))"), - }) - UserDto userToUserDto(User data); - - /* keep */ - User userDtoToUserDto(UserDto data); - - /* keep */ - @Named("userToFullName") - default String userToFullName(User data) { - final StringBuilder name = new StringBuilder(); - if (data.getFirstname() != null) { - name.append(data.getFirstname()); - } - if (data.getLastname() != null) { - name.append(!name.isEmpty() ? " " : null) - .append(data.getLastname()); - } - return name.isEmpty() ? null : name.toString() - .trim(); - } - - /* keep */ - @Named("userToQualifiedName") - default String userToQualifiedName(User data) { - final String fullname = userToFullName(data); - final StringBuilder name = new StringBuilder(); - if (fullname != null) { - name.append(fullname); - } - if (!name.isEmpty()) { - name.append(" — "); - } - name.append("@") - .append(data.getUsername()); - return name.toString() - .trim(); - } - - User signupRequestDtoToUser(SignupRequestDto data); - -} diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ViewMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ViewMapper.java deleted file mode 100644 index 7d8f2416b1..0000000000 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ViewMapper.java +++ /dev/null @@ -1,48 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.database.ViewBriefDto; -import at.tuwien.api.database.ViewDto; -import at.tuwien.entities.database.View; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; -import org.mapstruct.Named; - -import java.text.Normalizer; -import java.util.Locale; -import java.util.regex.Pattern; - -@Mapper(componentModel = "spring", uses = {ContainerMapper.class, UserMapper.class, TableMapper.class, - IdentifierMapper.class}) -public interface ViewMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ViewMapper.class); - - @Named("internalNameMapping") - default String nameToInternalName(String data) { - if (data == null || data.isEmpty()) { - return data; - } - final Pattern NONLATIN = Pattern.compile("[^\\w-]"); - final Pattern WHITESPACE = Pattern.compile("[\\s]"); - String nowhitespace = WHITESPACE.matcher(data).replaceAll("_"); - String normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD); - String slug = NONLATIN.matcher(normalized).replaceAll(""); - return slug.toLowerCase(Locale.ENGLISH); - } - - @Mappings({ - @Mapping(target = "database.views", ignore = true), - @Mapping(target = "database.tables", ignore = true), - @Mapping(target = "database.identifiers", ignore = true), - }) - ViewDto viewToViewDto(View data); - - ViewBriefDto viewToViewBriefDto(View data); - - @Mappings({ - @Mapping(target = "createdBy", source = "creator.id"), - }) - View viewDtoToView(ViewDto data); - -} diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java index ea66257cc1..17c1e20978 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java @@ -7,7 +7,7 @@ import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.user.User; import at.tuwien.exception.*; -import at.tuwien.mapper.DatabaseMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.AccessService; import at.tuwien.service.DatabaseService; import at.tuwien.service.UserService; @@ -40,11 +40,11 @@ public class AccessEndpoint { private final UserService userService; private final AccessService accessService; - private final DatabaseMapper databaseMapper; + private final MetadataMapper databaseMapper; private final DatabaseService databaseService; @Autowired - public AccessEndpoint(UserService userService, AccessService accessService, DatabaseMapper databaseMapper, + public AccessEndpoint(UserService userService, AccessService accessService, MetadataMapper databaseMapper, DatabaseService databaseService) { this.userService = userService; this.accessService = accessService; diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ConceptEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ConceptEndpoint.java index 2d002e8449..cb58e62def 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ConceptEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ConceptEndpoint.java @@ -1,7 +1,7 @@ package at.tuwien.endpoints; import at.tuwien.api.database.table.columns.concepts.ConceptDto; -import at.tuwien.mapper.SemanticMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.ConceptService; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -26,12 +25,12 @@ import java.util.List; public class ConceptEndpoint { private final ConceptService conceptService; - private final SemanticMapper semanticMapper; + private final MetadataMapper metadataMapper; @Autowired - public ConceptEndpoint(ConceptService conceptService, SemanticMapper semanticMapper) { + public ConceptEndpoint(ConceptService conceptService, MetadataMapper metadataMapper) { this.conceptService = conceptService; - this.semanticMapper = semanticMapper; + this.metadataMapper = metadataMapper; } @GetMapping @@ -49,7 +48,7 @@ public class ConceptEndpoint { log.debug("endpoint list concepts"); final List<ConceptDto> dtos = conceptService.findAll() .stream() - .map(semanticMapper::tableColumnConceptToConceptDto) + .map(metadataMapper::tableColumnConceptToConceptDto) .toList(); log.trace("Find all concepts resulted in dtos {}", dtos); return ResponseEntity.ok() diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java index 02f5a00f2b..9d6e24801b 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java @@ -8,7 +8,7 @@ import at.tuwien.entities.container.Container; import at.tuwien.exception.ContainerAlreadyExistsException; import at.tuwien.exception.ContainerNotFoundException; import at.tuwien.exception.ImageNotFoundException; -import at.tuwien.mapper.ContainerMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.ContainerService; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -42,12 +42,12 @@ import java.util.stream.Collectors; @RequestMapping(path = "/api/container") public class ContainerEndpoint { - private final ContainerMapper containerMapper; + private final MetadataMapper metadataMapper; private final ContainerService containerService; @Autowired - public ContainerEndpoint(ContainerService containerService, ContainerMapper containerMapper) { - this.containerMapper = containerMapper; + public ContainerEndpoint(ContainerService containerService, MetadataMapper metadataMapper) { + this.metadataMapper = metadataMapper; this.containerService = containerService; } @@ -66,7 +66,7 @@ public class ContainerEndpoint { log.debug("endpoint find all containers, limit={}", limit); final List<Container> containers = containerService.getAll(limit); final List<ContainerBriefDto> dtos = containers.stream() - .map(containerMapper::containerToDatabaseContainerBriefDto) + .map(metadataMapper::containerToDatabaseContainerBriefDto) .collect(Collectors.toList()); log.trace("find all containers resulted in containers {}", dtos); return ResponseEntity.ok() @@ -99,7 +99,7 @@ public class ContainerEndpoint { throws ImageNotFoundException, ContainerAlreadyExistsException { log.debug("endpoint create container, data={}", data); final Container container = containerService.create(data); - final ContainerBriefDto dto = containerMapper.containerToDatabaseContainerBriefDto(container); + final ContainerBriefDto dto = metadataMapper.containerToDatabaseContainerBriefDto(container); log.trace("create container resulted in container {}", dto); return ResponseEntity.status(HttpStatus.CREATED) .body(dto); @@ -126,7 +126,7 @@ public class ContainerEndpoint { throws ContainerNotFoundException { log.debug("endpoint find container, containerId={}", containerId); final Container container = containerService.find(containerId); - final ContainerDto dto = containerMapper.containerToContainerDto(container); + final ContainerDto dto = metadataMapper.containerToContainerDto(container); log.trace("find container resulted in container {}", dto); final HttpHeaders headers = new HttpHeaders(); if (principal != null) { diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java index b364e2dcfc..b5248ed232 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java @@ -8,7 +8,7 @@ import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.user.User; import at.tuwien.exception.*; -import at.tuwien.mapper.DatabaseMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.*; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -45,13 +45,13 @@ public class DatabaseEndpoint { private final RabbitConfig rabbitConfig; private final AccessService accessService; private final BrokerService brokerService; - private final DatabaseMapper databaseMapper; + private final MetadataMapper databaseMapper; private final StorageService storageService; private final DatabaseService databaseService; @Autowired public DatabaseEndpoint(UserService userService, RabbitConfig rabbitConfig, AccessService accessService, - BrokerService brokerService, DatabaseMapper databaseMapper, + BrokerService brokerService, MetadataMapper databaseMapper, StorageService storageService, DatabaseService databaseService) { this.userService = userService; this.rabbitConfig = rabbitConfig; @@ -146,7 +146,7 @@ public class DatabaseEndpoint { log.debug("endpoint create database, data.name={}", data.getName()); final User user = userService.findByUsername(principal.getName()); final Database database = databaseService.create(data, user); - final DatabaseDto dto = databaseMapper.databaseToDatabaseDto(database); + final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(database); return ResponseEntity.status(HttpStatus.CREATED) .body(dto); } @@ -198,7 +198,7 @@ public class DatabaseEndpoint { log.error("Failed to refresh database tables metadata: not owner"); throw new NotAllowedException("Failed to refresh tables metadata: not owner"); } - final DatabaseDto dto = databaseMapper.databaseToDatabaseDto(databaseService.updateTableMetadata(database)); + final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(databaseService.updateTableMetadata(database)); return ResponseEntity.ok(dto); } @@ -244,7 +244,7 @@ public class DatabaseEndpoint { log.error("Failed to refresh database views metadata: not owner"); throw new NotAllowedException("Failed to refresh database views metadata: not owner"); } - final DatabaseDto dto = databaseMapper.databaseToDatabaseDto(databaseService.updateViewMetadata(database)); + final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(databaseService.updateViewMetadata(database)); return ResponseEntity.ok(dto); } @@ -290,7 +290,7 @@ public class DatabaseEndpoint { log.error("Failed to modify database visibility: not owner"); throw new NotAllowedException("Failed to modify database visibility: not owner"); } - final DatabaseDto dto = databaseMapper.databaseToDatabaseDto(databaseService.modifyVisibility(database, data)); + final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(databaseService.modifyVisibility(database, data)); return ResponseEntity.accepted() .body(dto); } @@ -340,7 +340,7 @@ public class DatabaseEndpoint { log.error("Failed to transfer database: not owner"); throw new NotAllowedException("Failed to transfer database: not owner"); } - final DatabaseDto dto = databaseMapper.databaseToDatabaseDto(databaseService.modifyOwner(database, newOwner)); + final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(databaseService.modifyOwner(database, newOwner)); return ResponseEntity.accepted() .body(dto); } @@ -399,7 +399,7 @@ public class DatabaseEndpoint { if (data.getKey() != null) { image = storageService.getBytes(data.getKey()); } - dto = databaseMapper.databaseToDatabaseDto(databaseService.modifyImage(database, image)); + dto = databaseMapper.customDatabaseToDatabaseDto(databaseService.modifyImage(database, image)); return ResponseEntity.accepted() .body(dto); } @@ -435,7 +435,7 @@ public class DatabaseEndpoint { ServiceConnectionException, DatabaseNotFoundException, ExchangeNotFoundException { log.debug("endpoint find database, databaseId={}", databaseId); final Database database = databaseService.findById(databaseId); - final DatabaseDto dto = databaseMapper.databaseToDatabaseDto(database); + final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(database); if (database.getOwner().equals(principal)) { log.debug("current logged-in user is also the owner: additionally load access list"); /* only owner sees the access rights */ diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java index 726d118f13..c1bf712840 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java @@ -16,7 +16,7 @@ import at.tuwien.entities.identifier.IdentifierType; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.DataServiceGateway; -import at.tuwien.mapper.IdentifierMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.*; import at.tuwien.utils.UserUtil; import at.tuwien.validation.EndpointValidator; @@ -57,27 +57,27 @@ public class IdentifierEndpoint { private final TableService tableService; private final AccessService accessService; private final EndpointConfig endpointConfig; + private final MetadataMapper metadataMapper; private final DatabaseService databaseService; private final MetadataService metadataService; - private final IdentifierMapper identifierMapper; private final EndpointValidator endpointValidator; private final IdentifierService identifierService; private final DataServiceGateway dataServiceGateway; @Autowired public IdentifierEndpoint(UserService userService, ViewService viewService, TableService tableService, - AccessService accessService, EndpointConfig endpointConfig, + AccessService accessService, EndpointConfig endpointConfig, MetadataMapper metadataMapper, DatabaseService databaseService, MetadataService metadataService, - IdentifierMapper identifierMapper, EndpointValidator endpointValidator, - IdentifierService identifierService, DataServiceGateway dataServiceGateway) { + EndpointValidator endpointValidator, IdentifierService identifierService, + DataServiceGateway dataServiceGateway) { this.userService = userService; this.viewService = viewService; this.tableService = tableService; this.accessService = accessService; this.endpointConfig = endpointConfig; + this.metadataMapper = metadataMapper; this.databaseService = databaseService; this.metadataService = metadataService; - this.identifierMapper = identifierMapper; this.endpointValidator = endpointValidator; this.identifierService = identifierService; this.dataServiceGateway = dataServiceGateway; @@ -121,14 +121,14 @@ public class IdentifierEndpoint { case "application/json": log.trace("accept header matches json"); final List<IdentifierDto> resource1 = identifiers.stream() - .map(identifierMapper::identifierToIdentifierDto) + .map(metadataMapper::identifierToIdentifierDto) .toList(); log.debug("find identifier resulted in identifiers {}", resource1); return ResponseEntity.ok(resource1); case "application/ld+json": log.trace("accept header matches json-ld"); final List<LdDatasetDto> resource2 = identifiers.stream() - .map(i -> identifierMapper.identifierToLdDatasetDto(i, endpointConfig.getWebsiteUrl())) + .map(i -> metadataMapper.identifierToLdDatasetDto(i, endpointConfig.getWebsiteUrl())) .toList(); log.debug("find identifier resulted in identifiers {}", resource2); return ResponseEntity.ok(resource2); @@ -209,12 +209,12 @@ public class IdentifierEndpoint { switch (accept) { case "application/json": log.trace("accept header matches json"); - final IdentifierDto resource1 = identifierMapper.identifierToIdentifierDto(identifier); + final IdentifierDto resource1 = metadataMapper.identifierToIdentifierDto(identifier); log.debug("find identifier resulted in identifier {}", resource1); return ResponseEntity.ok(resource1); case "application/ld+json": log.trace("accept header matches json-ld"); - final LdDatasetDto resource2 = identifierMapper.identifierToLdDatasetDto(identifier, endpointConfig.getWebsiteUrl()); + final LdDatasetDto resource2 = metadataMapper.identifierToLdDatasetDto(identifier, endpointConfig.getWebsiteUrl()); log.debug("find identifier resulted in identifier {}", resource2); return ResponseEntity.ok(resource2); case "text/csv": @@ -253,7 +253,7 @@ public class IdentifierEndpoint { log.trace("no accept header present"); } final HttpHeaders headers = new HttpHeaders(); - final String url = identifierMapper.identifierToLocationUrl(endpointConfig.getWebsiteUrl(), identifier); + final String url = metadataMapper.identifierToLocationUrl(endpointConfig.getWebsiteUrl(), identifier); headers.add("Location", url); log.debug("find identifier resulted in http redirect, headers={}, url={}", headers, url); return ResponseEntity.status(HttpStatus.MOVED_PERMANENTLY) @@ -353,7 +353,7 @@ public class IdentifierEndpoint { log.debug("endpoint publish identifier, identifierId={}", identifierId); final Identifier identifier = identifierService.find(identifierId); return ResponseEntity.status(HttpStatus.CREATED) - .body(identifierMapper.identifierToIdentifierDto(identifierService.publish(identifierId))); + .body(metadataMapper.identifierToIdentifierDto(identifierService.publish(identifierId))); } @PutMapping("/{identifierId}") @@ -478,7 +478,7 @@ public class IdentifierEndpoint { } } return ResponseEntity.accepted() - .body(identifierMapper.identifierToIdentifierDto(identifierService.save(database, user, data))); + .body(metadataMapper.identifierToIdentifierDto(identifierService.save(database, user, data))); } @PostMapping @@ -544,7 +544,7 @@ public class IdentifierEndpoint { } final Identifier identifier = identifierService.create(database, user, data); return ResponseEntity.status(HttpStatus.CREATED) - .body(identifierMapper.identifierToIdentifierDto(identifier)); + .body(metadataMapper.identifierToIdentifierDto(identifier)); } @GetMapping("/retrieve") diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java index 44b25250a8..d295cc7a11 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java @@ -9,7 +9,7 @@ import at.tuwien.entities.container.image.ContainerImage; import at.tuwien.exception.ImageAlreadyExistsException; import at.tuwien.exception.ImageInvalidException; import at.tuwien.exception.ImageNotFoundException; -import at.tuwien.mapper.ImageMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.impl.ImageServiceImpl; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -40,13 +40,13 @@ import java.util.stream.Collectors; @RequestMapping(path = "/api/image") public class ImageEndpoint { + private final MetadataMapper metadataMapper; private final ImageServiceImpl imageService; - private final ImageMapper imageMapper; @Autowired - public ImageEndpoint(ImageServiceImpl imageService, ImageMapper imageMapper) { + public ImageEndpoint(ImageServiceImpl imageService, MetadataMapper metadataMapper) { this.imageService = imageService; - this.imageMapper = imageMapper; + this.metadataMapper = metadataMapper; } @GetMapping @@ -65,7 +65,7 @@ public class ImageEndpoint { final List<ContainerImage> containers = imageService.getAll(); return ResponseEntity.ok() .body(containers.stream() - .map(imageMapper::containerImageToImageBriefDto) + .map(metadataMapper::containerImageToImageBriefDto) .collect(Collectors.toList())); } @@ -100,7 +100,7 @@ public class ImageEndpoint { throw new ImageInvalidException("Failed to create image, default port is null"); } final ContainerImage image = imageService.create(data, principal); - final ImageDto dto = imageMapper.containerImageToImageDto(image); + final ImageDto dto = metadataMapper.containerImageToImageDto(image); log.trace("create image resulted in image {}", dto); return ResponseEntity.status(HttpStatus.CREATED) .body(dto); @@ -125,7 +125,7 @@ public class ImageEndpoint { public ResponseEntity<ImageDto> findById(@NotNull @PathVariable("imageId") Long imageId) throws ImageNotFoundException { log.debug("endpoint find image, id={}", imageId); final ContainerImage image = imageService.find(imageId); - final ImageDto dto = imageMapper.containerImageToImageDto(image); + final ImageDto dto = metadataMapper.containerImageToImageDto(image); log.trace("find image resulted in image {}", dto); return ResponseEntity.ok() .body(dto); @@ -154,7 +154,7 @@ public class ImageEndpoint { log.debug("endpoint update image, id={}, changeDto={}", imageId, changeDto); ContainerImage image = imageService.find(imageId); image = imageService.update(image, changeDto); - final ImageDto dto = imageMapper.containerImageToImageDto(image); + final ImageDto dto = metadataMapper.containerImageToImageDto(image); log.trace("update image resulted in image {}", dto); return ResponseEntity.accepted() .body(dto); diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java index dd81274a29..e2b47905c9 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java @@ -1,7 +1,7 @@ package at.tuwien.endpoints; import at.tuwien.api.database.LicenseDto; -import at.tuwien.mapper.LicenseMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.LicenseService; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -29,13 +29,13 @@ import java.util.stream.Collectors; @RequestMapping(path = "/api/license") public class LicenseEndpoint { - private final LicenseMapper licenseMapper; private final LicenseService licenseService; + private final MetadataMapper metadataMapper; @Autowired - public LicenseEndpoint(LicenseMapper licenseMapper, LicenseService licenseService) { - this.licenseMapper = licenseMapper; + public LicenseEndpoint(LicenseService licenseService, MetadataMapper metadataMapper) { this.licenseService = licenseService; + this.metadataMapper = metadataMapper; } @GetMapping @@ -53,7 +53,7 @@ public class LicenseEndpoint { log.debug("endpoint list licenses"); final List<LicenseDto> licenses = licenseService.findAll() .stream() - .map(licenseMapper::licenseToLicenseDto) + .map(metadataMapper::licenseToLicenseDto) .collect(Collectors.toList()); log.trace("list licenses resulted in licenses {}", licenses); return ResponseEntity.status(HttpStatus.OK) diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MessageEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MessageEndpoint.java index bcff92bc49..62677967b0 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MessageEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MessageEndpoint.java @@ -7,7 +7,7 @@ import at.tuwien.api.maintenance.BannerMessageDto; import at.tuwien.api.maintenance.BannerMessageUpdateDto; import at.tuwien.entities.maintenance.BannerMessage; import at.tuwien.exception.MessageNotFoundException; -import at.tuwien.mapper.BannerMessageMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.BannerMessageService; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -34,12 +34,12 @@ import java.util.List; @RequestMapping(path = "/api/message") public class MessageEndpoint { - private final BannerMessageMapper bannerMessageMapper; + private final MetadataMapper metadataMapper; private final BannerMessageService bannerMessageService; @Autowired - public MessageEndpoint(BannerMessageMapper bannerMessageMapper, BannerMessageService bannerMessageService) { - this.bannerMessageMapper = bannerMessageMapper; + public MessageEndpoint(MetadataMapper metadataMapper, BannerMessageService bannerMessageService) { + this.metadataMapper = metadataMapper; this.bannerMessageService = bannerMessageService; } @@ -59,12 +59,12 @@ public class MessageEndpoint { if (filter.equals("active")) { dtos = bannerMessageService.getActive() .stream() - .map(bannerMessageMapper::bannerMessageToBannerMessageDto) + .map(metadataMapper::bannerMessageToBannerMessageDto) .toList(); } else { dtos = bannerMessageService.findAll() .stream() - .map(bannerMessageMapper::bannerMessageToBannerMessageDto) + .map(metadataMapper::bannerMessageToBannerMessageDto) .toList(); } log.trace("list maintenance messages results in dtos {}", dtos); @@ -89,7 +89,7 @@ public class MessageEndpoint { public ResponseEntity<BannerMessageDto> find(@NotNull @PathVariable("messageId") Long messageId) throws MessageNotFoundException { log.debug("endpoint find one maintenance messages"); - final BannerMessageDto dto = bannerMessageMapper.bannerMessageToBannerMessageDto(bannerMessageService.find(messageId)); + final BannerMessageDto dto = metadataMapper.bannerMessageToBannerMessageDto(bannerMessageService.find(messageId)); log.trace("find one maintenance message results in dto {}", dto); return ResponseEntity.ok(dto); } @@ -107,7 +107,7 @@ public class MessageEndpoint { }) public ResponseEntity<BannerMessageDto> create(@Valid @RequestBody BannerMessageCreateDto data) { log.debug("endpoint create maintenance message, data={}", data); - final BannerMessageDto dto = bannerMessageMapper.bannerMessageToBannerMessageDto(bannerMessageService.create(data)); + final BannerMessageDto dto = metadataMapper.bannerMessageToBannerMessageDto(bannerMessageService.create(data)); log.trace("create maintenance message results in dto {}", dto); return ResponseEntity.status(HttpStatus.CREATED) .body(dto); @@ -134,7 +134,7 @@ public class MessageEndpoint { throws MessageNotFoundException { log.debug("endpoint update maintenance message, messageId={}, data={}", messageId, data); final BannerMessage message = bannerMessageService.find(messageId); - final BannerMessageDto dto = bannerMessageMapper.bannerMessageToBannerMessageDto(bannerMessageService.update(message, data)); + final BannerMessageDto dto = metadataMapper.bannerMessageToBannerMessageDto(bannerMessageService.update(message, data)); log.trace("update maintenance message results in dto {}", dto); return ResponseEntity.status(HttpStatus.ACCEPTED) .body(dto); diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java index d9a1b2c50b..8d626db323 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java @@ -4,7 +4,8 @@ import at.tuwien.api.error.ApiErrorDto; import at.tuwien.api.semantics.*; import at.tuwien.entities.semantics.Ontology; import at.tuwien.exception.*; -import at.tuwien.mapper.OntologyMapper; +import at.tuwien.mapper.MetadataMapper; +import at.tuwien.mapper.SparqlMapper; import at.tuwien.service.EntityService; import at.tuwien.service.OntologyService; import io.micrometer.observation.annotation.Observed; @@ -33,15 +34,16 @@ import java.util.List; @RequestMapping(path = "/api/ontology") public class OntologyEndpoint { - private final OntologyMapper ontologyMapper; - private final OntologyService ontologyService; private final EntityService entityService; + private final MetadataMapper metadataMapper; + private final OntologyService ontologyService; @Autowired - public OntologyEndpoint(OntologyMapper ontologyMapper, OntologyService ontologyService, EntityService entityService) { - this.ontologyMapper = ontologyMapper; - this.ontologyService = ontologyService; + public OntologyEndpoint(EntityService entityService, MetadataMapper metadataMapper, + OntologyService ontologyService) { this.entityService = entityService; + this.metadataMapper = metadataMapper; + this.ontologyService = ontologyService; } @GetMapping @@ -58,7 +60,7 @@ public class OntologyEndpoint { log.debug("endpoint find all ontologies"); final List<OntologyBriefDto> dtos = ontologyService.findAll() .stream() - .map(ontologyMapper::ontologyToOntologyBriefDto) + .map(metadataMapper::ontologyToOntologyBriefDto) .toList(); log.trace("create ontology resulted in dtos {}", dtos); return ResponseEntity.ok(dtos); @@ -82,7 +84,7 @@ public class OntologyEndpoint { public ResponseEntity<OntologyDto> find(@NotNull @PathVariable("ontologyId") Long ontologyId) throws OntologyNotFoundException { log.debug("endpoint find all ontologies, ontologyId={}", ontologyId); - final OntologyDto dto = ontologyMapper.ontologyToOntologyDto(ontologyService.find(ontologyId)); + final OntologyDto dto = metadataMapper.ontologyToOntologyDto(ontologyService.find(ontologyId)); log.trace("create ontology resulted in dto {}", dto); return ResponseEntity.ok(dto); } @@ -101,7 +103,7 @@ public class OntologyEndpoint { public ResponseEntity<OntologyDto> create(@NotNull @Valid @RequestBody OntologyCreateDto data, @NotNull Principal principal) { log.debug("endpoint create ontology, data={}", data); - final OntologyDto dto = ontologyMapper.ontologyToOntologyDto(ontologyService.create(data, principal)); + final OntologyDto dto = metadataMapper.ontologyToOntologyDto(ontologyService.create(data, principal)); log.trace("create ontology resulted in dto {}", dto); return ResponseEntity.status(HttpStatus.CREATED) .body(dto); @@ -128,7 +130,7 @@ public class OntologyEndpoint { throws OntologyNotFoundException { log.debug("endpoint update ontology, data={}", data); final Ontology ontology = ontologyService.find(ontologyId); - final OntologyDto dto = ontologyMapper.ontologyToOntologyDto(ontologyService.update(ontology, data)); + final OntologyDto dto = metadataMapper.ontologyToOntologyDto(ontologyService.update(ontology, data)); log.trace("update ontology resulted in dto {}", dto); return ResponseEntity.accepted() .body(dto); diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index 6e008287ba..97f7207f0b 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -18,7 +18,7 @@ import at.tuwien.entities.database.table.Table; import at.tuwien.entities.database.table.columns.TableColumn; import at.tuwien.entities.user.User; import at.tuwien.exception.*; -import at.tuwien.mapper.TableMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.*; import at.tuwien.utils.UserUtil; import at.tuwien.validation.EndpointValidator; @@ -52,25 +52,25 @@ import java.util.stream.Collectors; @RequestMapping(path = "/api/database/{databaseId}/table") public class TableEndpoint { - private final TableMapper tableMapper; private final UserService userService; private final TableService tableService; private final RabbitConfig rabbitMqConfig; private final EntityService entityService; private final BrokerService messageQueueService; + private final MetadataMapper metadataMapper; private final DatabaseService databaseService; private final EndpointValidator endpointValidator; @Autowired - public TableEndpoint(TableMapper tableMapper, UserService userService, TableService tableService, - RabbitConfig rabbitMqConfig, EntityService entityService, BrokerService messageQueueService, + public TableEndpoint(UserService userService, TableService tableService, RabbitConfig rabbitMqConfig, + EntityService entityService, BrokerService messageQueueService, MetadataMapper metadataMapper, DatabaseService databaseService, EndpointValidator endpointValidator) { - this.tableMapper = tableMapper; this.userService = userService; this.tableService = tableService; this.rabbitMqConfig = rabbitMqConfig; this.entityService = entityService; this.messageQueueService = messageQueueService; + this.metadataMapper = metadataMapper; this.databaseService = databaseService; this.endpointValidator = endpointValidator; } @@ -105,7 +105,7 @@ public class TableEndpoint { endpointValidator.validateOnlyPrivateHasRole(database, principal, "list-tables"); final List<TableBriefDto> dto = database.getTables() .stream() - .map(tableMapper::tableToTableBriefDto) + .map(metadataMapper::tableToTableBriefDto) .collect(Collectors.toList()); log.trace("list tables resulted in tables {}", dto); return ResponseEntity.ok(dto); @@ -251,7 +251,7 @@ public class TableEndpoint { TableColumn column = tableService.findColumnById(table, columnId); column = tableService.update(column, updateDto); log.info("Updated table semantics of table with id {}", tableId); - final ColumnDto columnDto = tableMapper.tableColumnToColumnDto(column); + final ColumnDto columnDto = metadataMapper.tableColumnToColumnDto(column); log.trace("find table data resulted in column {}", columnDto); return ResponseEntity.accepted() .body(columnDto); @@ -359,7 +359,7 @@ public class TableEndpoint { throw new MalformedException("Failed to create table: date column(s) " + failedDateColumns.stream().map(ColumnCreateDto::getName).toList() + " do not contain date format id"); } final Table table = tableService.createTable(database, data, principal); - final TableDto dto = tableMapper.tableToTableDto(table); + final TableDto dto = metadataMapper.customTableToTableDto(table); log.debug("create table resulted in table.id={}", dto.getId()); return ResponseEntity.status(HttpStatus.CREATED) .body(dto); @@ -402,7 +402,7 @@ public class TableEndpoint { ServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, QueueNotFoundException { log.debug("endpoint find table, databaseId={}, tableId={}", databaseId, tableId); final Table table = tableService.findById(databaseId, tableId); - final TableDto dto = tableMapper.tableToTableDto(table); + final TableDto dto = metadataMapper.customTableToTableDto(table); final HttpHeaders headers = new HttpHeaders(); if (principal != null) { /* extra effort only when logged-in */ diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UnitEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UnitEndpoint.java index 7808b1b291..71dfa78cef 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UnitEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UnitEndpoint.java @@ -1,7 +1,7 @@ package at.tuwien.endpoints; import at.tuwien.api.database.table.columns.concepts.UnitDto; -import at.tuwien.mapper.SemanticMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.UnitService; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -26,11 +25,11 @@ import java.util.List; public class UnitEndpoint { private final UnitService unitService; - private final SemanticMapper semanticMapper; + private final MetadataMapper metadataMapper; @Autowired - public UnitEndpoint(SemanticMapper semanticMapper, UnitService unitService) { - this.semanticMapper = semanticMapper; + public UnitEndpoint(MetadataMapper metadataMapper, UnitService unitService) { + this.metadataMapper = metadataMapper; this.unitService = unitService; } @@ -49,7 +48,7 @@ public class UnitEndpoint { log.debug("endpoint list units"); final List<UnitDto> dtos = unitService.findAll() .stream() - .map(semanticMapper::tableColumnUnitToUnitDto) + .map(metadataMapper::tableColumnUnitToUnitDto) .toList(); log.trace("Find all units resulted in dtos {}", dtos); return ResponseEntity.ok() diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java index 4d5ad8164b..fb9ddc0096 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java @@ -9,7 +9,7 @@ import at.tuwien.api.user.*; import at.tuwien.entities.database.Database; import at.tuwien.entities.user.User; import at.tuwien.exception.*; -import at.tuwien.mapper.UserMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.AuthenticationService; import at.tuwien.service.DatabaseService; import at.tuwien.service.UserService; @@ -22,7 +22,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import lombok.extern.log4j.Log4j2; @@ -43,16 +42,16 @@ import java.util.UUID; @RequestMapping(path = "/api/user") public class UserEndpoint { - private final UserMapper userMapper; private final UserService userService; + private final MetadataMapper userMapper; private final DatabaseService databaseService; private final AuthenticationService authenticationService; @Autowired - public UserEndpoint(UserMapper userMapper, UserService userService, DatabaseService databaseService, + public UserEndpoint(UserService userService, MetadataMapper userMapper, DatabaseService databaseService, AuthenticationService authenticationService) { - this.userMapper = userMapper; this.userService = userService; + this.userMapper = userMapper; this.databaseService = databaseService; this.authenticationService = authenticationService; } diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java index a6627689aa..701e3172fb 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java @@ -8,7 +8,7 @@ import at.tuwien.entities.database.Database; import at.tuwien.entities.database.View; import at.tuwien.entities.user.User; import at.tuwien.exception.*; -import at.tuwien.mapper.ViewMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.DatabaseService; import at.tuwien.service.UserService; import at.tuwien.service.ViewService; @@ -42,17 +42,17 @@ import java.util.stream.Collectors; @RequestMapping(path = "/api/database/{databaseId}/view") public class ViewEndpoint { - private final ViewMapper viewMapper; private final UserService userService; private final ViewService viewService; + private final MetadataMapper metadataMapper; private final DatabaseService databaseService; @Autowired - public ViewEndpoint(ViewService viewService, DatabaseService databaseService, - ViewMapper viewMapper, UserService userService) { - this.viewMapper = viewMapper; + public ViewEndpoint(UserService userService, ViewService viewService, MetadataMapper metadataMapper, + DatabaseService databaseService) { this.userService = userService; this.viewService = viewService; + this.metadataMapper = metadataMapper; this.databaseService = databaseService; } @@ -81,7 +81,7 @@ public class ViewEndpoint { log.trace("find all views for database {}", database); final List<ViewBriefDto> views = viewService.findAll(database, user) .stream() - .map(viewMapper::viewToViewBriefDto) + .map(metadataMapper::viewToViewBriefDto) .collect(Collectors.toList()); return ResponseEntity.ok(views); } @@ -153,7 +153,7 @@ public class ViewEndpoint { log.trace("create view for database {}", database); final View view; view = viewService.create(database, user, data); - final ViewBriefDto dto = viewMapper.viewToViewBriefDto(view); + final ViewBriefDto dto = metadataMapper.viewToViewBriefDto(view); return ResponseEntity.status(HttpStatus.CREATED) .body(dto); } @@ -201,7 +201,7 @@ public class ViewEndpoint { } return ResponseEntity.status(HttpStatus.OK) .headers(headers) - .body(viewMapper.viewToViewDto(view)); + .body(metadataMapper.viewToViewDto(view)); } @DeleteMapping("/{viewId}") diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/AccessEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/AccessEndpointUnitTest.java index 10a9afc94c..0444a76690 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/AccessEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/AccessEndpointUnitTest.java @@ -1,5 +1,6 @@ package at.tuwien.endpoints; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.test.AbstractUnitTest; import at.tuwien.api.database.AccessTypeDto; import at.tuwien.api.database.DatabaseAccessDto; @@ -7,7 +8,6 @@ import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.user.User; import at.tuwien.exception.*; -import at.tuwien.mapper.AccessMapper; import at.tuwien.repository.DatabaseRepository; import at.tuwien.repository.UserRepository; import at.tuwien.service.AccessService; @@ -49,7 +49,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { private AccessEndpoint accessEndpoint; @Autowired - private AccessMapper accessMapper; + private MetadataMapper metadataMapper; @Test @WithAnonymousUser @@ -264,7 +264,9 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { final DatabaseAccessDto dto = response.getBody(); assertEquals(userId, dto.getHuserid()); assertEquals(databaseId, dto.getHdbid()); - assertEquals(accessMapper.accessType(access.getType()), dto.getType()); + if (access != null) { + assertEquals(metadataMapper.accessTypeToAccessTypeDto(access.getType()), dto.getType()); + } } protected void generic_update(DatabaseAccess access, String otherUsername, User otherUser, Principal principal, diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/ContainerMapperTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/ContainerMapperTest.java deleted file mode 100644 index effb6e04a5..0000000000 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/ContainerMapperTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.test.AbstractUnitTest; -import at.tuwien.entities.container.Container; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class ContainerMapperTest extends AbstractUnitTest { - - @Test - public void equals_fails() { - - /* test */ - assertNotEquals(CONTAINER_1, CONTAINER_2); - } - - @Test - public void equals_identity_succeeds() { - - /* test */ - assertEquals(CONTAINER_1, CONTAINER_1); - } - - @Test - public void equals_similar_succeeds() { - final Container tmp = Container.builder() - .id(CONTAINER_1_ID) - .build(); - - /* test */ - assertEquals(CONTAINER_1, tmp); - } - -} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/DatabaseMapperUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/DatabaseMapperUnitTest.java deleted file mode 100644 index 4dfdadd102..0000000000 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/DatabaseMapperUnitTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.identifier.IdentifierDto; -import at.tuwien.test.AbstractUnitTest; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@Log4j2 -@SpringBootTest -public class DatabaseMapperUnitTest extends AbstractUnitTest { - - @Autowired - private DatabaseMapper databaseMapper; - - @BeforeEach - public void beforeEach() { - genesis(); - } - - @Test - public void databaseToDatabaseDto_succeeds() { - - /* test */ - final DatabaseDto response = databaseMapper.databaseToDatabaseDto(DATABASE_1); - assertEquals(DATABASE_1_ID, response.getId()); - assertEquals(4, response.getIdentifiers().size()); - /* identifier 1 */ - final IdentifierDto identifier1 = response.getIdentifiers().get(0); - assertEquals(DATABASE_1_ID, identifier1.getDatabaseId()); - assertNotNull(identifier1.getCreator()); - assertEquals(IDENTIFIER_1_CREATED_BY, identifier1.getCreator().getId()); - assertNotNull(identifier1.getCreated()); - assertNotNull(identifier1.getLastModified()); - /* identifier 2 */ - final IdentifierDto identifier2 = response.getIdentifiers().get(1); - assertEquals(DATABASE_1_ID, identifier2.getDatabaseId()); - assertNotNull(identifier2.getCreator()); - assertEquals(IDENTIFIER_2_CREATED_BY, identifier2.getCreator().getId()); - assertNotNull(identifier2.getCreated()); - assertNotNull(identifier2.getLastModified()); - /* identifier 3 */ - final IdentifierDto identifier3 = response.getIdentifiers().get(2); - assertEquals(DATABASE_1_ID, identifier3.getDatabaseId()); - assertNotNull(identifier3.getCreator()); - assertEquals(IDENTIFIER_3_CREATED_BY, identifier3.getCreator().getId()); - assertNotNull(identifier3.getCreated()); - assertNotNull(identifier3.getLastModified()); - /* identifier 4 */ - final IdentifierDto identifier4 = response.getIdentifiers().get(3); - assertEquals(DATABASE_1_ID, identifier4.getDatabaseId()); - assertNotNull(identifier4.getCreator()); - assertEquals(IDENTIFIER_4_CREATED_BY, identifier4.getCreator().getId()); - assertNotNull(identifier4.getCreated()); - assertNotNull(identifier4.getLastModified()); - } - -} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/IdentifierMapperUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/IdentifierMapperUnitTest.java deleted file mode 100644 index 0089ad8a04..0000000000 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/IdentifierMapperUnitTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.identifier.IdentifierTypeDto; -import at.tuwien.entities.identifier.Identifier; -import at.tuwien.entities.identifier.IdentifierType; -import at.tuwien.test.AbstractUnitTest; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -@Log4j2 -@SpringBootTest -public class IdentifierMapperUnitTest extends AbstractUnitTest { - - @Autowired - private IdentifierMapper identifierMapper; - - @Test - public void identifierTypeDtoToIdentifierType_succeeds() { - - /* test */ - assertEquals(IdentifierType.VIEW, identifierMapper.identifierTypeDtoToIdentifierType(IdentifierTypeDto.VIEW)); - assertEquals(IdentifierType.TABLE, identifierMapper.identifierTypeDtoToIdentifierType(IdentifierTypeDto.TABLE)); - assertEquals(IdentifierType.SUBSET, identifierMapper.identifierTypeDtoToIdentifierType(IdentifierTypeDto.SUBSET)); - assertEquals(IdentifierType.DATABASE, identifierMapper.identifierTypeDtoToIdentifierType(IdentifierTypeDto.DATABASE)); - } - - @Test - public void identifierCreateDtoToIdentifier_succeeds() { - - /* test */ - final Identifier response = identifierMapper.identifierCreateDtoToIdentifier(IDENTIFIER_1_CREATE_DTO); - assertNull(response.getDatabase()); - assertNull(response.getViewId()); - assertNull(response.getQueryId()); - assertNull(response.getTableId()); - assertNull(response.getDoi()); - assertEquals(IDENTIFIER_1_TYPE, response.getType()); - } - - @Test - public void identifierCreateDtoToIdentifier_withDoi_succeeds() { - - /* test */ - final Identifier response = identifierMapper.identifierCreateDtoToIdentifier(IDENTIFIER_1_CREATE_WITH_DOI_DTO); - assertNull(response.getDatabase()); - assertNull(response.getViewId()); - assertNull(response.getQueryId()); - assertNull(response.getTableId()); - assertEquals(IDENTIFIER_1_DOI_NOT_NULL, response.getDoi()); - assertEquals(IDENTIFIER_1_TYPE, response.getType()); - } - - @Test - public void identifierCreateDtoToIdentifier_subset_succeeds() { - - /* test */ - final Identifier response = identifierMapper.identifierCreateDtoToIdentifier(IDENTIFIER_2_CREATE_DTO); - assertNull(response.getDatabase()); - assertNull(response.getViewId()); - assertNull(response.getTableId()); - assertEquals(IDENTIFIER_2_QUERY_ID, response.getQueryId()); - assertNull(response.getDoi()); - assertEquals(IDENTIFIER_2_TYPE, response.getType()); - } - - @Test - public void identifierCreateDtoToIdentifier_view_succeeds() { - - /* test */ - final Identifier response = identifierMapper.identifierCreateDtoToIdentifier(IDENTIFIER_3_CREATE_DTO); - assertNull(response.getDatabase()); - assertNull(response.getQueryId()); - assertNull(response.getTableId()); - assertEquals(IDENTIFIER_3_VIEW_ID, response.getViewId()); - assertNull(response.getDoi()); - assertEquals(IDENTIFIER_3_TYPE, response.getType()); - } - -} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/MetadataMapperUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/MetadataMapperUnitTest.java new file mode 100644 index 0000000000..e253d4f764 --- /dev/null +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/MetadataMapperUnitTest.java @@ -0,0 +1,475 @@ +package at.tuwien.mapper; + +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.ViewDto; +import at.tuwien.api.database.table.TableDto; +import at.tuwien.api.database.table.columns.ColumnTypeDto; +import at.tuwien.api.database.table.constraints.foreign.ForeignKeyDto; +import at.tuwien.api.database.table.constraints.foreign.ForeignKeyReferenceDto; +import at.tuwien.api.database.table.constraints.foreign.ReferenceTypeDto; +import at.tuwien.api.database.table.constraints.primary.PrimaryKeyDto; +import at.tuwien.api.database.table.constraints.unique.UniqueDto; +import at.tuwien.api.identifier.IdentifierDto; +import at.tuwien.api.identifier.IdentifierTypeDto; +import at.tuwien.api.user.UserBriefDto; +import at.tuwien.api.user.UserDto; +import at.tuwien.entities.container.Container; +import at.tuwien.entities.database.Database; +import at.tuwien.entities.database.table.Table; +import at.tuwien.entities.identifier.Identifier; +import at.tuwien.entities.identifier.IdentifierType; +import at.tuwien.test.AbstractUnitTest; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +@Log4j2 +@SpringBootTest +public class MetadataMapperUnitTest extends AbstractUnitTest { + + private final DateTimeFormatter mariaDbFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSS]") + .withZone(ZoneId.of("UTC")); + + @Autowired + private MetadataMapper metadataMapper; + + @BeforeEach + public void beforeEach() { + genesis(); + } + + @Test + public void mapMariaDbInstant_succeeds() { + final String timestamp = "2023-01-08 08:49:29"; + final Instant compare = Instant.ofEpochSecond(1673167769); + + /* test */ + final Instant response = LocalDateTime.parse(timestamp, mariaDbFormatter) + .atZone(ZoneId.of("UTC")) + .toInstant(); + assertEquals(compare, response); + } + + @Test + public void containerEquals_fails() { + + /* test */ + assertNotEquals(CONTAINER_1, CONTAINER_2); + } + + @Test + public void containerEquals_identity_succeeds() { + + /* test */ + assertEquals(CONTAINER_1, CONTAINER_1); + } + + @Test + public void containerEquals_similar_succeeds() { + final Container tmp = Container.builder() + .id(CONTAINER_1_ID) + .build(); + + /* test */ + assertEquals(CONTAINER_1, tmp); + } + + @Test + public void identifierTypeDtoToIdentifierType_succeeds() { + + /* test */ + assertEquals(IdentifierType.VIEW, metadataMapper.identifierTypeDtoToIdentifierType(IdentifierTypeDto.VIEW)); + assertEquals(IdentifierType.TABLE, metadataMapper.identifierTypeDtoToIdentifierType(IdentifierTypeDto.TABLE)); + assertEquals(IdentifierType.SUBSET, metadataMapper.identifierTypeDtoToIdentifierType(IdentifierTypeDto.SUBSET)); + assertEquals(IdentifierType.DATABASE, metadataMapper.identifierTypeDtoToIdentifierType(IdentifierTypeDto.DATABASE)); + } + + @Test + public void identifierCreateDtoToIdentifier_succeeds() { + + /* test */ + final Identifier response = metadataMapper.identifierCreateDtoToIdentifier(IDENTIFIER_1_CREATE_DTO); + assertNull(response.getDatabase()); + assertNull(response.getViewId()); + assertNull(response.getQueryId()); + assertNull(response.getTableId()); + assertNull(response.getDoi()); + assertEquals(IDENTIFIER_1_TYPE, response.getType()); + } + + @Test + public void identifierCreateDtoToIdentifier_withDoi_succeeds() { + + /* test */ + final Identifier response = metadataMapper.identifierCreateDtoToIdentifier(IDENTIFIER_1_CREATE_WITH_DOI_DTO); + assertNull(response.getDatabase()); + assertNull(response.getViewId()); + assertNull(response.getQueryId()); + assertNull(response.getTableId()); + assertEquals(IDENTIFIER_1_DOI_NOT_NULL, response.getDoi()); + assertEquals(IDENTIFIER_1_TYPE, response.getType()); + } + + @Test + public void identifierCreateDtoToIdentifier_subset_succeeds() { + + /* test */ + final Identifier response = metadataMapper.identifierCreateDtoToIdentifier(IDENTIFIER_2_CREATE_DTO); + assertNull(response.getDatabase()); + assertNull(response.getViewId()); + assertNull(response.getTableId()); + assertEquals(IDENTIFIER_2_QUERY_ID, response.getQueryId()); + assertNull(response.getDoi()); + assertEquals(IDENTIFIER_2_TYPE, response.getType()); + } + + @Test + public void identifierCreateDtoToIdentifier_view_succeeds() { + + /* test */ + final Identifier response = metadataMapper.identifierCreateDtoToIdentifier(IDENTIFIER_3_CREATE_DTO); + assertNull(response.getDatabase()); + assertNull(response.getQueryId()); + assertNull(response.getTableId()); + assertEquals(IDENTIFIER_3_VIEW_ID, response.getViewId()); + assertNull(response.getDoi()); + assertEquals(IDENTIFIER_3_TYPE, response.getType()); + } + + @Test + public void databaseToDatabaseDto_succeeds() { + + /* test */ + final DatabaseDto response = metadataMapper.customDatabaseToDatabaseDto(DATABASE_1); + assertEquals(DATABASE_1_ID, response.getId()); + assertNotNull(response.getContact()); + assertEquals(USER_1_ID, response.getContact().getId()); + /* identifiers formatted */ + assertEquals(4, response.getIdentifiers().size()); + final IdentifierDto identifier1 = response.getIdentifiers().get(0); + assertEquals(DATABASE_1_ID, identifier1.getDatabaseId()); + assertNotNull(identifier1.getCreator()); + assertEquals(IDENTIFIER_1_CREATED_BY, identifier1.getCreator().getId()); + assertNotNull(identifier1.getCreated()); + assertNotNull(identifier1.getLastModified()); + final IdentifierDto identifier2 = response.getIdentifiers().get(1); + assertEquals(DATABASE_1_ID, identifier2.getDatabaseId()); + assertNotNull(identifier2.getCreator()); + assertEquals(IDENTIFIER_2_CREATED_BY, identifier2.getCreator().getId()); + assertNotNull(identifier2.getCreated()); + assertNotNull(identifier2.getLastModified()); + final IdentifierDto identifier3 = response.getIdentifiers().get(2); + assertEquals(DATABASE_1_ID, identifier3.getDatabaseId()); + assertNotNull(identifier3.getCreator()); + assertEquals(IDENTIFIER_3_CREATED_BY, identifier3.getCreator().getId()); + assertNotNull(identifier3.getCreated()); + assertNotNull(identifier3.getLastModified()); + final IdentifierDto identifier4 = response.getIdentifiers().get(3); + assertEquals(DATABASE_1_ID, identifier4.getDatabaseId()); + assertNotNull(identifier4.getCreator()); + assertEquals(IDENTIFIER_4_CREATED_BY, identifier4.getCreator().getId()); + assertNotNull(identifier4.getCreated()); + assertNotNull(identifier4.getLastModified()); + /* Table 1 formatted */ + final TableDto table0 = response.getTables().get(0); + assertEquals(TABLE_1_ID, table0.getId()); + assertEquals(TABLE_1_NAME, table0.getName()); + assertEquals(TABLE_1_INTERNALNAME, table0.getInternalName()); + assertEquals(TABLE_1_DESCRIPTION, table0.getDescription()); + assertEquals(DATABASE_1_ID, table0.getTdbid()); + assertEquals(USER_1_ID, table0.getCreatedBy()); + assertEquals(USER_1_ID, table0.getOwner().getId()); + assertEquals(USER_1_ID, table0.getCreator().getId()); + assertEquals(TABLE_1_AVG_ROW_LENGTH, table0.getAvgRowLength()); + assertEquals(TABLE_1_NUM_ROWS, table0.getNumRows()); + assertEquals(TABLE_1_DATA_LENGTH, table0.getDataLength()); + assertEquals(TABLE_1_MAX_DATA_LENGTH, table0.getMaxDataLength()); + assertNotNull(table0.getCreated()); + /* columns formatted */ + assertEquals(TABLE_1_COLUMNS.size(), table0.getColumns().size()); + for (int i = 0; i < TABLE_1_COLUMNS.size(); i++) { + assertEquals(TABLE_1_COLUMNS.get(i).getId(), table0.getColumns().get(i).getId()); + assertEquals(TABLE_1_COLUMNS.get(i).getOrdinalPosition(), table0.getColumns().get(i).getOrdinalPosition()); + assertNotNull(table0.getColumns().get(i).getOrdinalPosition()); + assertEquals(TABLE_1_COLUMNS.get(i).getTable().getId(), table0.getColumns().get(i).getTableId()); + assertEquals(TABLE_1_COLUMNS.get(i).getName(), table0.getColumns().get(i).getName()); + assertEquals(TABLE_1_COLUMNS.get(i).getInternalName(), table0.getColumns().get(i).getInternalName()); + assertEquals(List.of(ColumnTypeDto.BIGINT, ColumnTypeDto.DATE, ColumnTypeDto.VARCHAR, ColumnTypeDto.DECIMAL, ColumnTypeDto.DECIMAL).get(i), table0.getColumns().get(i).getColumnType()); + assertEquals(TABLE_1_COLUMNS.get(i).getSize(), table0.getColumns().get(i).getSize()); + assertEquals(TABLE_1_COLUMNS.get(i).getD(), table0.getColumns().get(i).getD()); + assertEquals(TABLE_1_COLUMNS.get(i).getIsNullAllowed(), table0.getColumns().get(i).getIsNullAllowed()); + assertEquals(TABLE_1_COLUMNS.get(i).getAutoGenerated(), table0.getColumns().get(i).getAutoGenerated()); + assertEquals(TABLE_1_COLUMNS.get(i).getEnums(), table0.getColumns().get(i).getEnums()); + assertEquals(TABLE_1_COLUMNS.get(i).getSets(), table0.getColumns().get(i).getSets()); + } + /* constraints formatted */ + assertNotNull(table0.getConstraints()); + assertEquals(0, table0.getConstraints().getUniques().size()); + assertEquals(0, table0.getConstraints().getChecks().size()); + assertEquals(0, table0.getConstraints().getForeignKeys().size()); + assertEquals(1, table0.getConstraints().getPrimaryKey().size()); + final PrimaryKeyDto table0pk = new ArrayList<>(table0.getConstraints().getPrimaryKey()).get(0); + assertEquals(1L, table0pk.getId()); + assertEquals(TABLE_1_COLUMNS_BRIEF_0_DTO.getId(), table0pk.getColumn().getId()); + assertEquals(TABLE_1_COLUMNS_BRIEF_0_DTO.getName(), table0pk.getColumn().getName()); + assertEquals(TABLE_1_COLUMNS_BRIEF_0_DTO.getId(), table0pk.getColumn().getId()); + assertEquals(TABLE_1_COLUMNS_BRIEF_0_DTO.getName(), table0pk.getColumn().getName()); + assertEquals(TABLE_1_COLUMNS_BRIEF_0_DTO.getInternalName(), table0pk.getColumn().getInternalName()); + assertEquals(TABLE_1_ID, table0pk.getTable().getId()); + assertEquals(DATABASE_1_ID, table0pk.getTable().getDatabaseId()); + assertEquals(ColumnTypeDto.BIGINT, table0pk.getColumn().getColumnType()); + assertNull(table0pk.getColumn().getAlias()); + assertEquals(TABLE_1_ID, table0pk.getColumn().getTableId()); + assertEquals(DATABASE_1_ID, table0pk.getColumn().getDatabaseId()); + /* Table 2 formatted */ + final TableDto table1 = response.getTables().get(1); + assertEquals(TABLE_2_ID, table1.getId()); + assertEquals(TABLE_2_NAME, table1.getName()); + assertEquals(TABLE_2_INTERNALNAME, table1.getInternalName()); + assertEquals(TABLE_2_DESCRIPTION, table1.getDescription()); + assertEquals(DATABASE_1_ID, table1.getTdbid()); + assertEquals(USER_2_ID, table1.getCreatedBy()); + assertEquals(USER_2_ID, table1.getOwner().getId()); + assertEquals(USER_2_ID, table1.getCreator().getId()); + assertEquals(TABLE_2_AVG_ROW_LENGTH, table1.getAvgRowLength()); + assertEquals(TABLE_2_NUM_ROWS, table1.getNumRows()); + assertEquals(TABLE_2_DATA_LENGTH, table1.getDataLength()); + assertEquals(TABLE_2_MAX_DATA_LENGTH, table1.getMaxDataLength()); + assertNotNull(table1.getCreated()); + /* columns formatted */ + assertEquals(TABLE_2_COLUMNS.size(), table1.getColumns().size()); + for (int i = 0; i < TABLE_2_COLUMNS.size(); i++) { + assertEquals(TABLE_2_COLUMNS.get(i).getId(), table1.getColumns().get(i).getId()); + assertEquals(TABLE_2_COLUMNS.get(i).getOrdinalPosition(), table1.getColumns().get(i).getOrdinalPosition()); + assertNotNull(table1.getColumns().get(i).getOrdinalPosition()); + assertEquals(TABLE_2_COLUMNS.get(i).getTable().getId(), table1.getColumns().get(i).getTableId()); + assertEquals(TABLE_2_COLUMNS.get(i).getName(), table1.getColumns().get(i).getName()); + assertEquals(TABLE_2_COLUMNS.get(i).getInternalName(), table1.getColumns().get(i).getInternalName()); + assertEquals(List.of(ColumnTypeDto.VARCHAR, ColumnTypeDto.DECIMAL, ColumnTypeDto.DECIMAL).get(i), table1.getColumns().get(i).getColumnType()); + assertEquals(TABLE_2_COLUMNS.get(i).getSize(), table1.getColumns().get(i).getSize()); + assertEquals(TABLE_2_COLUMNS.get(i).getD(), table1.getColumns().get(i).getD()); + assertEquals(TABLE_2_COLUMNS.get(i).getIsNullAllowed(), table1.getColumns().get(i).getIsNullAllowed()); + assertEquals(TABLE_2_COLUMNS.get(i).getAutoGenerated(), table1.getColumns().get(i).getAutoGenerated()); + assertEquals(TABLE_2_COLUMNS.get(i).getEnums(), table1.getColumns().get(i).getEnums()); + assertEquals(TABLE_2_COLUMNS.get(i).getSets(), table1.getColumns().get(i).getSets()); + } + /* constraints formatted */ + assertNotNull(table1.getConstraints()); + assertEquals(1, table1.getConstraints().getUniques().size()); + final UniqueDto table1uk = table1.getConstraints().getUniques().get(0); + assertEquals(1L, table1uk.getId()); + assertEquals(TABLE_2_ID, table1uk.getTable().getId()); + assertEquals(DATABASE_1_ID, table1uk.getTable().getDatabaseId()); + assertEquals("uk_1", table1uk.getName()); + assertEquals(TABLE_2_COLUMNS_DTO.get(1).getId(), table1uk.getColumns().get(0).getId()); + assertEquals(1, table1.getConstraints().getChecks().size()); + assertEquals("`mintemp` > 0", new ArrayList<>(table1.getConstraints().getChecks()).get(0)); + assertEquals(1, table1.getConstraints().getForeignKeys().size()); + final ForeignKeyDto table1fk = new ArrayList<>(table1.getConstraints().getForeignKeys()).get(0); + assertEquals("fk_location", table1fk.getName()); + assertEquals(ReferenceTypeDto.NO_ACTION, table1fk.getOnDelete()); + assertEquals(ReferenceTypeDto.NO_ACTION, table1fk.getOnUpdate()); + assertEquals(TABLE_1_ID, table1fk.getTable().getId()); + assertEquals(TABLE_2_ID, table1fk.getReferencedTable().getId()); + final ForeignKeyReferenceDto table1fkr = table1fk.getReferences().get(0); + assertEquals(1L, table1fkr.getId()); + assertEquals(TABLE_2_COLUMNS_DTO.get(2).getId(), table1fkr.getColumn().getId()); + assertEquals(1, table1.getConstraints().getPrimaryKey().size()); + final PrimaryKeyDto table1pk = new ArrayList<>(table1.getConstraints().getPrimaryKey()).get(0); + assertEquals(2L, table1pk.getId()); + assertEquals(TABLE_2_COLUMNS_BRIEF_0_DTO.getId(), table1pk.getColumn().getId()); + assertEquals(TABLE_2_COLUMNS_BRIEF_0_DTO.getName(), table1pk.getColumn().getName()); + assertEquals(TABLE_2_COLUMNS_BRIEF_0_DTO.getId(), table1pk.getColumn().getId()); + assertEquals(TABLE_2_COLUMNS_BRIEF_0_DTO.getName(), table1pk.getColumn().getName()); + assertEquals(TABLE_2_COLUMNS_BRIEF_0_DTO.getInternalName(), table1pk.getColumn().getInternalName()); + assertEquals(ColumnTypeDto.VARCHAR, table1pk.getColumn().getColumnType()); + assertNull(table1pk.getColumn().getAlias()); + assertEquals(TABLE_2_ID, table1pk.getColumn().getTableId()); + assertEquals(DATABASE_1_ID, table1pk.getColumn().getDatabaseId()); + /* Table 3 formatted */ + final TableDto table2 = response.getTables().get(2); + assertEquals(TABLE_3_ID, table2.getId()); + assertEquals(TABLE_3_NAME, table2.getName()); + assertEquals(TABLE_3_INTERNALNAME, table2.getInternalName()); + assertEquals(TABLE_3_DESCRIPTION, table2.getDescription()); + assertEquals(DATABASE_1_ID, table2.getTdbid()); + assertEquals(USER_3_ID, table2.getCreatedBy()); + assertEquals(USER_3_ID, table2.getOwner().getId()); + assertEquals(USER_3_ID, table2.getCreator().getId()); + assertEquals(TABLE_3_AVG_ROW_LENGTH, table2.getAvgRowLength()); + assertEquals(TABLE_3_NUM_ROWS, table2.getNumRows()); + assertEquals(TABLE_3_DATA_LENGTH, table2.getDataLength()); + assertEquals(TABLE_3_MAX_DATA_LENGTH, table2.getMaxDataLength()); + assertNotNull(table2.getCreated()); + /* columns formatted */ + assertEquals(TABLE_3_COLUMNS.size(), table2.getColumns().size()); + for (int i = 0; i < TABLE_3_COLUMNS.size(); i++) { + assertEquals(TABLE_3_COLUMNS.get(i).getId(), table2.getColumns().get(i).getId()); + assertEquals(TABLE_3_COLUMNS.get(i).getOrdinalPosition(), table2.getColumns().get(i).getOrdinalPosition()); + assertNotNull(table2.getColumns().get(i).getOrdinalPosition()); + assertEquals(TABLE_3_COLUMNS.get(i).getTable().getId(), table2.getColumns().get(i).getTableId()); + assertEquals(TABLE_3_COLUMNS.get(i).getName(), table2.getColumns().get(i).getName()); + assertEquals(TABLE_3_COLUMNS.get(i).getInternalName(), table2.getColumns().get(i).getInternalName()); + assertEquals(List.of(ColumnTypeDto.BIGINT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.DATE, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.DATE, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.DATE, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT, ColumnTypeDto.INT).get(i), table2.getColumns().get(i).getColumnType()); + assertEquals(TABLE_3_COLUMNS.get(i).getSize(), table2.getColumns().get(i).getSize()); + assertEquals(TABLE_3_COLUMNS.get(i).getD(), table2.getColumns().get(i).getD()); + assertEquals(TABLE_3_COLUMNS.get(i).getIsNullAllowed(), table2.getColumns().get(i).getIsNullAllowed()); + assertEquals(TABLE_3_COLUMNS.get(i).getAutoGenerated(), table2.getColumns().get(i).getAutoGenerated()); + assertEquals(TABLE_3_COLUMNS.get(i).getEnums(), table2.getColumns().get(i).getEnums()); + assertEquals(TABLE_3_COLUMNS.get(i).getSets(), table2.getColumns().get(i).getSets()); + } + /* constraints formatted */ + final PrimaryKeyDto table2pk = new ArrayList<>(table2.getConstraints().getPrimaryKey()).get(0); + assertEquals(TABLE_3_COLUMNS_BRIEF_0_DTO.getId(), table2pk.getColumn().getId()); + assertEquals(TABLE_3_COLUMNS_BRIEF_0_DTO.getName(), table2pk.getColumn().getName()); + assertEquals(TABLE_3_COLUMNS_BRIEF_0_DTO.getId(), table2pk.getColumn().getId()); + assertEquals(TABLE_3_COLUMNS_BRIEF_0_DTO.getName(), table2pk.getColumn().getName()); + assertEquals(TABLE_3_COLUMNS_BRIEF_0_DTO.getInternalName(), table2pk.getColumn().getInternalName()); + assertEquals(ColumnTypeDto.BIGINT, table2pk.getColumn().getColumnType()); + assertNull(table2pk.getColumn().getAlias()); + assertEquals(TABLE_3_ID, table2pk.getColumn().getTableId()); + assertEquals(DATABASE_1_ID, table2pk.getColumn().getDatabaseId()); + /* Table 4 formatted */ + final TableDto table3 = response.getTables().get(3); + assertEquals(TABLE_4_ID, table3.getId()); + assertEquals(TABLE_4_NAME, table3.getName()); + assertEquals(TABLE_4_INTERNALNAME, table3.getInternalName()); + assertEquals(TABLE_4_DESCRIPTION, table3.getDescription()); + assertEquals(DATABASE_1_ID, table3.getTdbid()); + assertEquals(USER_1_ID, table3.getCreatedBy()); + assertEquals(USER_1_ID, table3.getOwner().getId()); + assertEquals(USER_1_ID, table3.getCreator().getId()); + assertEquals(TABLE_4_AVG_ROW_LENGTH, table3.getAvgRowLength()); + assertEquals(TABLE_4_NUM_ROWS, table3.getNumRows()); + assertEquals(TABLE_4_DATA_LENGTH, table3.getDataLength()); + assertEquals(TABLE_4_MAX_DATA_LENGTH, table3.getMaxDataLength()); + assertNotNull(table3.getCreated()); + /* columns formatted */ + assertEquals(TABLE_4_COLUMNS.size(), table3.getColumns().size()); + for (int i = 0; i < TABLE_4_COLUMNS.size(); i++) { + assertEquals(TABLE_4_COLUMNS.get(i).getId(), table3.getColumns().get(i).getId()); + assertEquals(TABLE_4_COLUMNS.get(i).getOrdinalPosition(), table3.getColumns().get(i).getOrdinalPosition()); + assertNotNull(table3.getColumns().get(i).getOrdinalPosition()); + assertEquals(TABLE_4_COLUMNS.get(i).getTable().getId(), table3.getColumns().get(i).getTableId()); + assertEquals(TABLE_4_COLUMNS.get(i).getName(), table3.getColumns().get(i).getName()); + assertEquals(TABLE_4_COLUMNS.get(i).getInternalName(), table3.getColumns().get(i).getInternalName()); + assertEquals(List.of(ColumnTypeDto.TIMESTAMP, ColumnTypeDto.DECIMAL).get(i), table3.getColumns().get(i).getColumnType()); + assertEquals(TABLE_4_COLUMNS.get(i).getSize(), table3.getColumns().get(i).getSize()); + assertEquals(TABLE_4_COLUMNS.get(i).getD(), table3.getColumns().get(i).getD()); + assertEquals(TABLE_4_COLUMNS.get(i).getIsNullAllowed(), table3.getColumns().get(i).getIsNullAllowed()); + assertEquals(TABLE_4_COLUMNS.get(i).getAutoGenerated(), table3.getColumns().get(i).getAutoGenerated()); + assertEquals(TABLE_4_COLUMNS.get(i).getEnums(), table3.getColumns().get(i).getEnums()); + assertEquals(TABLE_4_COLUMNS.get(i).getSets(), table3.getColumns().get(i).getSets()); + } + /* constraints formatted */ + final PrimaryKeyDto table3pk = new ArrayList<>(table3.getConstraints().getPrimaryKey()).get(0); + assertEquals(TABLE_4_COLUMNS_BRIEF_0_DTO.getId(), table3pk.getColumn().getId()); + assertEquals(TABLE_4_COLUMNS_BRIEF_0_DTO.getName(), table3pk.getColumn().getName()); + assertEquals(TABLE_4_COLUMNS_BRIEF_0_DTO.getId(), table3pk.getColumn().getId()); + assertEquals(TABLE_4_COLUMNS_BRIEF_0_DTO.getName(), table3pk.getColumn().getName()); + assertEquals(TABLE_4_COLUMNS_BRIEF_0_DTO.getInternalName(), table3pk.getColumn().getInternalName()); + assertEquals(ColumnTypeDto.TIMESTAMP, table3pk.getColumn().getColumnType()); + assertNull(table3pk.getColumn().getAlias()); + assertEquals(TABLE_4_ID, table3pk.getColumn().getTableId()); + assertEquals(DATABASE_1_ID, table3pk.getColumn().getDatabaseId()); + } + + public static Stream<Arguments> nameToInternalName_parameters() { + return Stream.of( + Arguments.arguments("dash_minus", "OE/NO-027", "oeno-027"), + Arguments.arguments("percent", "OE%NO-027", "oeno-027"), + Arguments.arguments("umlaut", "OE/NÖ-027", "oeno-027"), + Arguments.arguments("dot", "OE.NO-027", "oeno-027") + ); + } + + @ParameterizedTest + @MethodSource("nameToInternalName_parameters") + public void nameToInternalName_succeeds(String name, String request, String compare) { + + /* test */ + final String response = metadataMapper.nameToInternalName(request); + assertEquals(compare, response); + } + + @Test + public void userEquals_fails() { + + /* test */ + assertNotEquals(USER_1_DTO, USER_2_DTO); + } + + @Test + public void userEquals_identity_succeeds() { + + /* test */ + assertEquals(USER_1_DTO, USER_1_DTO); + } + + @Test + public void userEquals_similar_succeeds() { + final UserDto tmp = UserDto.builder() + .id(USER_1_ID) + .build(); + + /* test */ + assertEquals(USER_1_DTO, tmp); + } + + @Test + public void userToUserBriefDto_succeeds() { + + /* test */ + final UserBriefDto response = metadataMapper.userToUserBriefDto(USER_1); + assertEquals(USER_1_NAME, response.getName()); + assertEquals(USER_1_NAME + " — @" + USER_1_USERNAME, response.getQualifiedName()); + } + + @Test + public void userToUserDto_succeeds() { + + /* test */ + final UserDto response = metadataMapper.userToUserDto(USER_1); + assertEquals(USER_1_NAME, response.getName()); + assertEquals(USER_1_NAME + " — @" + USER_1_USERNAME, response.getQualifiedName()); + } + + @Test + public void viewToViewDto_succeeds() { + + /* test */ + final ViewDto response = metadataMapper.viewToViewDto(VIEW_1); + assertEquals(VIEW_1_ID, response.getId()); + assertEquals(VIEW_1_DATABASE_ID, response.getVdbid()); + assertEquals(VIEW_1_NAME, response.getName()); + assertEquals(VIEW_1_INTERNAL_NAME, response.getInternalName()); + assertNotNull(response.getDatabase()); + assertEquals(VIEW_1_DATABASE_ID, response.getDatabase().getId()); + assertEquals(VIEW_1_QUERY, response.getQuery()); + assertEquals(VIEW_1_QUERY_HASH, response.getQueryHash()); + assertNotNull(response.getIdentifiers()); + assertEquals(1, response.getIdentifiers().size()); + final IdentifierDto identifier0 = response.getIdentifiers().get(0); + assertEquals(IDENTIFIER_3_ID, identifier0.getId()); + assertEquals(VIEW_1_DATABASE_ID, identifier0.getDatabaseId()); + assertEquals(VIEW_1_ID, identifier0.getViewId()); + assertEquals(VIEW_1_QUERY, identifier0.getQuery()); + assertEquals(VIEW_1_QUERY_HASH, identifier0.getQueryHash()); + } + +} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/StoreMapperUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/StoreMapperUnitTest.java deleted file mode 100644 index 202c1cf224..0000000000 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/StoreMapperUnitTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.test.AbstractUnitTest; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; - -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Log4j2 -public class StoreMapperUnitTest extends AbstractUnitTest { - - private final DateTimeFormatter mariaDbFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSS]") - .withZone(ZoneId.of("UTC")); - - @Test - public void mapMariaDbInstant_succeeds() { - final String timestamp = "2023-01-08 08:49:29"; - final Instant compare = Instant.ofEpochSecond(1673167769); - - /* test */ - final Instant response = LocalDateTime.parse(timestamp, mariaDbFormatter) - .atZone(ZoneId.of("UTC")) - .toInstant(); - assertEquals(compare, response); - } - -} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/TableMapperUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/TableMapperUnitTest.java deleted file mode 100644 index b02d660e0b..0000000000 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/TableMapperUnitTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.test.AbstractUnitTest; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Log4j2 -@EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class) -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class TableMapperUnitTest extends AbstractUnitTest { - - @Autowired - private TableMapper tableMapper; - - public static Stream<Arguments> nameToInternalName_parameters() { - return Stream.of( - Arguments.arguments("dash_minus", "OE/NO-027", "oe_no_027"), - Arguments.arguments("percent", "OE%NO-027", "oe_no_027"), - Arguments.arguments("umlaut", "OE/NÖ-027", "oe_no__027"), - Arguments.arguments("dot", "OE.NO-027", "oe_no_027") - ); - } - - @ParameterizedTest - @MethodSource("nameToInternalName_parameters") - public void nameToInternalName_succeeds(String name, String request, String compare) { - - /* test */ - final String response = tableMapper.nameToInternalName(request); - assertEquals(compare, response); - } - -} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/UserMapperUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/UserMapperUnitTest.java deleted file mode 100644 index dab115605f..0000000000 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/UserMapperUnitTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.test.AbstractUnitTest; -import at.tuwien.api.user.UserBriefDto; -import at.tuwien.api.user.UserDto; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; - -@Log4j2 -@SpringBootTest -public class UserMapperUnitTest extends AbstractUnitTest { - - @Autowired - private UserMapper userMapper; - - @Test - public void equals_fails() { - - /* test */ - assertNotEquals(USER_1_DTO, USER_2_DTO); - } - - @Test - public void equals_identity_succeeds() { - - /* test */ - assertEquals(USER_1_DTO, USER_1_DTO); - } - - @Test - public void equals_similar_succeeds() { - final UserDto tmp = UserDto.builder() - .id(USER_1_ID) - .build(); - - /* test */ - assertEquals(USER_1_DTO, tmp); - } - - @Test - public void userToUserBriefDto_succeeds() { - - /* test */ - final UserBriefDto response = userMapper.userToUserBriefDto(USER_1); - assertEquals(USER_1_NAME, response.getName()); - assertEquals(USER_1_NAME + " — @" + USER_1_USERNAME, response.getQualifiedName()); - } - - @Test - public void userToUserDto_succeeds() { - - /* test */ - final UserDto response = userMapper.userToUserDto(USER_1); - assertEquals(USER_1_NAME, response.getName()); - assertEquals(USER_1_NAME + " — @" + USER_1_USERNAME, response.getQualifiedName()); - } - -} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/ViewMapperUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/ViewMapperUnitTest.java deleted file mode 100644 index 07a7098264..0000000000 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mapper/ViewMapperUnitTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.database.ViewDto; -import at.tuwien.api.identifier.IdentifierDto; -import at.tuwien.test.AbstractUnitTest; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@Log4j2 -@SpringBootTest -public class ViewMapperUnitTest extends AbstractUnitTest { - - @Autowired - private ViewMapper viewMapper; - - @BeforeEach - public void beforeEach() { - genesis(); - } - - @Test - public void viewToViewDto_succeeds() { - - /* test */ - final ViewDto response = viewMapper.viewToViewDto(VIEW_1); - assertEquals(VIEW_1_ID, response.getId()); - assertEquals(VIEW_1_DATABASE_ID, response.getVdbid()); - assertEquals(VIEW_1_NAME, response.getName()); - assertEquals(VIEW_1_INTERNAL_NAME, response.getInternalName()); - assertNotNull(response.getDatabase()); - assertEquals(VIEW_1_DATABASE_ID, response.getDatabase().getId()); - assertEquals(VIEW_1_QUERY, response.getQuery()); - assertEquals(VIEW_1_QUERY_HASH, response.getQueryHash()); - assertNotNull(response.getIdentifiers()); - assertEquals(1, response.getIdentifiers().size()); - final IdentifierDto identifier0 = response.getIdentifiers().get(0); - assertEquals(IDENTIFIER_3_ID, identifier0.getId()); - assertEquals(VIEW_1_DATABASE_ID, identifier0.getDatabaseId()); - assertEquals(VIEW_1_ID, identifier0.getViewId()); - assertEquals(VIEW_1_QUERY, identifier0.getQuery()); - assertEquals(VIEW_1_QUERY_HASH, identifier0.getQueryHash()); - } - -} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServicePersistenceTest.java index ed5f21281d..1d6d1c30e4 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServicePersistenceTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServicePersistenceTest.java @@ -5,7 +5,6 @@ import at.tuwien.exception.*; import at.tuwien.gateway.DataServiceGateway; import at.tuwien.gateway.SearchServiceGateway; import at.tuwien.repository.*; -import at.tuwien.service.impl.DatabaseServiceImpl; import at.tuwien.test.AbstractUnitTest; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; @@ -17,7 +16,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -64,7 +62,6 @@ public class DatabaseServicePersistenceTest extends AbstractUnitTest { } @Test - @Transactional(readOnly = true) public void findById_succeeds() throws DatabaseNotFoundException { /* test */ diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java index 0e74c54947..6dfcb5187e 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java @@ -182,7 +182,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { assertTrue(column0.getAutoGenerated()); final TableColumn column1 = response.getColumns().get(1); assertEquals("I Am Späshül", column1.getName()); - assertEquals("i_am_spa_shu_l", column1.getInternalName()); + assertEquals("i_am_spashul", column1.getInternalName()); assertEquals(TableColumnType.TEXT, column1.getColumnType()); assertTrue(column1.getIsNullAllowed()); assertFalse(column1.getAutoGenerated()); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/config/JacksonConfig.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/config/JacksonConfig.java index c4944a4691..a451032e9d 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/config/JacksonConfig.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/config/JacksonConfig.java @@ -3,6 +3,7 @@ package at.tuwien.config; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.hibernate6.Hibernate6Module; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import lombok.extern.slf4j.Slf4j; @@ -21,6 +22,7 @@ public class JacksonConfig { final ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new Jdk8Module()); objectMapper.registerModule(new JavaTimeModule()); + objectMapper.registerModule(new Hibernate6Module()); /* lazy load mapping on REST endpoints */ objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); objectMapper.setTimeZone(TimeZone.getTimeZone("UTC")); log.debug("current time is {}", objectMapper.writeValueAsString(new Date())); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java index 0e96a47b70..1ad9cc46c0 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java @@ -6,7 +6,7 @@ import at.tuwien.api.user.UserPasswordDto; import at.tuwien.config.KeycloakConfig; import at.tuwien.exception.*; import at.tuwien.gateway.KeycloakGateway; -import at.tuwien.mapper.UserMapper; +import at.tuwien.mapper.MetadataMapper; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.*; @@ -24,15 +24,15 @@ import java.util.UUID; @Service public class KeycloakGatewayImpl implements KeycloakGateway { - private final UserMapper userMapper; private final RestTemplate restTemplate; private final KeycloakConfig keycloakConfig; + private final MetadataMapper metadataMapper; - public KeycloakGatewayImpl(UserMapper userMapper, @Qualifier("keycloakRestTemplate") RestTemplate restTemplate, - KeycloakConfig keycloakConfig) { - this.userMapper = userMapper; + public KeycloakGatewayImpl(@Qualifier("keycloakRestTemplate") RestTemplate restTemplate, + KeycloakConfig keycloakConfig, MetadataMapper metadataMapper) { this.restTemplate = restTemplate; this.keycloakConfig = keycloakConfig; + this.metadataMapper = metadataMapper; } public TokenDto obtainToken() throws ServiceConnectionException, ServiceException { @@ -192,7 +192,7 @@ public class KeycloakGatewayImpl implements KeycloakGateway { /* obtain admin token */ final HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", "Bearer " + obtainToken().getAccessToken()); - final UpdateCredentialsDto payload = userMapper.passwordToUpdateCredentialsDto(data.getPassword()); + final UpdateCredentialsDto payload = metadataMapper.passwordToUpdateCredentialsDto(data.getPassword()); final String url = keycloakConfig.getKeycloakEndpoint() + "/admin/realms/dbrepo/users/" + id; log.debug("update user credentials at url {}", url); final ResponseEntity<Void> response; diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/SearchServiceGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/SearchServiceGatewayImpl.java index b0aa338883..49a2a1423c 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/SearchServiceGatewayImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/SearchServiceGatewayImpl.java @@ -2,13 +2,10 @@ package at.tuwien.gateway.impl; import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.table.constraints.unique.UniqueDto; import at.tuwien.entities.database.Database; -import at.tuwien.entities.database.View; import at.tuwien.exception.*; import at.tuwien.gateway.SearchServiceGateway; -import at.tuwien.mapper.DatabaseMapper; -import at.tuwien.mapper.TableMapper; +import at.tuwien.mapper.MetadataMapper; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -26,63 +23,27 @@ import java.util.List; @Service public class SearchServiceGatewayImpl implements SearchServiceGateway { - private final TableMapper tableMapper; private final RestTemplate restTemplate; - private final DatabaseMapper databaseMapper; + private final MetadataMapper metadataMapper; @Autowired - public SearchServiceGatewayImpl(TableMapper tableMapper, - @Qualifier("searchServiceRestTemplate") RestTemplate restTemplate, - DatabaseMapper databaseMapper) { - this.tableMapper = tableMapper; + public SearchServiceGatewayImpl(@Qualifier("searchServiceRestTemplate") RestTemplate restTemplate, + MetadataMapper metadataMapper) { this.restTemplate = restTemplate; - this.databaseMapper = databaseMapper; + this.metadataMapper = metadataMapper; } @Override public DatabaseDto update(Database database) throws SearchServiceConnectionException, SearchServiceException, DatabaseNotFoundException { final ResponseEntity<DatabaseDto> response; - final DatabaseDto payload = databaseMapper.databaseToDatabaseDto(database); - payload.getTables() - .forEach(table -> { - table.setIsPublic(database.getIsPublic()); - table.getColumns() - .forEach(column -> { - column.setTable(table); - column.setTableId(table.getId()); - column.setDatabaseId(payload.getId()); - column.setIsPublic(payload.getIsPublic()); - }); - table.getConstraints() - .getUniques() - .forEach(uk -> { - uk.setTable(tableMapper.tableDtoToTableBriefDto(table)); - uk.getTable().setDatabaseId(database.getId()); - uk.setColumns(new LinkedList<>()); -// uk.getColumns() -// .forEach(column -> { -// column.setTable(table); -// column.setTableId(table.getId()); -// column.setDatabaseId(database.getId()); -// column.setIsPublic(database.getIsPublic()); -// }); - }); - }); - payload.getViews() - .stream() - .map(ViewDto::getColumns) - .flatMap(List::stream) - .forEach(columns -> { - columns.setIsPublic(database.getIsPublic()); - columns.setDatabaseId(database.getId()); - }); final HttpHeaders headers = new HttpHeaders(); headers.set("Accept", "application/json"); headers.set("Content-Type", "application/json"); final String url = "/api/search/database/" + database.getId(); log.debug("update database in search service"); try { - response = restTemplate.exchange(url, HttpMethod.PUT, new HttpEntity<>(payload, headers), DatabaseDto.class); + response = restTemplate.exchange(url, HttpMethod.PUT, new HttpEntity<>( + metadataMapper.customDatabaseToDatabaseDto(database), headers), DatabaseDto.class); } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable | HttpServerErrorException.InternalServerError e) { log.error("Failed to update database: {}", e.getMessage()); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java index 57bb2c9df7..5de1366e9f 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java @@ -7,7 +7,7 @@ import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.DataServiceGateway; import at.tuwien.gateway.SearchServiceGateway; -import at.tuwien.mapper.DatabaseMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.repository.DatabaseRepository; import at.tuwien.service.AccessService; import at.tuwien.service.DatabaseService; @@ -23,17 +23,17 @@ import java.util.Optional; @Service public class AccessServiceImpl implements AccessService { - private final DatabaseMapper databaseMapper; + private final MetadataMapper metadataMapper; private final DatabaseService databaseService; private final DatabaseRepository databaseRepository; private final DataServiceGateway dataServiceGateway; private final SearchServiceGateway searchServiceGateway; @Autowired - public AccessServiceImpl(DatabaseMapper databaseMapper, DatabaseService databaseService, + public AccessServiceImpl(MetadataMapper metadataMapper, DatabaseService databaseService, DatabaseRepository databaseRepository, DataServiceGateway dataServiceGateway, SearchServiceGateway searchServiceGateway) { - this.databaseMapper = databaseMapper; + this.metadataMapper = metadataMapper; this.databaseService = databaseService; this.databaseRepository = databaseRepository; this.dataServiceGateway = dataServiceGateway; @@ -73,7 +73,7 @@ public class AccessServiceImpl implements AccessService { .hdbid(database.getId()) .database(database) .huserid(user.getId()) - .type(databaseMapper.accessTypeDtoToAccessType(access)) + .type(metadataMapper.accessTypeDtoToAccessType(access)) .build()); database = databaseRepository.save(database); /* create in search service */ @@ -94,7 +94,7 @@ public class AccessServiceImpl implements AccessService { .database(database) .huserid(user.getId()) .user(user) - .type(databaseMapper.accessTypeDtoToAccessType(access)) + .type(metadataMapper.accessTypeDtoToAccessType(access)) .build(); final int idx = database.getAccesses().indexOf(entity); if (idx == -1) { diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java index c47c93c867..6fa8b25056 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java @@ -8,7 +8,7 @@ import at.tuwien.api.user.UserPasswordDto; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.KeycloakGateway; -import at.tuwien.mapper.UserMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.AuthenticationService; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; @@ -20,19 +20,19 @@ import java.util.UUID; @Service public class AuthenticationServiceImpl implements AuthenticationService { - private final UserMapper userMapper; + private final MetadataMapper metadataMapper; private final KeycloakGateway keycloakGateway; @Autowired - public AuthenticationServiceImpl(UserMapper userMapper, KeycloakGateway keycloakGateway) { - this.userMapper = userMapper; + public AuthenticationServiceImpl(MetadataMapper metadataMapper, KeycloakGateway keycloakGateway) { + this.metadataMapper = metadataMapper; this.keycloakGateway = keycloakGateway; } @Override public void create(SignupRequestDto data) throws UserExistsException, ServiceException, ServiceConnectionException, EmailExistsException { - keycloakGateway.createUser(userMapper.signupRequestDtoToUserCreateDto(data)); + keycloakGateway.createUser(metadataMapper.signupRequestDtoToUserCreateDto(data)); } @Override diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/BannerMessageServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/BannerMessageServiceImpl.java index 86d28ddde2..ac46523250 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/BannerMessageServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/BannerMessageServiceImpl.java @@ -4,7 +4,7 @@ import at.tuwien.api.maintenance.BannerMessageCreateDto; import at.tuwien.api.maintenance.BannerMessageUpdateDto; import at.tuwien.entities.maintenance.BannerMessage; import at.tuwien.exception.MessageNotFoundException; -import at.tuwien.mapper.BannerMessageMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.repository.BannerMessageRepository; import at.tuwien.service.BannerMessageService; import lombok.extern.log4j.Log4j2; @@ -18,13 +18,13 @@ import java.util.Optional; @Service public class BannerMessageServiceImpl implements BannerMessageService { - private final BannerMessageMapper bannerMessageMapper; + private final MetadataMapper metadataMapper; private final BannerMessageRepository bannerMessageRepository; @Autowired - public BannerMessageServiceImpl(BannerMessageMapper bannerMessageMapper, + public BannerMessageServiceImpl(MetadataMapper metadataMapper, BannerMessageRepository bannerMessageRepository) { - this.bannerMessageMapper = bannerMessageMapper; + this.metadataMapper = metadataMapper; this.bannerMessageRepository = bannerMessageRepository; } @@ -50,7 +50,7 @@ public class BannerMessageServiceImpl implements BannerMessageService { @Override public BannerMessage create(BannerMessageCreateDto data) { - final BannerMessage entity = bannerMessageMapper.bannerMessageCreateDtoToBannerMessage(data); + final BannerMessage entity = metadataMapper.bannerMessageCreateDtoToBannerMessage(data); final BannerMessage message = bannerMessageRepository.save(entity); log.info("Created banner message with id {}", message.getId()); return message; @@ -61,7 +61,7 @@ public class BannerMessageServiceImpl implements BannerMessageService { message.setMessage(data.getMessage()); message.setDisplayEnd(data.getDisplayEnd()); message.setDisplayStart(data.getDisplayStart()); - message.setType(bannerMessageMapper.bannerMessageTypeDtoToBannerMessageType(data.getType())); + message.setType(metadataMapper.bannerMessageTypeDtoToBannerMessageType(data.getType())); message = bannerMessageRepository.save(message); log.info("Updated banner message with id {}", message.getId()); return message; diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java index 80c564c989..91db7736db 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java @@ -6,7 +6,7 @@ import at.tuwien.entities.container.image.ContainerImage; import at.tuwien.exception.ContainerAlreadyExistsException; import at.tuwien.exception.ContainerNotFoundException; import at.tuwien.exception.ImageNotFoundException; -import at.tuwien.mapper.ContainerMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.repository.ContainerRepository; import at.tuwien.repository.ImageRepository; import at.tuwien.service.ContainerService; @@ -24,14 +24,14 @@ import java.util.Optional; @Service public class ContainerServiceImpl implements ContainerService { - private final ContainerMapper containerMapper; + private final MetadataMapper metadataMapper; private final ImageRepository imageRepository; private final ContainerRepository containerRepository; @Autowired - public ContainerServiceImpl(ContainerMapper containerMapper, ImageRepository imageRepository, + public ContainerServiceImpl(MetadataMapper metadataMapper, ImageRepository imageRepository, ContainerRepository containerRepository) { - this.containerMapper = containerMapper; + this.metadataMapper = metadataMapper; this.imageRepository = imageRepository; this.containerRepository = containerRepository; } @@ -40,9 +40,9 @@ public class ContainerServiceImpl implements ContainerService { @Transactional public Container create(ContainerCreateDto data) throws ImageNotFoundException, ContainerAlreadyExistsException { + final String containerName = "dbrepo-userdb-" + metadataMapper.nameToInternalName(data.getName()); /* check */ - final Optional<Container> optional = containerRepository.findByInternalName( - containerMapper.containerToInternalContainerName(data.getName())); + final Optional<Container> optional = containerRepository.findByInternalName(containerName); if (optional.isPresent()) { log.error("Failed to create container with name {}: exists in metadata database", data.getName()); throw new ContainerAlreadyExistsException("Failed to create container: exists in metadata database"); @@ -56,7 +56,7 @@ public class ContainerServiceImpl implements ContainerService { Container container = Container.builder() .image(optional2.get()) .name(data.getName()) - .internalName(containerMapper.containerToInternalContainerName(data.getName())) + .internalName(containerName) .host(data.getHost()) .port(data.getPort()) .privilegedUsername(data.getPrivilegedUsername()) diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java index bb5691514a..87f178b1c0 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java @@ -15,8 +15,7 @@ import at.tuwien.entities.database.Database; import at.tuwien.entities.identifier.Identifier; import at.tuwien.entities.user.User; import at.tuwien.exception.*; -import at.tuwien.mapper.DataCiteMapper; -import at.tuwien.mapper.IdentifierMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.repository.IdentifierRepository; import at.tuwien.service.IdentifierService; import lombok.extern.slf4j.Slf4j; @@ -42,7 +41,7 @@ public class DataCiteIdentifierServiceImpl implements IdentifierService { private final RestTemplate restTemplate; private final DataCiteConfig dataCiteConfig; - private final DataCiteMapper dataCiteMapper; + private final MetadataMapper metadataMapper; private final EndpointConfig endpointConfig; private final IdentifierService identifierService; private final IdentifierRepository identifierRepository; @@ -51,12 +50,12 @@ public class DataCiteIdentifierServiceImpl implements IdentifierService { }; public DataCiteIdentifierServiceImpl(@Qualifier("dataCiteRestTemplate") RestTemplate restTemplate, - DataCiteConfig dataCiteConfig, DataCiteMapper dataCiteMapper, + DataCiteConfig dataCiteConfig, MetadataMapper metadataMapper, EndpointConfig endpointConfig, IdentifierServiceImpl identifierService, IdentifierRepository identifierRepository) { this.restTemplate = restTemplate; this.dataCiteConfig = dataCiteConfig; - this.dataCiteMapper = dataCiteMapper; + this.metadataMapper = metadataMapper; this.endpointConfig = endpointConfig; this.identifierService = identifierService; this.identifierRepository = identifierRepository; @@ -130,7 +129,7 @@ public class DataCiteIdentifierServiceImpl implements IdentifierService { DataCiteBody.<DataCiteCreateDoi>builder() .data(DataCiteData.<DataCiteCreateDoi>builder() .type("dois") - .attributes(dataCiteMapper.identifierToDataCiteCreateDoi(identifier, + .attributes(metadataMapper.identifierToDataCiteCreateDoi(identifier, endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId(), dataCiteConfig.getPrefix(), event)) .build()) diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java index f695267a4a..c4a8187b52 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java @@ -6,7 +6,6 @@ import at.tuwien.api.database.DatabaseModifyVisibilityDto; import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.internal.CreateDatabaseDto; import at.tuwien.api.database.table.TableDto; -import at.tuwien.api.database.table.columns.ColumnDto; import at.tuwien.api.database.table.constraints.primary.PrimaryKeyDto; import at.tuwien.api.user.internal.UpdateUserPasswordDto; import at.tuwien.entities.container.Container; @@ -18,9 +17,7 @@ import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.DataServiceGateway; import at.tuwien.gateway.SearchServiceGateway; -import at.tuwien.mapper.DatabaseMapper; -import at.tuwien.mapper.TableMapper; -import at.tuwien.mapper.ViewMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.repository.DatabaseRepository; import at.tuwien.service.*; import lombok.extern.log4j.Log4j2; @@ -35,21 +32,17 @@ import java.util.*; @Service public class DatabaseServiceImpl implements DatabaseService { - private final ViewMapper viewMapper; - private final TableMapper tableMapper; - private final DatabaseMapper databaseMapper; + private final MetadataMapper metadataMapper; private final ContainerService containerService; private final DatabaseRepository databaseRepository; private final DataServiceGateway dataServiceGateway; private final SearchServiceGateway searchServiceGateway; @Autowired - public DatabaseServiceImpl(ViewMapper viewMapper, TableMapper tableMapper, DatabaseMapper databaseMapper, - ContainerService containerService, DatabaseRepository databaseRepository, - DataServiceGateway dataServiceGateway, SearchServiceGateway searchServiceGateway) { - this.viewMapper = viewMapper; - this.tableMapper = tableMapper; - this.databaseMapper = databaseMapper; + public DatabaseServiceImpl(MetadataMapper metadataMapper, ContainerService containerService, + DatabaseRepository databaseRepository, DataServiceGateway dataServiceGateway, + SearchServiceGateway searchServiceGateway) { + this.metadataMapper = metadataMapper; this.containerService = containerService; this.databaseRepository = databaseRepository; this.dataServiceGateway = dataServiceGateway; @@ -96,7 +89,7 @@ public class DatabaseServiceImpl implements DatabaseService { Database database = Database.builder() .isPublic(data.getIsPublic()) .name(data.getName()) - .internalName(databaseMapper.nameToInternalName(data.getName()) + "_" + RandomStringUtils.randomAlphabetic(4).toLowerCase()) + .internalName(metadataMapper.nameToInternalName(data.getName()) + "_" + RandomStringUtils.randomAlphabetic(4).toLowerCase()) .cid(data.getCid()) .container(container) .ownedBy(user.getId()) @@ -206,7 +199,7 @@ public class DatabaseServiceImpl implements DatabaseService { continue; } log.debug("fetched unknown table from data service: {}.{}", database.getInternalName(), table.getInternalName()); - final Table tableEntity = tableMapper.tableDtoToTable(table); + final Table tableEntity = metadataMapper.tableDtoToTable(table); tableEntity.setDatabase(database); tableEntity.getColumns() .forEach(column -> { @@ -267,7 +260,7 @@ public class DatabaseServiceImpl implements DatabaseService { continue; } log.debug("fetched unknown view from data service: {}.{}", database.getInternalName(), view.getInternalName()); - final View viewEntity = viewMapper.viewDtoToView(view); + final View viewEntity = metadataMapper.viewDtoToView(view); viewEntity.setDatabase(database); for (ViewColumn column : viewEntity.getColumns()) { column.setView(viewEntity); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/EntityServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/EntityServiceImpl.java index 6dee3f7d71..dba30481f5 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/EntityServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/EntityServiceImpl.java @@ -7,10 +7,9 @@ import at.tuwien.entities.database.table.Table; import at.tuwien.entities.database.table.columns.TableColumn; import at.tuwien.entities.semantics.Ontology; import at.tuwien.exception.*; -import at.tuwien.mapper.OntologyMapper; +import at.tuwien.mapper.SparqlMapper; import at.tuwien.service.EntityService; import at.tuwien.service.OntologyService; -import at.tuwien.service.TableService; import lombok.extern.log4j.Log4j2; import org.apache.jena.query.*; import org.apache.jena.rdf.model.RDFNode; @@ -23,7 +22,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Optional; import java.util.concurrent.TimeUnit; @Log4j2 @@ -32,11 +30,11 @@ public class EntityServiceImpl implements EntityService { private final Dataset dataset; private final JenaConfig jenaConfig; - private final OntologyMapper ontologyMapper; + private final SparqlMapper ontologyMapper; private final OntologyService ontologyService; @Autowired - public EntityServiceImpl(Dataset dataset, JenaConfig jenaConfig, OntologyMapper ontologyMapper, + public EntityServiceImpl(Dataset dataset, JenaConfig jenaConfig, SparqlMapper ontologyMapper, OntologyService ontologyService) { this.dataset = dataset; this.jenaConfig = jenaConfig; diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java index a41e36877a..37215d0787 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java @@ -14,7 +14,6 @@ import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.DataServiceGateway; import at.tuwien.gateway.SearchServiceGateway; -import at.tuwien.mapper.IdentifierMapper; import at.tuwien.mapper.MetadataMapper; import at.tuwien.repository.IdentifierRepository; import at.tuwien.service.*; @@ -39,21 +38,18 @@ public class IdentifierServiceImpl implements IdentifierService { private final MetadataConfig metadataConfig; private final MetadataMapper metadataMapper; private final TemplateEngine templateEngine; - private final IdentifierMapper identifierMapper; private final DataServiceGateway dataServiceGateway; private final IdentifierRepository identifierRepository; private final SearchServiceGateway searchServiceGateway; public IdentifierServiceImpl(ViewService viewService, TemplateEngine templateEngine, MetadataMapper metadataMapper, - IdentifierMapper identifierMapper, MetadataConfig metadataConfig, - DataServiceGateway dataServiceGateway, IdentifierRepository identifierRepository, - SearchServiceGateway searchServiceGateway) { + MetadataConfig metadataConfig, DataServiceGateway dataServiceGateway, + IdentifierRepository identifierRepository, SearchServiceGateway searchServiceGateway) { this.viewService = viewService; this.metadataConfig = metadataConfig; this.metadataMapper = metadataMapper; this.templateEngine = templateEngine; - this.identifierMapper = identifierMapper; this.dataServiceGateway = dataServiceGateway; this.identifierRepository = identifierRepository; this.searchServiceGateway = searchServiceGateway; @@ -117,7 +113,7 @@ public class IdentifierServiceImpl implements IdentifierService { if (type != null) { log.trace("filter by type: {}", type); stream = stream.filter(i -> Objects.nonNull(i.getType())) - .filter(i -> i.getType().equals(identifierMapper.identifierTypeDtoToIdentifierType(type))); + .filter(i -> i.getType().equals(metadataMapper.identifierTypeDtoToIdentifierType(type))); } if (databaseId != null) { log.trace("filter by database id: {}", databaseId); @@ -171,20 +167,20 @@ public class IdentifierServiceImpl implements IdentifierService { identifier.setQueryId(data.getQueryId()); identifier.setViewId(data.getViewId()); identifier.setDoi(data.getDoi()); - identifier.setLanguage(identifierMapper.languageTypeDtoToLanguageType(data.getLanguage())); + identifier.setLanguage(metadataMapper.languageTypeDtoToLanguageType(data.getLanguage())); identifier.setLicenses(new LinkedList<>(data.getLicenses() .stream() - .map(identifierMapper::licenseDtoToLicense) + .map(metadataMapper::licenseDtoToLicense) .toList())); identifier.setPublicationDay(data.getPublicationDay()); identifier.setPublicationMonth(data.getPublicationMonth()); identifier.setPublicationYear(data.getPublicationYear()); - identifier.setType(identifierMapper.identifierTypeDtoToIdentifierType(data.getType())); + identifier.setType(metadataMapper.identifierTypeDtoToIdentifierType(data.getType())); /* create in metadata database */ if (data.getCreators() != null) { identifier.setCreators(new LinkedList<>(data.getCreators() .stream() - .map(identifierMapper::creatorCreateDtoToCreator) + .map(metadataMapper::creatorCreateDtoToCreator) .peek(c -> c.setIdentifier(identifier)) .toList())); log.debug("set {} creator(s)", identifier.getCreators().size()); @@ -192,7 +188,7 @@ public class IdentifierServiceImpl implements IdentifierService { if (data.getRelatedIdentifiers() != null) { identifier.setRelatedIdentifiers(new LinkedList<>(data.getRelatedIdentifiers() .stream() - .map(identifierMapper::relatedIdentifierCreateDtoToRelatedIdentifier) + .map(metadataMapper::relatedIdentifierCreateDtoToRelatedIdentifier) .peek(r -> r.setIdentifier(identifier)) .toList())); log.debug("set {} related identifier(s)", identifier.getRelatedIdentifiers().size()); @@ -200,7 +196,7 @@ public class IdentifierServiceImpl implements IdentifierService { if (data.getTitles() != null) { identifier.setTitles(new LinkedList<>(data.getTitles() .stream() - .map(identifierMapper::identifierCreateTitleDtoToIdentifierTitle) + .map(metadataMapper::identifierCreateTitleDtoToIdentifierTitle) .peek(t -> t.setIdentifier(identifier)) .toList())); log.debug("set {} title(s)", identifier.getTitles().size()); @@ -208,7 +204,7 @@ public class IdentifierServiceImpl implements IdentifierService { if (data.getDescriptions() != null) { identifier.setDescriptions(new LinkedList<>(data.getDescriptions() .stream() - .map(identifierMapper::identifierCreateDescriptionDtoToIdentifierDescription) + .map(metadataMapper::identifierCreateDescriptionDtoToIdentifierDescription) .peek(d -> d.setIdentifier(identifier)) .toList())); log.debug("set {} description(s)", identifier.getDescriptions().size()); @@ -216,7 +212,7 @@ public class IdentifierServiceImpl implements IdentifierService { if (data.getFunders() != null) { identifier.setFunders(new LinkedList<>(data.getFunders() .stream() - .map(identifierMapper::identifierFunderSaveDtoToIdentifierFunder) + .map(metadataMapper::identifierFunderSaveDtoToIdentifierFunder) .peek(d -> d.setIdentifier(identifier)) .toList())); log.debug("set {} funder(s)", identifier.getFunders().size()); @@ -229,7 +225,7 @@ public class IdentifierServiceImpl implements IdentifierService { public Identifier create(Database database, User user, IdentifierCreateDto data) throws SearchServiceException, ServiceException, QueryNotFoundException, ServiceConnectionException, DatabaseNotFoundException, SearchServiceConnectionException, IdentifierNotFoundException, ViewNotFoundException { - final Identifier identifier = identifierMapper.identifierCreateDtoToIdentifier(data); + final Identifier identifier = metadataMapper.identifierCreateDtoToIdentifier(data); identifier.setDatabase(database); identifier.setCreatedBy(user.getId()); identifier.setCreator(user); @@ -238,7 +234,7 @@ public class IdentifierServiceImpl implements IdentifierService { if (data.getCreators() != null) { identifier.setCreators(new LinkedList<>(data.getCreators() .stream() - .map(identifierMapper::creatorCreateDtoToCreator) + .map(metadataMapper::creatorCreateDtoToCreator) .peek(c -> c.setIdentifier(identifier)) .toList())); log.debug("set {} creator(s)", identifier.getCreators().size()); @@ -246,7 +242,7 @@ public class IdentifierServiceImpl implements IdentifierService { if (data.getRelatedIdentifiers() != null) { identifier.setRelatedIdentifiers(new LinkedList<>(data.getRelatedIdentifiers() .stream() - .map(identifierMapper::relatedIdentifierCreateDtoToRelatedIdentifier) + .map(metadataMapper::relatedIdentifierCreateDtoToRelatedIdentifier) .peek(r -> r.setIdentifier(identifier)) .toList())); log.debug("set {} related identifier(s)", identifier.getRelatedIdentifiers().size()); @@ -255,7 +251,7 @@ public class IdentifierServiceImpl implements IdentifierService { identifier.setTitles(null); identifier.setTitles(new LinkedList<>(data.getTitles() .stream() - .map(identifierMapper::identifierCreateTitleDtoToIdentifierTitle) + .map(metadataMapper::identifierCreateTitleDtoToIdentifierTitle) .peek(t -> t.setIdentifier(identifier)) .toList())); log.debug("set {} title(s)", identifier.getTitles().size()); @@ -263,7 +259,7 @@ public class IdentifierServiceImpl implements IdentifierService { if (data.getDescriptions() != null) { identifier.setDescriptions(new LinkedList<>(data.getDescriptions() .stream() - .map(identifierMapper::identifierCreateDescriptionDtoToIdentifierDescription) + .map(metadataMapper::identifierCreateDescriptionDtoToIdentifierDescription) .peek(d -> d.setIdentifier(identifier)) .toList())); log.debug("set {} description(s)", identifier.getDescriptions().size()); @@ -271,7 +267,7 @@ public class IdentifierServiceImpl implements IdentifierService { if (data.getFunders() != null) { identifier.setFunders(new LinkedList<>(data.getFunders() .stream() - .map(identifierMapper::identifierFunderSaveDtoToIdentifierFunder) + .map(metadataMapper::identifierFunderSaveDtoToIdentifierFunder) .peek(d -> d.setIdentifier(identifier)) .toList())); log.debug("set {} funder(s)", identifier.getFunders().size()); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ImageServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ImageServiceImpl.java index f7c9dcec9f..8e4decfc17 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ImageServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ImageServiceImpl.java @@ -5,7 +5,7 @@ import at.tuwien.api.container.image.ImageCreateDto; import at.tuwien.entities.container.image.ContainerImage; import at.tuwien.exception.ImageAlreadyExistsException; import at.tuwien.exception.ImageNotFoundException; -import at.tuwien.mapper.ImageMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.repository.ImageRepository; import at.tuwien.service.ImageService; import jakarta.validation.ConstraintViolationException; @@ -23,13 +23,13 @@ import java.util.Optional; @Service public class ImageServiceImpl implements ImageService { - private final ImageMapper imageMapper; + private final MetadataMapper metadataMapper; private final ImageRepository imageRepository; @Autowired - public ImageServiceImpl(ImageRepository imageRepository, ImageMapper imageMapper) { + public ImageServiceImpl(ImageRepository imageRepository, MetadataMapper metadataMapper) { this.imageRepository = imageRepository; - this.imageMapper = imageMapper; + this.metadataMapper = metadataMapper; } @Override @@ -52,7 +52,7 @@ public class ImageServiceImpl implements ImageService { @Override @Transactional public ContainerImage create(ImageCreateDto createDto, Principal principal) throws ImageAlreadyExistsException { - final ContainerImage image = imageMapper.createImageDtoToContainerImage(createDto); + final ContainerImage image = metadataMapper.createImageDtoToContainerImage(createDto); if (imageRepository.findByNameAndVersion(createDto.getName(), createDto.getVersion()).isPresent()) { log.error("Failed to create image {}:{}: exists in the metadata database", createDto.getName(), createDto.getVersion()); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/MetadataServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/MetadataServiceImpl.java index a89188c02c..9dfefd18cc 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/MetadataServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/MetadataServiceImpl.java @@ -10,7 +10,6 @@ import at.tuwien.exception.*; import at.tuwien.gateway.CrossrefGateway; import at.tuwien.gateway.OrcidGateway; import at.tuwien.gateway.RorGateway; -import at.tuwien.mapper.ExternalMapper; import at.tuwien.mapper.MetadataMapper; import at.tuwien.oaipmh.OaiErrorType; import at.tuwien.oaipmh.OaiListIdentifiersParameters; @@ -26,18 +25,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.Writer; import java.time.Instant; import java.util.List; import java.util.Optional; @@ -48,7 +36,6 @@ public class MetadataServiceImpl implements MetadataService { private final RorGateway rorGateway; private final OrcidGateway orcidGateway; - private final ExternalMapper externalMapper; private final MetadataConfig metadataConfig; private final MetadataMapper metadataMapper; private final TemplateEngine templateEngine; @@ -57,13 +44,12 @@ public class MetadataServiceImpl implements MetadataService { private final IdentifierRepository identifierRepository; @Autowired - public MetadataServiceImpl(RorGateway rorGateway, OrcidGateway orcidGateway, ExternalMapper externalMapper, - MetadataConfig metadataConfig, MetadataMapper metadataMapper, - TemplateEngine templateEngine, CrossrefGateway crossrefGateway, - IdentifierService identifierService, IdentifierRepository identifierRepository) { + public MetadataServiceImpl(RorGateway rorGateway, OrcidGateway orcidGateway, MetadataConfig metadataConfig, + MetadataMapper metadataMapper, TemplateEngine templateEngine, + CrossrefGateway crossrefGateway, IdentifierService identifierService, + IdentifierRepository identifierRepository) { this.rorGateway = rorGateway; this.orcidGateway = orcidGateway; - this.externalMapper = externalMapper; this.metadataConfig = metadataConfig; this.metadataMapper = metadataMapper; this.templateEngine = templateEngine; @@ -175,7 +161,7 @@ public class MetadataServiceImpl implements MetadataService { DoiNotFoundException, IdentifierNotSupportedException { if (url.contains("orcid.org")) { final OrcidDto orcidDto = orcidGateway.findByUrl(url); - return externalMapper.orcidDtoToExternalMetadataDto(orcidDto); + return metadataMapper.orcidDtoToExternalMetadataDto(orcidDto); } else if (url.contains("ror.org")) { final int idx = url.lastIndexOf('/'); if (idx + 1 >= url.length()) { @@ -184,7 +170,7 @@ public class MetadataServiceImpl implements MetadataService { } final String id = url.substring(idx + 1); final RorDto rorDto = rorGateway.findById(id); - return externalMapper.rorDtoToExternalMetadataDto(rorDto); + return metadataMapper.rorDtoToExternalMetadataDto(rorDto); } else if (url.contains("doi.org")) { final int idx = url.indexOf("doi.org/"); if (idx + 1 >= url.length()) { @@ -193,7 +179,7 @@ public class MetadataServiceImpl implements MetadataService { } final String id = url.substring(idx + 8); final CrossrefDto crossrefDto = crossrefGateway.findById(id); - return externalMapper.crossrefDtoToExternalMetadataDto(crossrefDto); + return metadataMapper.crossrefDtoToExternalMetadataDto(crossrefDto); } log.error("Failed to find metadata: unsupported identifier {}", url); throw new IdentifierNotSupportedException("Failed to find metadata: unsupported identifier " + url); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/OntologyServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/OntologyServiceImpl.java index 92d1cec924..3f242914e9 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/OntologyServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/OntologyServiceImpl.java @@ -4,7 +4,8 @@ import at.tuwien.api.semantics.OntologyCreateDto; import at.tuwien.api.semantics.OntologyModifyDto; import at.tuwien.entities.semantics.Ontology; import at.tuwien.exception.OntologyNotFoundException; -import at.tuwien.mapper.OntologyMapper; +import at.tuwien.mapper.MetadataMapper; +import at.tuwien.mapper.SparqlMapper; import at.tuwien.repository.OntologyRepository; import at.tuwien.service.OntologyService; import lombok.extern.log4j.Log4j2; @@ -21,12 +22,15 @@ import java.util.Optional; @Service public class OntologyServiceImpl implements OntologyService { - private final OntologyMapper ontologyMapper; + private final SparqlMapper sparqlMapper; + private final MetadataMapper metadataMapper; private final OntologyRepository ontologyRepository; @Autowired - public OntologyServiceImpl(OntologyMapper ontologyMapper, OntologyRepository ontologyRepository) { - this.ontologyMapper = ontologyMapper; + public OntologyServiceImpl(SparqlMapper ontologyMapper, MetadataMapper metadataMapper, + OntologyRepository ontologyRepository) { + this.sparqlMapper = ontologyMapper; + this.metadataMapper = metadataMapper; this.ontologyRepository = ontologyRepository; } @@ -71,7 +75,7 @@ public class OntologyServiceImpl implements OntologyService { @Override public Ontology create(OntologyCreateDto data, Principal principal) { /* delete in metadata database */ - final Ontology entity = ontologyMapper.ontologyCreateDtoToOntology(data); + final Ontology entity = metadataMapper.ontologyCreateDtoToOntology(data); final Ontology ontology = ontologyRepository.save(entity); log.info("Created ontology with id {} ", ontology.getId()); return ontology; diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java index d53e1c0434..bf5ca0a98f 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java @@ -18,8 +18,8 @@ import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.DataServiceGateway; import at.tuwien.gateway.SearchServiceGateway; -import at.tuwien.mapper.OntologyMapper; -import at.tuwien.mapper.TableMapper; +import at.tuwien.mapper.MetadataMapper; +import at.tuwien.mapper.SparqlMapper; import at.tuwien.repository.DatabaseRepository; import at.tuwien.service.*; import lombok.extern.log4j.Log4j2; @@ -34,31 +34,31 @@ import java.util.*; @Service public class TableServiceImpl implements TableService { - private final TableMapper tableMapper; private final UserService userService; private final UnitService unitService; + private final SparqlMapper ontologyMapper; private final RabbitConfig rabbitConfig; private final EntityService entityService; private final ConceptService conceptService; - private final OntologyMapper ontologyMapper; + private final MetadataMapper metadataMapper; private final DatabaseService databaseService; private final DataServiceGateway dataServiceGateway; private final DatabaseRepository databaseRepository; private final SearchServiceGateway searchServiceGateway; @Autowired - public TableServiceImpl(TableMapper tableMapper, UserService userService, UnitService unitService, - RabbitConfig rabbitConfig, EntityService entityService, ConceptService conceptService, - OntologyMapper ontologyMapper, DatabaseService databaseService, - DataServiceGateway dataServiceGateway, DatabaseRepository databaseRepository, + public TableServiceImpl(UserService userService, UnitService unitService, SparqlMapper ontologyMapper, + RabbitConfig rabbitConfig, EntityService entityService, ConceptService conceptService, + MetadataMapper metadataMapper, DatabaseService databaseService, + DataServiceGateway dataServiceGateway, DatabaseRepository databaseRepository, SearchServiceGateway searchServiceGateway) { - this.tableMapper = tableMapper; this.userService = userService; this.unitService = unitService; + this.ontologyMapper = ontologyMapper; this.rabbitConfig = rabbitConfig; this.entityService = entityService; this.conceptService = conceptService; - this.ontologyMapper = ontologyMapper; + this.metadataMapper = metadataMapper; this.databaseService = databaseService; this.dataServiceGateway = dataServiceGateway; this.databaseRepository = databaseRepository; @@ -125,7 +125,7 @@ public class TableServiceImpl implements TableService { final Table table = Table.builder() .isVersioned(true) .name(data.getName()) - .internalName(tableMapper.nameToInternalName(data.getName())) + .internalName(metadataMapper.nameToInternalName(data.getName())) .description(data.getDescription()) .queueName(rabbitConfig.getQueueName()) .tdbid(database.getId()) @@ -143,7 +143,7 @@ public class TableServiceImpl implements TableService { .addAll(data.getColumns() .stream() .map(c -> { - final TableColumn column = tableMapper.columnCreateDtoToTableColumn(c, database.getContainer().getImage()); + final TableColumn column = metadataMapper.columnCreateDtoToTableColumn(c, database.getContainer().getImage()); if (data.isNeedSequence() && column.getName().equals("id")) { column.setAutoGenerated(true); } @@ -165,7 +165,7 @@ public class TableServiceImpl implements TableService { }) .toList()); /* set constraints */ - table.setConstraints(tableMapper.constraintsCreateDtoToConstraints(data.getConstraints(), database, table)); + table.setConstraints(metadataMapper.constraintsCreateDtoToConstraints(data.getConstraints(), database, table)); } catch (IllegalArgumentException e) { throw new MalformedException(e); } @@ -228,7 +228,7 @@ public class TableServiceImpl implements TableService { try { unit = unitService.find(data.getUnitUri()); } catch (UnitNotFoundException e) { - unit = ontologyMapper.entityDtoToTableColumnUnit(entityService.findOneByUri(data.getUnitUri())); + unit = metadataMapper.entityDtoToTableColumnUnit(entityService.findOneByUri(data.getUnitUri())); } column.setUnit(unit); } else { @@ -239,7 +239,7 @@ public class TableServiceImpl implements TableService { try { concept = conceptService.find(data.getConceptUri()); } catch (ConceptNotFoundException e) { - concept = ontologyMapper.entityDtoToTableColumnConcept(entityService.findOneByUri(data.getConceptUri())); + concept = metadataMapper.entityDtoToTableColumnConcept(entityService.findOneByUri(data.getConceptUri())); } column.setConcept(concept); } else { diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ViewServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ViewServiceImpl.java index ec26939199..f9d888b5a0 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ViewServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/ViewServiceImpl.java @@ -8,7 +8,7 @@ import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.DataServiceGateway; import at.tuwien.gateway.SearchServiceGateway; -import at.tuwien.mapper.ViewMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.repository.DatabaseRepository; import at.tuwien.service.ViewService; import com.google.common.hash.Hashing; @@ -26,15 +26,15 @@ import java.util.Optional; @Service public class ViewServiceImpl implements ViewService { - private final ViewMapper viewMapper; + private final MetadataMapper metadataMapper; private final DataServiceGateway dataServiceGateway; private final DatabaseRepository databaseRepository; private final SearchServiceGateway searchServiceGateway; @Autowired - public ViewServiceImpl(ViewMapper viewMapper, DataServiceGateway dataServiceGateway, + public ViewServiceImpl(MetadataMapper metadataMapper, DataServiceGateway dataServiceGateway, DatabaseRepository databaseRepository, SearchServiceGateway searchServiceGateway) { - this.viewMapper = viewMapper; + this.metadataMapper = metadataMapper; this.dataServiceGateway = dataServiceGateway; this.databaseRepository = databaseRepository; this.searchServiceGateway = searchServiceGateway; @@ -91,7 +91,7 @@ public class ViewServiceImpl implements ViewService { .vdbid(database.getId()) .database(database) .name(data.getName()) - .internalName(viewMapper.nameToInternalName(data.getName())) + .internalName(metadataMapper.nameToInternalName(data.getName())) .createdBy(creator.getId()) .creator(creator) .identifiers(new LinkedList<>()) diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java index a780819631..3ba5fc9e36 100644 --- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java +++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java @@ -20,6 +20,7 @@ public abstract class AbstractUnitTest extends BaseTest { /* USER_4 */ USER_5.setAccesses(new LinkedList<>()); /* DATABASE 1 */ + DATABASE_1.setSubsets(new LinkedList<>()); DATABASE_1.setAccesses(new LinkedList<>(List.of(DATABASE_1_USER_1_READ_ACCESS, DATABASE_1_USER_2_WRITE_OWN_ACCESS, DATABASE_1_USER_3_WRITE_ALL_ACCESS))); DATABASE_1_PRIVILEGED_DTO.setAccesses(new LinkedList<>(List.of(DATABASE_1_USER_1_READ_ACCESS_DTO, DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO, DATABASE_1_USER_3_WRITE_ALL_ACCESS_DTO))); TABLE_1.setDatabase(DATABASE_1); @@ -67,6 +68,7 @@ public abstract class AbstractUnitTest extends BaseTest { IDENTIFIER_3.setDatabase(DATABASE_1); IDENTIFIER_4.setDatabase(DATABASE_1); /* DATABASE 2 */ + DATABASE_2.setSubsets(new LinkedList<>()); DATABASE_2.setAccesses(new LinkedList<>(List.of(DATABASE_2_USER_2_WRITE_ALL_ACCESS, DATABASE_2_USER_3_READ_ACCESS))); DATABASE_2.setTables(new LinkedList<>(List.of(TABLE_5, TABLE_6, TABLE_7))); DATABASE_2.setViews(new LinkedList<>(List.of(VIEW_4))); @@ -91,6 +93,7 @@ public abstract class AbstractUnitTest extends BaseTest { VIEW_4.setDatabase(DATABASE_2); IDENTIFIER_5.setDatabase(DATABASE_2); /* DATABASE 3 */ + DATABASE_3.setSubsets(new LinkedList<>()); DATABASE_3.setAccesses(new LinkedList<>(List.of(DATABASE_3_USER_1_WRITE_ALL_ACCESS))); DATABASE_3.setTables(new LinkedList<>(List.of(TABLE_8))); DATABASE_3.setViews(new LinkedList<>(List.of(VIEW_5))); @@ -106,6 +109,7 @@ public abstract class AbstractUnitTest extends BaseTest { VIEW_5.setColumns(VIEW_5_COLUMNS); IDENTIFIER_6.setDatabase(DATABASE_3); /* DATABASE 4 */ + DATABASE_4.setSubsets(new LinkedList<>()); DATABASE_4.setAccesses(new LinkedList<>(List.of(DATABASE_4_USER_1_READ_ACCESS, DATABASE_4_USER_2_WRITE_OWN_ACCESS, DATABASE_4_USER_3_WRITE_ALL_ACCESS))); DATABASE_4.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_7))); IDENTIFIER_7.setDatabase(DATABASE_4); diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java index e0275f5d6e..b786195756 100644 --- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java +++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java @@ -18,10 +18,7 @@ import at.tuwien.api.database.table.TableBriefDto; import at.tuwien.api.database.table.TableCreateDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.TableStatisticDto; -import at.tuwien.api.database.table.columns.ColumnCreateDto; -import at.tuwien.api.database.table.columns.ColumnDto; -import at.tuwien.api.database.table.columns.ColumnStatisticDto; -import at.tuwien.api.database.table.columns.ColumnTypeDto; +import at.tuwien.api.database.table.columns.*; import at.tuwien.api.database.table.columns.concepts.*; import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; import at.tuwien.api.database.table.constraints.ConstraintsDto; @@ -1517,6 +1514,10 @@ public abstract class BaseTest { public final static String TABLE_1_QUEUE_NAME = TABLE_1_INTERNALNAME; public final static String TABLE_1_ROUTING_KEY = "dbrepo\\." + DATABASE_1_ID + "\\." + TABLE_1_ID; public final static Long TABLE_1_DATABASE_ID = DATABASE_1_ID; + public final static Long TABLE_1_AVG_ROW_LENGTH = 3L; + public final static Long TABLE_1_NUM_ROWS = 3L; + public final static Long TABLE_1_DATA_LENGTH = 2000L; + public final static Long TABLE_1_MAX_DATA_LENGTH = Long.MAX_VALUE; public final static Instant TABLE_1_CREATED = Instant.ofEpochSecond(1677399975L) /* 2023-02-26 08:26:15 (UTC) */; public final static Instant TABLE_1_LAST_MODIFIED = Instant.ofEpochSecond(1677399975L) /* 2023-02-26 08:26:15 (UTC) */; @@ -1537,6 +1538,10 @@ public abstract class BaseTest { .createdBy(USER_1_ID) .owner(USER_1_DTO) .isPublic(DATABASE_1_PUBLIC) + .avgRowLength(TABLE_1_AVG_ROW_LENGTH) + .numRows(TABLE_1_NUM_ROWS) + .dataLength(TABLE_1_DATA_LENGTH) + .maxDataLength(TABLE_1_MAX_DATA_LENGTH) .build(); public final static Table TABLE_1 = Table.builder() @@ -1557,6 +1562,10 @@ public abstract class BaseTest { .ownedBy(USER_1_ID) .owner(USER_1) .lastModified(TABLE_1_LAST_MODIFIED) + .avgRowLength(TABLE_1_AVG_ROW_LENGTH) + .numRows(TABLE_1_NUM_ROWS) + .dataLength(TABLE_1_DATA_LENGTH) + .maxDataLength(TABLE_1_MAX_DATA_LENGTH) .build(); public final static TableDto TABLE_1_DTO = TableDto.builder() @@ -1574,6 +1583,10 @@ public abstract class BaseTest { .constraints(null) /* TABLE_1_CONSTRAINT_DTO */ .createdBy(USER_1_ID) .owner(USER_1_DTO) + .avgRowLength(TABLE_1_AVG_ROW_LENGTH) + .numRows(TABLE_1_NUM_ROWS) + .dataLength(TABLE_1_DATA_LENGTH) + .maxDataLength(TABLE_1_MAX_DATA_LENGTH) .build(); public final static List<ColumnDto> TABLE_1_COLUMNS_DTO = List.of(ColumnDto.builder() @@ -1665,6 +1678,10 @@ public abstract class BaseTest { public final static String TABLE_2_ROUTING_KEY = "dbrepo\\." + DATABASE_1_ID + "\\." + TABLE_2_ID; public final static Instant TABLE_2_CREATED = Instant.ofEpochSecond(1677400007L) /* 2023-02-26 08:26:47 (UTC) */; public final static Instant TABLE_2_LAST_MODIFIED = Instant.ofEpochSecond(1677400007L) /* 2023-02-26 08:26:47 (UTC) */; + public final static Long TABLE_2_AVG_ROW_LENGTH = 3L; + public final static Long TABLE_2_NUM_ROWS = 3L; + public final static Long TABLE_2_DATA_LENGTH = 2000L; + public final static Long TABLE_2_MAX_DATA_LENGTH = Long.MAX_VALUE; public final static Table TABLE_2 = Table.builder() .id(TABLE_2_ID) @@ -1679,9 +1696,14 @@ public abstract class BaseTest { .queueName(TABLE_2_QUEUE_NAME) .columns(new LinkedList<>() /* TABLE_2_COLUMNS */) .constraints(null) /* TABLE_2_CONSTRAINTS */ + .creator(USER_2) .createdBy(USER_2_ID) - .ownedBy(USER_2_ID) .owner(USER_2) + .ownedBy(USER_2_ID) + .avgRowLength(TABLE_2_AVG_ROW_LENGTH) + .numRows(TABLE_2_NUM_ROWS) + .dataLength(TABLE_2_DATA_LENGTH) + .maxDataLength(TABLE_2_MAX_DATA_LENGTH) .build(); public final static PrivilegedTableDto TABLE_2_PRIVILEGED_DTO = PrivilegedTableDto.builder() @@ -1698,8 +1720,13 @@ public abstract class BaseTest { .identifiers(new LinkedList<>()) .columns(new LinkedList<>() /* TABLE_2_COLUMNS_DTO */) .constraints(null) /* TABLE_2_CONSTRAINTS_DTO */ - .createdBy(USER_1_ID) - .owner(USER_1_DTO) + .creator(USER_2_DTO) + .createdBy(USER_2_ID) + .owner(USER_2_DTO) + .avgRowLength(TABLE_2_AVG_ROW_LENGTH) + .numRows(TABLE_2_NUM_ROWS) + .dataLength(TABLE_2_DATA_LENGTH) + .maxDataLength(TABLE_2_MAX_DATA_LENGTH) .build(); public final static TableDto TABLE_2_DTO = TableDto.builder() @@ -1714,8 +1741,13 @@ public abstract class BaseTest { .routingKey(TABLE_2_ROUTING_KEY) .columns(new LinkedList<>() /* TABLE_2_COLUMNS_DTO */) .constraints(null) /* TABLE_2_CONSTRAINTS_DTO */ + .creator(USER_2_DTO) .createdBy(USER_2_ID) .owner(USER_2_DTO) + .avgRowLength(TABLE_2_AVG_ROW_LENGTH) + .numRows(TABLE_2_NUM_ROWS) + .dataLength(TABLE_2_DATA_LENGTH) + .maxDataLength(TABLE_2_MAX_DATA_LENGTH) .build(); public final static TableBriefDto TABLE_2_BRIEF_DTO = TableBriefDto.builder() @@ -1738,6 +1770,10 @@ public abstract class BaseTest { public final static String TABLE_3_ROUTING_KEY = "dbrepo\\." + DATABASE_1_ID + "\\." + TABLE_3_ID; public final static Instant TABLE_3_CREATED = Instant.ofEpochSecond(1677400031L) /* 2023-02-26 08:27:11 (UTC) */; public final static Instant TABLE_3_LAST_MODIFIED = Instant.ofEpochSecond(1677400031L) /* 2023-02-26 08:27:11 (UTC) */; + public final static Long TABLE_3_AVG_ROW_LENGTH = 6L; + public final static Long TABLE_3_NUM_ROWS = 6L; + public final static Long TABLE_3_DATA_LENGTH = 1800L; + public final static Long TABLE_3_MAX_DATA_LENGTH = Long.MAX_VALUE; public final static Table TABLE_3 = Table.builder() .id(TABLE_3_ID) @@ -1752,9 +1788,14 @@ public abstract class BaseTest { .queueName(TABLE_3_QUEUE_NAME) .columns(new LinkedList<>() /* TABLE_3_COLUMNS */) .constraints(null) /* TABLE_3_CONSTRAINTS */ + .creator(USER_3) .createdBy(USER_3_ID) - .ownedBy(USER_3_ID) .owner(USER_3) + .ownedBy(USER_3_ID) + .avgRowLength(TABLE_3_AVG_ROW_LENGTH) + .numRows(TABLE_3_NUM_ROWS) + .dataLength(TABLE_3_DATA_LENGTH) + .maxDataLength(TABLE_3_MAX_DATA_LENGTH) .build(); public final static TableDto TABLE_3_DTO = TableDto.builder() @@ -1769,8 +1810,13 @@ public abstract class BaseTest { .routingKey(TABLE_3_ROUTING_KEY) .columns(new LinkedList<>() /* TABLE_3_COLUMNS_DTO */) .constraints(null) /* TABLE_3_CONSTRAINTS_DTO */ + .creator(USER_3_DTO) .createdBy(USER_3_ID) .owner(USER_3_DTO) + .avgRowLength(TABLE_3_AVG_ROW_LENGTH) + .numRows(TABLE_3_NUM_ROWS) + .dataLength(TABLE_3_DATA_LENGTH) + .maxDataLength(TABLE_3_MAX_DATA_LENGTH) .build(); public final static TableBriefDto TABLE_3_BRIEF_DTO = TableBriefDto.builder() @@ -1980,19 +2026,23 @@ public abstract class BaseTest { public final static Long TABLE_4_ID = 4L; public final static String TABLE_4_NAME = "Sensor 2"; - public final static String TABLE_4_INTERNAL_NAME = "sensor_2"; + public final static String TABLE_4_INTERNALNAME = "sensor_2"; public final static Boolean TABLE_4_VERSIONED = true; public final static Boolean TABLE_4_PROCESSED_CONSTRAINTS = true; public final static String TABLE_4_DESCRIPTION = "Hello sensor"; - public final static String TABLE_4_QUEUE_NAME = TABLE_4_INTERNAL_NAME; + public final static String TABLE_4_QUEUE_NAME = TABLE_4_INTERNALNAME; public final static String TABLE_4_ROUTING_KEY = "dbrepo\\." + DATABASE_1_ID + "\\." + TABLE_4_ID; public final static Instant TABLE_4_CREATED = Instant.ofEpochSecond(1677400175L) /* 2023-02-26 08:29:35 (UTC) */; public final static Instant TABLE_4_LAST_MODIFIED = Instant.ofEpochSecond(1677400175L) /* 2023-02-26 08:29:35 (UTC) */; + public final static Long TABLE_4_AVG_ROW_LENGTH = 0L; + public final static Long TABLE_4_NUM_ROWS = 0L; + public final static Long TABLE_4_DATA_LENGTH = 1000L; + public final static Long TABLE_4_MAX_DATA_LENGTH = Long.MAX_VALUE; public final static Table TABLE_4 = Table.builder() .id(TABLE_4_ID) .tdbid(DATABASE_1_ID) - .internalName(TABLE_4_INTERNAL_NAME) + .internalName(TABLE_4_INTERNALNAME) .description(TABLE_4_DESCRIPTION) .database(null /* DATABASE_1 */) .name(TABLE_4_NAME) @@ -2000,17 +2050,22 @@ public abstract class BaseTest { .columns(new LinkedList<>()) /* TABLE_4_COLUMNS */ .constraints(null) /* TABLE_4_CONSTRAINTS */ .isVersioned(TABLE_4_VERSIONED) + .creator(USER_1) .createdBy(USER_1_ID) - .ownedBy(USER_1_ID) .owner(USER_1) + .ownedBy(USER_1_ID) .created(TABLE_4_CREATED) .lastModified(TABLE_4_LAST_MODIFIED) + .avgRowLength(TABLE_4_AVG_ROW_LENGTH) + .numRows(TABLE_4_NUM_ROWS) + .dataLength(TABLE_4_DATA_LENGTH) + .maxDataLength(TABLE_4_MAX_DATA_LENGTH) .build(); public final static TableDto TABLE_4_DTO = TableDto.builder() .id(TABLE_4_ID) .tdbid(DATABASE_1_ID) - .internalName(TABLE_4_INTERNAL_NAME) + .internalName(TABLE_4_INTERNALNAME) .description(TABLE_4_DESCRIPTION) .name(TABLE_4_NAME) .queueName(TABLE_4_QUEUE_NAME) @@ -2018,14 +2073,19 @@ public abstract class BaseTest { .columns(new LinkedList<>()) /* TABLE_4_COLUMNS_DTO */ .constraints(null) /* TABLE_4_CONSTRAINTS_DTO */ .isVersioned(TABLE_4_VERSIONED) + .creator(USER_1_DTO) .createdBy(USER_1_ID) .owner(USER_1_DTO) .created(TABLE_4_CREATED) + .avgRowLength(TABLE_4_AVG_ROW_LENGTH) + .numRows(TABLE_4_NUM_ROWS) + .dataLength(TABLE_4_DATA_LENGTH) + .maxDataLength(TABLE_4_MAX_DATA_LENGTH) .build(); public final static TableBriefDto TABLE_4_BRIEF_DTO = TableBriefDto.builder() .id(TABLE_4_ID) - .internalName(TABLE_4_INTERNAL_NAME) + .internalName(TABLE_4_INTERNALNAME) .description(TABLE_4_DESCRIPTION) .name(TABLE_4_NAME) .columns(new LinkedList<>() /* TABLE_4_COLUMNS */) @@ -2033,6 +2093,13 @@ public abstract class BaseTest { .owner(USER_1_BRIEF_DTO) .build(); + public final static ColumnBriefDto TABLE_4_COLUMNS_BRIEF_0_DTO = ColumnBriefDto.builder() + .id(44L) + .name("Timestamp") + .internalName("timestamp") + .columnType(ColumnTypeDto.TIMESTAMP) + .build(); + public final static List<TableColumn> TABLE_4_COLUMNS = List.of(TableColumn.builder() .id(44L) .ordinalPosition(0) @@ -2339,6 +2406,13 @@ public abstract class BaseTest { public final static List<String> COLUMN_8_2_SET_VALUES = null; public final static List<String> COLUMN_8_2_SET_VALUES_DTO = null; + public final static ColumnBriefDto TABLE_8_COLUMNS_BRIEF_0_DTO = ColumnBriefDto.builder() + .id(COLUMN_8_1_ID) + .name(COLUMN_8_1_NAME) + .internalName(COLUMN_8_1_INTERNAL_NAME) + .columnType(ColumnTypeDto.BIGINT) + .build(); + public final static List<TableColumn> TABLE_8_COLUMNS = List.of(TableColumn.builder() .id(COLUMN_8_1_ID) .ordinalPosition(COLUMN_8_1_ORDINALPOS) @@ -2658,6 +2732,13 @@ public abstract class BaseTest { .isPersisted(QUERY_6_PERSISTED) .build(); + public final static ColumnBriefDto TABLE_1_COLUMNS_BRIEF_0_DTO = ColumnBriefDto.builder() + .id(1L) + .name("id") + .internalName("id") + .columnType(ColumnTypeDto.BIGINT) + .build(); + public final static List<TableColumn> TABLE_1_COLUMNS = List.of(TableColumn.builder() .id(1L) .ordinalPosition(0) @@ -2772,6 +2853,13 @@ public abstract class BaseTest { .sets(null) .build()); + public final static ColumnBriefDto TABLE_2_COLUMNS_BRIEF_0_DTO = ColumnBriefDto.builder() + .id(6L) + .name("location") + .internalName("location") + .columnType(ColumnTypeDto.VARCHAR) + .build(); + public final static List<ColumnDto> TABLE_2_COLUMNS_DTO = List.of(ColumnDto.builder() .id(6L) .table(TABLE_2_DTO) @@ -2814,6 +2902,13 @@ public abstract class BaseTest { .sets(null) .build()); + public final static ColumnBriefDto TABLE_3_COLUMNS_BRIEF_0_DTO = ColumnBriefDto.builder() + .id(9L) + .columnType(ColumnTypeDto.BIGINT) + .name("id") + .internalName("id") + .build(); + public final static List<TableColumn> TABLE_3_COLUMNS = List.of(TableColumn.builder() .id(9L) .table(TABLE_3) @@ -3723,6 +3818,13 @@ public abstract class BaseTest { .sets(new LinkedList<>()) .build()); + public final static ColumnBriefDto TABLE_5_COLUMNS_BRIEF_0_DTO = ColumnBriefDto.builder() + .id(45L) + .name("id") + .internalName("id") + .columnType(ColumnTypeDto.BIGINT) + .build(); + public final static List<TableColumn> TABLE_5_COLUMNS = List.of(TableColumn.builder() .id(45L) .ordinalPosition(0) @@ -4344,6 +4446,13 @@ public abstract class BaseTest { .autoGenerated(false) .build()); + public final static ColumnBriefDto TABLE_6_COLUMNS_BRIEF_0_DTO = ColumnBriefDto.builder() + .id(66L) + .name("id") + .internalName("id") + .columnType(ColumnTypeDto.BIGINT) + .build(); + public final static List<ColumnDto> TABLE_6_COLUMNS_DTO = List.of(ColumnDto.builder() .id(66L) .ordinalPosition(0) @@ -4444,6 +4553,13 @@ public abstract class BaseTest { .constraints(TABLE_6_CONSTRAINTS_CREATE) .build(); + public final static ColumnBriefDto TABLE_7_COLUMNS_BRIEF_0_DTO = ColumnBriefDto.builder() + .id(26L) + .name("name_id") + .internalName("name_id") + .columnType(ColumnTypeDto.BIGINT) + .build(); + public final static List<TableColumn> TABLE_7_COLUMNS = List.of(TableColumn.builder() .id(26L) .ordinalPosition(0) @@ -6745,6 +6861,7 @@ public abstract class BaseTest { .owner(USER_1) .contactPerson(USER_1_ID) .contact(USER_1) + .subsets(new LinkedList<>()) .tables(new LinkedList<>()) .views(new LinkedList<>()) .accesses(new LinkedList<>()) @@ -7249,7 +7366,7 @@ public abstract class BaseTest { .primaryKey(new LinkedList<>(List.of(PrimaryKey.builder() .table(TABLE_1) .column(TABLE_1_COLUMNS.get(0)) - .pkid(1L) + .id(1L) .build()))) .build(); @@ -7259,8 +7376,8 @@ public abstract class BaseTest { .uniques(new LinkedList<>()) .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder() .table(TABLE_1_BRIEF_DTO) - .column(TABLE_1_COLUMNS_DTO.get(0)) - .pkid(1L) + .column(TABLE_1_COLUMNS_BRIEF_0_DTO) + .id(1L) .build()))) .build(); @@ -7280,7 +7397,7 @@ public abstract class BaseTest { .onUpdate(ReferenceType.NO_ACTION) .build()))) .uniques(new LinkedList<>(List.of(Unique.builder() - .uid(1L) + .id(1L) .table(TABLE_2) .name("uk_1") .columns(new LinkedList<>(List.of(TABLE_2_COLUMNS.get(1)))) @@ -7288,7 +7405,7 @@ public abstract class BaseTest { .primaryKey(new LinkedList<>(List.of(PrimaryKey.builder() .table(TABLE_2) .column(TABLE_2_COLUMNS.get(0)) - .pkid(2L) + .id(2L) .build()))) .build(); @@ -7308,15 +7425,15 @@ public abstract class BaseTest { .onUpdate(ReferenceTypeDto.NO_ACTION) .build()))) .uniques(new LinkedList<>(List.of(UniqueDto.builder() - .uid(1L) + .id(1L) .table(TABLE_2_BRIEF_DTO) .name("uk_1") .columns(new LinkedList<>(List.of(TABLE_2_COLUMNS_DTO.get(1)))) .build()))) .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder() .table(TABLE_2_BRIEF_DTO) - .column(TABLE_2_COLUMNS_DTO.get(0)) - .pkid(2L) + .column(TABLE_2_COLUMNS_BRIEF_0_DTO) + .id(2L) .build()))) .build(); @@ -7327,7 +7444,7 @@ public abstract class BaseTest { .primaryKey(new LinkedList<>(List.of(PrimaryKey.builder() .table(TABLE_3) .column(TABLE_3_COLUMNS.get(0)) - .pkid(3L) + .id(3L) .build()))) .build(); @@ -7337,8 +7454,8 @@ public abstract class BaseTest { .uniques(new LinkedList<>()) .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder() .table(TABLE_3_BRIEF_DTO) - .column(TABLE_3_COLUMNS_DTO.get(0)) - .pkid(3L) + .column(TABLE_3_COLUMNS_BRIEF_0_DTO) + .id(3L) .build()))) .build(); @@ -7349,7 +7466,7 @@ public abstract class BaseTest { .primaryKey(new LinkedList<>(List.of(PrimaryKey.builder() .table(TABLE_4) .column(TABLE_4_COLUMNS.get(0)) - .pkid(4L) + .id(4L) .build()))) .build(); @@ -7359,8 +7476,8 @@ public abstract class BaseTest { .uniques(new LinkedList<>()) .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder() .table(TABLE_4_BRIEF_DTO) - .column(TABLE_4_COLUMNS_DTO.get(0)) - .pkid(4L) + .column(TABLE_4_COLUMNS_BRIEF_0_DTO) + .id(4L) .build()))) .build(); @@ -7371,7 +7488,7 @@ public abstract class BaseTest { .primaryKey(new LinkedList<>(List.of(PrimaryKey.builder() .table(TABLE_5) .column(TABLE_5_COLUMNS.get(0)) - .pkid(5L) + .id(5L) .build()))) .build(); @@ -7381,8 +7498,8 @@ public abstract class BaseTest { .uniques(new LinkedList<>()) .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder() .table(TABLE_5_BRIEF_DTO) - .column(TABLE_5_COLUMNS_DTO.get(0)) - .pkid(5L) + .column(TABLE_5_COLUMNS_BRIEF_0_DTO) + .id(5L) .build()))) .build(); @@ -7393,7 +7510,7 @@ public abstract class BaseTest { .primaryKey(new LinkedList<>(List.of(PrimaryKey.builder() .table(TABLE_6) .column(TABLE_6_COLUMNS.get(0)) - .pkid(6L) + .id(6L) .build()))) .build(); @@ -7403,8 +7520,8 @@ public abstract class BaseTest { .uniques(new LinkedList<>()) .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder() .table(TABLE_6_BRIEF_DTO) - .column(TABLE_6_COLUMNS_DTO.get(0)) - .pkid(6L) + .column(TABLE_6_COLUMNS_BRIEF_0_DTO) + .id(6L) .build()))) .build(); @@ -7440,7 +7557,7 @@ public abstract class BaseTest { .primaryKey(new LinkedList<>(List.of(PrimaryKey.builder() .table(TABLE_7) .column(TABLE_7_COLUMNS.get(0)) - .pkid(7L) + .id(7L) .build()))) .build(); @@ -7475,8 +7592,8 @@ public abstract class BaseTest { .uniques(new LinkedList<>()) .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder() .table(TABLE_7_BRIEF_DTO) - .column(TABLE_7_COLUMNS_DTO.get(0)) - .pkid(7L) + .column(TABLE_7_COLUMNS_BRIEF_0_DTO) + .id(7L) .build()))) .build(); @@ -7487,7 +7604,7 @@ public abstract class BaseTest { .primaryKey(new LinkedList<>(List.of(PrimaryKey.builder() .table(TABLE_8) .column(TABLE_8_COLUMNS.get(0)) - .pkid(8L) + .id(8L) .build()))) .build(); @@ -7497,8 +7614,8 @@ public abstract class BaseTest { .uniques(new LinkedList<>()) .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder() .table(TABLE_8_BRIEF_DTO) - .column(TABLE_8_COLUMNS_DTO.get(0)) - .pkid(8L) + .column(TABLE_8_COLUMNS_BRIEF_0_DTO) + .id(8L) .build()))) .build(); diff --git a/dbrepo-search-service/Pipfile.lock b/dbrepo-search-service/Pipfile.lock index 1c60362635..c85c4145f4 100644 --- a/dbrepo-search-service/Pipfile.lock +++ b/dbrepo-search-service/Pipfile.lock @@ -132,11 +132,11 @@ }, "certifi": { "hashes": [ - "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", - "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" + "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516", + "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56" ], "markers": "python_version >= '3.6'", - "version": "==2024.2.2" + "version": "==2024.6.2" }, "cffi": { "hashes": [ @@ -340,10 +340,9 @@ }, "dbrepo": { "hashes": [ - "sha256:ceab260cf76c050e118ce0f0589fec66059396751e03f2ec41fa489cfacc4e7b" + "sha256:110db9e4e70f5656a6351409d4b022656abf7de0bd72d5e061a25685f708d9a4" ], - "path": "./lib/dbrepo-1.4.4.tar.gz", - "version": "==1.4.4" + "path": "./lib/dbrepo-1.4.4.tar.gz" }, "docker": { "hashes": [ @@ -353,6 +352,12 @@ "markers": "python_version >= '3.8'", "version": "==7.1.0" }, + "events": { + "hashes": [ + "sha256:a7286af378ba3e46640ac9825156c93bdba7502174dd696090fdfcd4d80a1abd" + ], + "version": "==0.5" + }, "flasgger": { "hashes": [ "sha256:ca098e10bfbb12f047acc6299cc70a33851943a746e550d86e65e60d4df245fb" @@ -366,6 +371,7 @@ "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==2.3.3" }, "flask-cors": { @@ -390,6 +396,7 @@ "sha256:9215d05a9413d3855764bcd67035e75819d23af2fafb6b55197eb5a3313fdfb2" ], "index": "pypi", + "markers": "python_version >= '3.7' and python_version < '4'", "version": "==4.6.0" }, "flask-sqlalchemy": { @@ -398,6 +405,7 @@ "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==3.1.1" }, "frozenlist": { @@ -553,6 +561,7 @@ "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==22.0.0" }, "idna": { @@ -615,6 +624,7 @@ "sha256:61c9170f92e736b530655e75374681d4fcca9cfa8763ab42be57353b2b203494" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==1.3.1" }, "markupsafe": { @@ -831,11 +841,12 @@ }, "opensearch-py": { "hashes": [ - "sha256:0dde4ac7158a717d92a8cd81964cb99705a4b80bcf9258ba195b9a9f23f5226d", - "sha256:cf093a40e272b60663f20417fc1264ac724dcf1e03c1a4542a6b44835b1e6c49" + "sha256:0b7c27e8ed84c03c99558406927b6161f186a72502ca6d0325413d8e5523ba96", + "sha256:b6e78b685dd4e9c016d7a4299cf1de69e299c88322e3f81c716e6e23fe5683c1" ], "index": "pypi", - "version": "==2.5.0" + "markers": "python_version >= '3.8' and python_version < '4'", + "version": "==2.6.0" }, "packaging": { "hashes": [ @@ -922,96 +933,96 @@ }, "pydantic": { "hashes": [ - "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5", - "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc" + "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e", + "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4" ], "markers": "python_version >= '3.8'", - "version": "==2.7.1" + "version": "==2.7.3" }, "pydantic-core": { "hashes": [ - "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b", - "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a", - "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90", - "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d", - "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e", - "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d", - "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027", - "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804", - "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347", - "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400", - "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3", - "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399", - "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349", - "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd", - "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c", - "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e", - "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413", - "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3", - "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e", - "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3", - "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91", - "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce", - "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c", - "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb", - "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664", - "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6", - "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd", - "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3", - "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af", - "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043", - "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350", - "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7", - "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0", - "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563", - "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761", - "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72", - "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3", - "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb", - "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788", - "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b", - "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c", - "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038", - "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250", - "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec", - "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c", - "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74", - "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81", - "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439", - "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75", - "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0", - "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8", - "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150", - "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438", - "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae", - "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857", - "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038", - "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374", - "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f", - "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241", - "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592", - "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4", - "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d", - "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b", - "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b", - "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182", - "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e", - "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641", - "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70", - "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9", - "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a", - "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543", - "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b", - "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f", - "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38", - "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845", - "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2", - "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0", - "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4", - "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242" + "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3", + "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8", + "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8", + "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30", + "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a", + "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8", + "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d", + "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc", + "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2", + "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab", + "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077", + "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e", + "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9", + "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9", + "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef", + "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1", + "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507", + "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528", + "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558", + "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b", + "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154", + "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724", + "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695", + "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9", + "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851", + "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805", + "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a", + "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5", + "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94", + "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c", + "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d", + "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef", + "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26", + "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2", + "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c", + "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0", + "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2", + "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4", + "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d", + "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2", + "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce", + "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34", + "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f", + "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d", + "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b", + "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07", + "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312", + "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057", + "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d", + "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af", + "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb", + "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd", + "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78", + "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b", + "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223", + "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a", + "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4", + "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5", + "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23", + "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a", + "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4", + "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8", + "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d", + "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443", + "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e", + "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f", + "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e", + "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d", + "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc", + "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443", + "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be", + "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2", + "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee", + "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f", + "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae", + "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864", + "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4", + "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951", + "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc" ], "markers": "python_version >= '3.8'", - "version": "==2.18.2" + "version": "==2.18.4" }, "pyjwt": { "hashes": [ @@ -1035,6 +1046,7 @@ "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==8.2.1" }, "python-dateutil": { @@ -1051,6 +1063,7 @@ "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==1.0.1" }, "pytz": { @@ -1123,6 +1136,7 @@ "sha256:9995eb8569428059b8c1affd26b25eac510d64f5043d9ce8c84e0d0036e995ae" ], "index": "pypi", + "markers": "python_full_version >= '3.8.1' and python_full_version < '4.0.0'", "version": "==7.0.0" }, "referencing": { @@ -1135,11 +1149,11 @@ }, "requests": { "hashes": [ - "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289", - "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c" + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], "markers": "python_version >= '3.8'", - "version": "==2.32.2" + "version": "==2.32.3" }, "rpds-py": { "hashes": [ @@ -1315,6 +1329,7 @@ "sha256:bc599c8c3b3319e53ce6c5c3c471120bd325d0071fb6f38a10e924e3d07b9990" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==0.41.2" }, "testcontainers-core": { @@ -1329,6 +1344,7 @@ "sha256:0bdf270b5b7f53915832f7c31dd2bd3ffdc20b534ea6b32231cc7003049bd0e1" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==0.0.1rc1" }, "tinydb": { @@ -1349,11 +1365,11 @@ }, "typing-extensions": { "hashes": [ - "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8", - "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594" + "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a", + "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1" ], "markers": "python_version >= '3.8'", - "version": "==4.12.0" + "version": "==4.12.1" }, "tzdata": { "hashes": [ @@ -1365,11 +1381,11 @@ }, "urllib3": { "hashes": [ - "sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07", - "sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0" + "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", + "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.18" + "markers": "python_version >= '3.10'", + "version": "==2.2.1" }, "werkzeug": { "hashes": [ @@ -1555,61 +1571,62 @@ "develop": { "coverage": { "hashes": [ - "sha256:06d96b9b19bbe7f049c2be3c4f9e06737ec6d8ef8933c7c3a4c557ef07936e46", - "sha256:13017a63b0e499c59b5ba94a8542fb62864ba3016127d1e4ef30d354fc2b00e9", - "sha256:1acc2e2ef098a1d4bf535758085f508097316d738101a97c3f996bccba963ea5", - "sha256:1aef719b6559b521ae913ddeb38f5048c6d1a3d366865e8b320270b7bc4693c2", - "sha256:1e4225990a87df898e40ca31c9e830c15c2c53b1d33df592bc8ef314d71f0281", - "sha256:1f11f98753800eb1ec872562a398081f6695f91cd01ce39819e36621003ec52a", - "sha256:1f29bf497d51a5077994b265e976d78b09d9d0dff6ca5763dbb4804534a5d380", - "sha256:1f96aa94739593ae0707eda9813ce363a0a0374a810ae0eced383340fc4a1f73", - "sha256:20e611fc36e1a0fc7bbf957ef9c635c8807d71fbe5643e51b2769b3cc0fb0b51", - "sha256:23f2f16958b16152b43a39a5ecf4705757ddd284b3b17a77da3a62aef9c057ef", - "sha256:24bb4c7859a3f757a116521d4d3a8a82befad56ea1bdacd17d6aafd113b0071e", - "sha256:26716a1118c6ce2188283b4b60a898c3be29b480acbd0a91446ced4fe4e780d8", - "sha256:29da75ce20cb0a26d60e22658dd3230713c6c05a3465dd8ad040ffc991aea318", - "sha256:2b144d142ec9987276aeff1326edbc0df8ba4afbd7232f0ca10ad57a115e95b6", - "sha256:2c79f058e7bec26b5295d53b8c39ecb623448c74ccc8378631f5cb5c16a7e02c", - "sha256:3bb5b92a0ab3d22dfdbfe845e2fef92717b067bdf41a5b68c7e3e857c0cff1a4", - "sha256:3d3f7744b8a8079d69af69d512e5abed4fb473057625588ce126088e50d05493", - "sha256:3d9c62cff2ffb4c2a95328488fd7aa96a7a4b34873150650fe76b19c08c9c792", - "sha256:3e12536446ad4527ac8ed91d8a607813085683bcce27af69e3b31cd72b3c5960", - "sha256:40dbb8e7727560fe8ab65efcddfec1ae25f30ef02e2f2e5d78cfb52a66781ec5", - "sha256:431a3917e32223fcdb90b79fe60185864a9109631ebc05f6c5aa03781a00b513", - "sha256:448ec61ea9ea7916d5579939362509145caaecf03161f6f13e366aebb692a631", - "sha256:482df956b055d3009d10fce81af6ffab28215d7ed6ad4a15e5c8e67cb7c5251c", - "sha256:4a00bd5ba8f1a4114720bef283cf31583d6cb1c510ce890a6da6c4268f0070b7", - "sha256:51b6cee539168a912b4b3b040e4042b9e2c9a7ad9c8546c09e4eaeff3eacba6b", - "sha256:554c7327bf0fd688050348e22db7c8e163fb7219f3ecdd4732d7ed606b417263", - "sha256:5662bf0f6fb6757f5c2d6279c541a5af55a39772c2362ed0920b27e3ce0e21f7", - "sha256:5997d418c219dcd4dcba64e50671cca849aaf0dac3d7a2eeeb7d651a5bd735b8", - "sha256:59a75e6aa5c25b50b5a1499f9718f2edff54257f545718c4fb100f48d570ead4", - "sha256:60b66b0363c5a2a79fba3d1cd7430c25bbd92c923d031cae906bdcb6e054d9a2", - "sha256:6e34680049eecb30b6498784c9637c1c74277dcb1db75649a152f8004fbd6646", - "sha256:74eeaa13e8200ad72fca9c5f37395fb310915cec6f1682b21375e84fd9770e84", - "sha256:7c5c5b7ae2763533152880d5b5b451acbc1089ade2336b710a24b2b0f5239d20", - "sha256:829fb55ad437d757c70d5b1c51cfda9377f31506a0a3f3ac282bc6a387d6a5f1", - "sha256:878243e1206828908a6b4a9ca7b1aa8bee9eb129bf7186fc381d2646f4524ce9", - "sha256:8809c0ea0e8454f756e3bd5c36d04dddf222989216788a25bfd6724bfcee342c", - "sha256:8941e35a0e991a7a20a1fa3e3182f82abe357211f2c335a9e6007067c3392fcf", - "sha256:894b1acded706f1407a662d08e026bfd0ff1e59e9bd32062fea9d862564cfb65", - "sha256:900532713115ac58bc3491b9d2b52704a05ed408ba0918d57fd72c94bc47fba1", - "sha256:976cd92d9420e6e2aa6ce6a9d61f2b490e07cb468968adf371546b33b829284b", - "sha256:97de509043d3f0f2b2cd171bdccf408f175c7f7a99d36d566b1ae4dd84107985", - "sha256:9a42970ce74c88bdf144df11c52c5cf4ad610d860de87c0883385a1c9d9fa4ab", - "sha256:9e41c94035e5cdb362beed681b58a707e8dc29ea446ea1713d92afeded9d1ddd", - "sha256:9f805481d5eff2a96bac4da1570ef662bf970f9a16580dc2c169c8c3183fa02b", - "sha256:a35c97af60a5492e9e89f8b7153fe24eadfd61cb3a2fb600df1a25b5dab34b7e", - "sha256:a7c6574225f34ce45466f04751d957b5c5e6b69fca9351db017c9249786172ce", - "sha256:c7ebf2a37e4f5fea3c1a11e1f47cea7d75d0f2d8ef69635ddbd5c927083211fc", - "sha256:d0305e02e40c7cfea5d08d6368576537a74c0eea62b77633179748d3519d6705", - "sha256:e1046aab24c48c694f0793f669ac49ea68acde6a0798ac5388abe0a5615b5ec8", - "sha256:e5d22eba19273b2069e4efeff88c897a26bdc64633cbe0357a198f92dca94268", - "sha256:ec27e93bbf5976f0465e8936f02eb5add99bbe4e4e7b233607e4d7622912d68d", - "sha256:fe76d6dee5e4febefa83998b17926df3a04e5089e3d2b1688c74a9157798d7a2" + "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523", + "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f", + "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d", + "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb", + "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0", + "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c", + "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98", + "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83", + "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8", + "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7", + "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac", + "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84", + "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb", + "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3", + "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884", + "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614", + "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd", + "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807", + "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd", + "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8", + "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc", + "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db", + "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0", + "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08", + "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232", + "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d", + "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a", + "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1", + "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286", + "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303", + "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341", + "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84", + "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45", + "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc", + "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec", + "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd", + "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155", + "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52", + "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d", + "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485", + "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31", + "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d", + "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d", + "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d", + "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85", + "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce", + "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb", + "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974", + "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24", + "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56", + "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9", + "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35" ], "index": "pypi", - "version": "==7.5.2" + "markers": "python_version >= '3.8'", + "version": "==7.5.3" }, "iniconfig": { "hashes": [ @@ -1641,6 +1658,7 @@ "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==8.2.1" } } diff --git a/dbrepo-search-service/app.py b/dbrepo-search-service/app.py index be4481cf08..844c01709f 100644 --- a/dbrepo-search-service/app.py +++ b/dbrepo-search-service/app.py @@ -361,8 +361,9 @@ def post_general_search(type): description='Time needed to update a database in the search database') @auth.login_required(role=['admin']) @swag_from("os-yml/update_database.yml") -def update_database(database_id: int): +def update_database(database_id: int) -> Database | ApiError: logging.debug(f"updating database with id: {database_id}") + logging.debug(f"====> {request.json}") try: payload: Database = Database.model_validate(request.json) except ValidationError as e: diff --git a/dbrepo-search-service/init/database.json b/dbrepo-search-service/init/database.json index 8e5d443965..4e5200c06a 100644 --- a/dbrepo-search-service/init/database.json +++ b/dbrepo-search-service/init/database.json @@ -763,7 +763,37 @@ "type": "keyword" }, "primary_key": { - "type": "keyword" + "type": "object", + "properties": { + "id": { + "type": "keyword" + }, + "table": { + "type": "object", + "properties": { + "id": { + "type": "keyword" + }, + "database_id": { + "type": "keyword" + } + } + }, + "column": { + "type": "object", + "properties": { + "id": { + "type": "keyword" + }, + "table_id": { + "type": "keyword" + }, + "database_id": { + "type": "keyword" + } + } + } + } } } }, diff --git a/dbrepo-search-service/lib/dbrepo-1.4.4-py3-none-any.whl b/dbrepo-search-service/lib/dbrepo-1.4.4-py3-none-any.whl index 4acaa2f242aa1a17081c6b649879afacfa191dad..f58e17a58e747e35bbd37f43efe6b460ba31530f 100644 GIT binary patch delta 14068 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 delta 13983 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 Zq!5&&Ca=p9V>$p4zn>+>W}gny0RZN02Au!^ diff --git a/dbrepo-search-service/lib/dbrepo-1.4.4.tar.gz b/dbrepo-search-service/lib/dbrepo-1.4.4.tar.gz index e0bdb5adee8536ec4455e215913a6a769237b1e9..5463f6b170c24fff05d29a434562104553292fea 100644 GIT binary patch literal 37662 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 b!VYJdM*{5#Cj0rv{~3Q@`|HRMz{&ssMI%h+ literal 37614 zcmb2|=HQ6&3QJ@9pORFRT9B`6sAr;QqF0hw#PFuJy8gD=rWgO#`X79JPwsSi>Q6!I z%)Rc_eM#EAW_4FL&3=3G+4H3;9?xbeg-+VE`0U&A{ht{eJ}jK>YVD`|_f~{b<G}=j z8In9{4X?sVU)P^|>!06xSM=a~xu=`Y&iAa}zCHfU(fc2Np8NLh+`IW}{OhZl&TI6% zVVL#*^7fxM{~nZbIp=VtEbi@|KOc{tJ9m2Tt9Rf2^;fTtoAz(ZuRjm%@0KaF{d?wD zx_;O8ugj0m{=fd)_mA(-t^S+)dHuZ4*46su-@kwRRvsYs=FPj@{l6w}{XgY%eS!X* z03SW`*MGx*t+ZqRx&OD3_W$@bPySCn@&9hbzxV%k?EjvXzkAo-Nq_Dy`(vLcXXY2( zdwj-!{qNt@PW-<+-CF-!;J^I+H5)${{Z~EpU%XmDXwBQ@bN=67@kc*$v)$K)cXtY2 z+jv<pPGi~B|E??xA6H+?E8U&+_v|;fGJ|bp+qZvL3)|JX=;6U@*Z%&ky}B%IRo#XA zMLSm(tZot2mljvoU0qj|B0F!coT>io^yRv*&)#e;y_yxWru*2>qc<;JRkT|duyS$Q z>rab*{^FW1&@gvZsY+GfbC-sg2wl&dYbpKL)_gct7Mrb*bG~cA@`)U53sb9OS3j<f zU6=ax&jG2{MM(!sIRCs`(cG^sBr@%-RicQ+TS2?+Z!Ww_n!G4`>+vw}5{VOh2l)R4 z{y+BX;-~5lhZ~FP(oF>7rW`xXZ`4ux=b=TxFY}2{?^i2rU{GE0!X^2%oU3`=gFmxx zO)mJ9<zilz;<)(lfwr!=`COh>5*!`--bV2<&2>zAlE}_kop@8WY0(A-BZo-NW9>{r zj_xgYOax50YPKicYz(lEaoW|i_uz76{x<>@<`O^VwWx4MefKLlT(INds;|fRIr<-J zNc=irXc+pnVYOk@<bZ1{v=@e!neA9ruiwPQ6VI@?#lu3betpEm{L&nW5*_u0AG8-a zB=F{(f5$CjlVdJZnx~Q^`ud++!1u&2D`RIT%$~Bde8Ox7ubYfM85M`5(>o?q`SGu? z6mevCoc?|L{`(7_)hO@_$1zH|D(q#5mtdAy=Fq~W)R4o?-*KTZK|}Zgugzn{|5xSL zMy9UlzTdQ!xA@9K`z0smUY1o{bHM1QGn2r4hG=yc7l!wZ!fi)B97(s~%!n{rbFJx` zl>uvF74M}vW;`zbEQ}$`x7#lIp=)ONVK-mN*=3PS*=~Hade`B>IQhb-KPQUjHa4$4 zY|&Pyzsvm_*Gfl;DS83%XKk5O4i+?a&H8V<+@9-&El;;L&&R@R|HL;qiIv`#{%kDK zH_`j^V(}l$6*qTov^@~zEAW6z|H!0mNg7p;PR+MHy={gK%eqN#7PL%!rRFnxVYBcl z^(QIjzYlO;V6T|-VuR-11<T8I_Byv5E-qKt)tBq~c;U^r-|Gry3vzw`Fu_30vSp5# zKjZP>yBCBTJk@ubHng^<-oM$Gy*<|SKkv>E$ILFC8yi)%Z${~9yktvhI25(VaOr{# z&M8|LbChxidL(K)PFcwOXtJ$@)9C;~7wZj{;YTKYeHh?ydVYe@&VPU1d|H(SEy^8^ zS|=(x9@=Wco*@0xWWwI*6K1e+ecUGd=s~54=<XYAMfL^!r?qmr+PI$|`rCVXr(p{t zPZ*08Lt2``I~ggjCrJvubJV9XaQ$xl#j5bcVD@HDCTCrBrw1a7_VM=q)br(9@$8E% zf7Hpo$}l-?%d6f#|GTED&0iu{v{}ZQNq?)G0^7F5&zDQwVl3pk)Vj-1pt0b%0so$8 zr5NcQtUr%zW>0eF-NoFb@#3azkdUaed)A_~2~Mv#_KWxbQtT>P`MB8K#+Lod6w_Om zS*8g4y$tGQXY%gh{8$hkrNlMq{Fa4LAL@iYi6#fRXnd{BxD}dYEwk0J%;Vw0H++xu z4$ZkRIk-_u$#R~d_7#oEyJoM_^>JohYGm5~=bo6u8Mz5bn?4%N-?sf)$^2_7I@i8O z={T!CJjB9NBXCSqrghs}A^E%P5$Ba3CqMCg6>Y)ov#5JUfX8x;9(C@E_G^4XU8Blo z<{qdw^ivR&71!RK5X5BSoSGS|A=5rlqGcHe`&?_G6;4)5lv6q7>aNt}tj&l%tx|PV zK$%m|U`1~Uv-EajF3;1!-OQ7wU+Ssx7t#;&eKc9gY18^H`GwjKFTCve6UZ^SJb`_? z#;>LuRzYghIp*$j-y|O)c)WvS_UTTIHP`$a?`=yxclA|N8Q1bR*>`hInbT+4s4)iG z@dfopnK$@EGuStt)bN<Zd@0TPqYBT_eB}r7;<MlWaL5n4p7BBIazE?EysN)B&uZm- z3n-6|3=7LXaw_yw*1M!*Gt!oYh%HdnZFUgr5;!n(jm0MJ0~RfDO|9l4f&p$D<vZ-Q zSHEAd_TkTaYP~faw#VkrPWZ}F8)NY^`F8KI*?S!h=(F?ew%*^|WV86}(_j43A**#) zFc#-@)XbS!ve81m?^b~L3XUGDlH7!nzbpl5q0*OxGb3iaHoJW0`lcB@Pkp*AU(HG3 z=D)z5+;A>eWLJH|MNMZ@!=8esgOikl)PxiyJ~d?=mMV(xy~tcF+i>?rdD9x3R)MvP z|HkpZwFzK4#QO2{|0b7@%^mD+rx`OfyjM>+((_7kQp0hNty%o-dV1!y{A_bq^lb88 zbgeL#{l(eT-gAtioE@IAy5_bs_9kYq^PN2x$8gDgZAQ!DlKGY&Pd{YSX|DD6=;e6C zuOpOuHF#?Y>s7_LhhDkdFE&q{?&4=w*zdfsB=&07w}L-kqcm7NTW!{|s}}w0X}Y`p zqbaB5&I0}8T;8{@HOjZmb}@M4c3ghp-y{1IpX8q2`u^}l;l%LRi|MKiBEct)n>l)E z`3V;+5tjHEptm*IVs1sju~pGoHf$*?nIz1PPMWjAM{TRbRAX0NlSh_nq4PGaim-kl za`a;Go}~6u39k=1AFY|Si-GrgXRH`&b?9rS8S@runc8kRzA?qyTX8~j-=<ulPZ?cP z-JP4#lugW-6y0lD#CZRF-RkD2H#@S`e?rRC3QZT5&d6*9mj0si<%zyWe#M?#*E}gM zJh#B#{Xk~z5%G(Uzq0YHW{!1Pm^y1;RcXh3ANSl#{4b{-iTCJTWp>+b_M07@zL_!Z z4}Z;_YsI)pKu2DN|7S~h|H6wlT^46AaR^U;SF6;PU~zJqwcF{&j)bPrDR*;p9%hy@ zNG!EZH(4XyBe|%_Sm#=w=*hdHBHgi#Wx+=c)H+-jSL`~uZzH!7`}{2)zEOEPwxT>v z>t0WG<c?z7-TCB+q`1q*M?qpWyB2#cDm4$tc=>TUx4q1(t(P~SewLWbt9E~R$BW*} ze%l(=wqNYC+|466!8q7x%F>TU4l9=~=S|Ki&+L^no_j)7n@d&t?;bV@%_|;81@0@j zr&hkmYB;&PKrgoQCPSO{8-eM;%cjSLb*5`exLtVB=yW;E;@Flx%_$F76=?C*8@5(9 zojg-Hx5OdRPk);63Zvf}+0GpL-t0B))V57udGx2pNIwZ!IenI9io*e}3GCbqY<GR% zJc;P<ZU1K(VX!z+F(;xW?%oG2o~Pl{&K#?l{PBQy=G!ODrY?IbT)9;{`{qQeY&^z! ze@~cn+4k=H%rWn0$YeSMuJGnMYb>ng^H+fF&>P{k%P;J`_?BzEwUowY(}RXy8$SH< z=S`1VCfGPtnEAv6>zOB_S+mu)F*gOy`ai>9@;BWgPm%RnCvsHNZKWABk54xg;c)qO z*hMn7h%ag_r?&a=rvBJ!%V;;AV>h%vRa@Q&d6ap1@zF;Whk}lLIrnm<%ZCk_hi8i` zPq?|(D<d(tbg}+SpUfwVTcX|8hXiRi9%*=UIm!JU%ZDh_ZA^z(l|F40<ITKqL_K50 z;|HInD@aMJ+}dyG7d^4f_;f+CZ%ORYX?ELYls(z7^19jH^W8dUm&|m{km5P4aHHhy zs=My%>R-7>c)q_kp)T|H9*x79x0gSaT_xzGE7NGj;TrzIO~8*i*(1d{Cw4=#$ch!y zU1}pP3qJ0D%Ck^(<_{ICwhr}!t2u=__Q`(O7gY6D@jFvolST-eM@Q1d4U2c%d7kU= z%}aLY;M6z2a-$~jw!}AvziGD?Ij*11@$SNM{dy+9N$oo~Bt^9rs(!ONnVO|gCv92u zokO&WBe_XyZkve6e)|J&Gmo&X;3;!wx6){L(zm)0Jp1GMRX3L$^UZ3O5j`-aDs*e# zoC(wXc4Xe(e6RU0XNlx%&jo?G{gsT~+W9d_N0S=dCW`5A6kj96KV#1C#GWk4Lf7Sv zRh?PW+`gwK6_{UG&#F|v_THvhYfWF5?!B6$`DkB$+&<ggySCiBuCMoM`Si1Hr>%Ay zY=3(GUc}eS@oUS^URE|Jm$}FIujSrFgLj?IMN6}Hcs`bNsSAoexzbRls54}X0@o%b zv5v}{du;MT8eT1*efF}s_}R<zrPO;bpYAHXw8Nr?JK|crdH?L``-QmoX2#7ki;}Sm zm~L?HwYWh-`0=mYum8My^k>c0gxu4+TlL=QF#L1QxUw*o{npHfp^1}X6P_^DOIT#g z3vRDI5F63*Lx0+iw*6D~_9k+e36_O-W~-~5l=~&?u0J_)#p&9xPse6Suf2Ix=kq=O z&>w3(x~BHY?tZ&tOXWS6v)PS5AG@}&A8v5hv$SS(kI0l>C$@DmE04%bJ!bzevFvNs z_uW|W;_f@ILsM7V+8h7ed!~))*O%DkzHG9J*WK$|?rjh7b(3fmxb&{#-=Bn+GXvyb zObK*;W_4x57mf=TZZYqk|K(wFuBeazJ9~?BWA*D}?~Hvk400;98F?kXy?oR*hh>$D z$*(&M`6c1(d(!)&VqRz6G4<0g>71pko9y=I$j2uMQbkw4NK9G&u2Xx>hTH4T^W6Ua z_ubydv-Ta14mkC%AY<`#f!roRrF)xY%SASrZMu8!M0c-rP?P0Eg&o`tXR<FfON%KQ z@O@I0dXecqr)A;Xmu272E%d!KkLP}aq(bL{CTW$5d5rd9Ypd8xnSvZW<8pS&X=fz| z?b+{m+14`l^6b{&?YF0h-rD)G_FL1W<;ELt`q_nVxLwusgtNoy(1mYSP1c4f<}0Su zB|ow@{9v#}rL4m+GPZU1)9tY@Ta|XFav$Z3+PCK_)8B(Xm9B^T&A9Qdk@Z;gf4Oj{ zJyWi*C|5}}JpLEx(*0a&x8kj5({3w0IWo0BaKf>qDIZk5)$h4>s#d+xG`ivNtEKSl zlP?i=k6WJq^xN_>^Hzx~|1|9wW}U}*eXsBEv8i-UDO|f!!vA@oz>(dlmx_6eB*UKW zh)7k>QhjZn*S~b5V0lK5NJr+X-k;UY(!84kA0OhmV`1yGca}Jt`}^0=p9d`Y`JGwD z>62K4i|pwSti~n$_XV}SSLt&ee*J7sz{Erm&-^B%)wMHB8FdaTKfSu*i1yEU%6IZI z8k-tUo^#sYyL$?gMe*Ci$G28YY?{Zp^6jy%nI5Z7h0fnNX<gJ)<@?dsKQbMY2|e&X z`S7BvR=UB?2Ko)c2aa5?IB~Uc;kMqrk#n>kN<8_gzE{wzyULB>2}?+c^PL5cE?x3p zwZvj->OMw2=kBS~V^h-jrW%Dsug(>0Uvq(P<sp?LUD*vXmg@r9)-PP=@vi37192B& ziSrkKf1Fg5bw6}>dXjp(Y)M|h@mVt!@A>>OXglxzn)xAb#j>(U`=G*;DzV9Dr=-m9 zk`&0Ay0M{e=baziO#!b3MOfDCTk<<*=Epwu=?hh)Ox-huk4Jp{o%?CpP0s0alTxgM z-q>kB32Af4XF3&<D#u+r>8)b&Ij?2YjU4q`<=PA+Sk)pQc+a|j>Vw**tt}o47R;D) zM(Tm&i&@v@84ovlGGra}cK_y?@0qi{{bpTg_`|QO|I82l_gw3L@z<YLpX$%u|J&j> zWwZL(|2J<}Z{0lo)c=}|`!}EZUpeV~XUCg$vNva63bSTN&Rt%@)poz}=$^n!`5m5K zYcDd`E@!`X@iliv?%e*x((=qN4!q+#wsiYHhnz)As~nbvY2@lI7Y|!}aj){8Rd-k3 zx+vC`cVoxN%bjis3vvpRqi-DRUK=#`kqiGC;SIqDmsRy|na*!+D0Rbj!?iQzqOU)l zs^Y(Jea(NV3!k-mum7Ks`){iX?_}oevRjuQz4YhzZ=Pi?>owJwQFp4`RCa^8dQ-!f z-T0!p_i(s})YX!j>3*w?9!raPub<Tsq4+Ya>hiV?TeQDws{Hl(zHNfZ8jBx`8%ox& zJniy0eTiYZ-}zZbj7~4hK048^dugukq2&im)}<JnyYw=~YEgDr)b%Cnbmu->%Ie1W zGS02z_@&JFWmo3T{b$-~<GQ4IrN3il;>J5sR<=I1V!pz&UDh7DbnMFV9bXJ}g}Qu{ zgXbq*J}G1v&boAJre$DC)yz!oGZWLyI<u$wSsQ=UQ2XoStfRu~E4=?iiHUO8+6`N_ zXdb)YeEIT~<vUEa9(f=#d7aqlD@vEI`G|%D{F?PkWZJQ5yj^j6bA4y8YhAMStcaxd z@|`a;Lqc}VHoW=VxBB$!)QOn|AGheKJoIuG7oS)$b!(d39J%Gnr@Or-2hV3;e)=hI zPU6er_`@s2W`-@dYh8L>b<q=UkIljJpI&}@W%+_F*2c>hbbQ%e!Mi(0YTmrRN0&UF zIq|RSlBbd%rum%@o}YB-OV66dpO?OVatRE4S<Pv=vqWmXnMAk8^X|3hS7z1<zv)T6 z{6}ueJ9EzIk1qWe^JSjX5}26T*t(2eY^qvB#;yl%wjA_y(NpXFAb5$B+fQ4^V(*3r zCWjAQ=H>Ra*0KA$;eyGLN0$Y;eZ6(eY&U)|Iofntl-n<Q&fI+#jvGX$om&xFW0=|7 z_IhW>7dPQMlQR33ZQnVe<oJ@^`DwS3?>UIgVw3!Rn#-%*BrK(MiR&}{o#OueA@d(y zQaQU&M=925W6B)kT%&$_F5ksh7H!>7;&u3#w$56kY1h4%hd&G4GNZ)X_Ubg>-Dh@a z$<3=<IelX5lEkdfTXvNA9DWsHr5jw!<$eB&a#YByWntz=mt;TtSd{p3c3W$tgVgeG zCSp_0t$Z5hm^t@Va7@+mjnlN%&KhqE<?{UQQ!!(j|8J9;yQi1et0`Ze=FNO{;v{}g z_Or7Sk1lObGy6T=Q`nc~xyS^)tThw4Jf(f}HtWr|%X(uZHpwk3W6{#fo{wd3FTL!= zYZw$zwesS{_T@95`SxUHF0XtlKda3u@oTh4)T+39A`|9i>F7=O4>nCbdg<k=w47s; zGFMj_#`*kO^=+~K<YTLzpJ95rrqXAtLsfR%SEfs`SC@BexxQt3M%b^{Et%Uqta9W! znl2?9-}|0=DTO;qP4!ah)#VGe+`Qp1cbZ+a6@Sg0L$~+rvT)mIC6LpmJ28B6L&cOY zZT2!b7A-4(XtM6@WQ<MZ+4kOL-Q9J3`8ms_ZyZTWaATW2r8cF%Am;7aylUZxZ4%8^ z_g<C0^?kX6W4qaQ4Hq4sZI>n$-gVs?RHc4wLHMqPr}9_HIQsIe*{WAE^^)Q#*E?Z6 z3nXsXuCrIUc$+Dz&*}Ap167H+QKjM+Jf`gZ(tbdhy<;0sf_e7g;&Y3dzLqYM`m#g* zMl)m4<&8-O8oI&jH|%_M&TrAFn*!p|5er=>mPEYZN#in*Jf@YrVL@cfN8_nkNe;yi zk~zfxObhZ$USqUSui{+$?4F~IU$09g7K&(ho^}3qHz%&I{ZWI-Z856_yA;wx^{zdW z7N}`p;GQM%O7P4VW+5G=o<sYum95q<yOiI4ZtdJkvv-Ty7fr6?du$|=&ULtE%i*Ax zyE#whEZscs%*7Ww_<kRM$$IuTf6VjbpAUuE#ca&j)C8KRCe3@I5n}ZE;vLZyH!XJU zX1P%t5YjnYuCJZX@b?7u<I}X7d}{(sW(LKHF8t)OTC?@#-)nB1xlQ~Q9G4jmzt-Jj z`E>XDhTl#RmdO(`R(?5O|4;b3c*2G0|M+>NCe{`+I9n~*AQ^N2^dD!9NUe#<A{HOT zBI75ig&c0J`JSHl==sFUJC9B|w<5%f#ar?(Z`cysDKd<#{j27F@SEkfL)oQlPSm|H zPU~YUUDiuZe#IK_WrCz5Q`dYIu3HW*datbYj&{T-iM8*umTK7WbiakZ*2(KTnptMq zFs++n6K))(dH9a(uMfMnt<#V@czRXN^(TA2a!H9ZN?C5*I`94Z(4x=$?FuXb53XLj zRkmSUWAVYZrw1jryByjrRL_3v`V)~Ar?y`|v2BV>keg8)|JxVKwuw!O7U|p_7Pzj| ze#P2Q2BVwxPwz#=Hm2M^-uN}r^po|JU*~T9(e7ahQeQLGFk^Y!|9}Q%wkaF7ttx$Z zoMEp)!WC)zJ^OxbOxzaFxM4}2ZQ)v_R)JIYHgEFVf41yca%9h<>eKJnz08a}dnJ2$ z^zD^CzRy!OZNK5&qm`0d_0!Mh!`7NQjq^V8tcJXM;(u?LryzPFHM{z6a%$SwiO(0D zSyAi381er{VY1jKn;3bcp13rt2OR4{pZV|dT|24abk@g~m2y}0rTN*ey*>C~?}~_P z<=57KxM^c~^G%sN7f*!LcI$QdM|YG-_8e*6_{nWub?)O=p(WY*37u+Y&st17pL8nf zO!(G&_?hS59|u1;J-@$b+vdL?9<F{H+9jd8sL=3mEA#$08I2;*y%&q+mDlf=T6|n? zasS4-1sN++xB7pYS(j<O(5Gw0<o5^Kb}Va5l5Tl3xhr&SnE4dd(A46dsg`^FGfVQk z>*k+cHS^Nv57#8)79SN<GBL7kx&E;*dfB#b{O1ii?pe$ezSof_I&b1e?y{V2-<XJ| zy^LB9P96N^|Jrm$)$HdxR=O~*Uh4b0Wz(V)7nptC@UgZ<Kjn|zFKm~SxXF}@@sV1U z{yUk%{a<CH9cJG+vMeVd+1Z?Tt(H_<|D|<Jd0SRStK^q+Okvb92oHUiA<}hhLRmqQ zjoD6Lr{$i1R6GT`Uc8K&R?NIOwxWHRh_%PV$c-HfW-oYuw1Q`@p5ny^6C^UE-nf-S ziQHcEqvguoB{qg>JLfkX>1geFboiuHUgE=oZtqr;a=z5M{TfqWNl43f=)J!Xv}WE@ z{?a8O($^*_-8y8(%)XfI=1r|V*6JT@i%#9S$@o@j^#hq7{wK~RPJa|rr7XVvswNBP zX4y5D?z?R$6Jd1I@7(tOe%qA3Cgo$ljTF{*L_4neyO}HZZ!lZhp<h?N=HA*~x`;Wd zWtyJU+S9LpRes)8SXEMY&1UbKwP&M#tUWq)T~u%P<zJr7;td}^f38~n_1~LE9pbeg zKZia3@QTT6u|}NsRsTA><;U~SM-^{ftvzRctV8SH>FdjS+ci@j9r8^1@;QE%`1J6v z$v1yZbNT%0&yO{ATNo_2B^d5Al~PLGu8~&n%YBo(!fZpkhDY?1WX1C=#Q`iq+d_Wr ze^LE9bS;1B`tIL(*II3}x)q*%IKdhGrnmUg_ib^1eiZ#~f8}dZVE#{AhWEeLdG%lG zb^kCH^v`0rYI7)qZ^mx>JylzGTMNDjul{%c-RU<!>-EE)|F=E)vwrunpY`kODz@(a z?_644`sx1m%76D=7uS_sTEVx=zw+PxbLYC>*4q8guTIbZeN}JX|L;ZrufF=TfBBC! zWp-iK@~h|eUp;p`>v{6l&l_+3jHt{Bx0YXR81=RWn{>rDYk6(+cdz~}zxvO=@w@%o zKRdp^x|${aBKr3KiQi5npZOpD`?v77`igVQ-_=+B6nggmU)KNUw<r8x{r7+R#{c{O zKjyc#){k5B?f?G5pYig4%%1pee^v13#qa;Oe^=ku_xSgI&#l|{?=#wN{9N??>c6c2 z|26H3=8M&GPkEz%Xj09S*ZEV@H21eB|NRkP!PWZb-LJoPx{B%VSReN#aQ>ZCvB}{~ zX3dcgx1{8^H#}E3^8Se-i><xE`#6s|*G?Mp>}c4Qz?gfqH|h8)F)QZ_<(3B~FT5ds zh)=+d_o4ii7ju)J|F+kfw&L6BkeFPj;>#Cu9{=%Kf8u9?R?(IX(XK0)&)j(4tX_ZA zqhRqa?w|9-_ig*tuqyKHf&HQfzxMX8T<xNCHNGZf>$xS>?Rp<JXEs;%zFzU?*;bhX zk7XhjHB-3x{z_RYFcjNp+VdO#%;IW`=3H{+SS?piRZfFJ_R6(?O6;FJF3~-j#@Td8 zBGUAAh-~)k1%1v3Id*;DdDri1zxAXl!N*y9G)^+7t$CfjN9)<IT{2wTr$0_U?^tPT zdF=J%?4{PmJ5%DWU)%o4;rUnN<q>h~%?p&7_aAxQ?QI#9dwb#K#Qe0G=k->ut&iV! znD3UB^un*UZ+&ue-o1HLE<X9i&x787Y!67Y{0hk1n{;vatfuNK6}4Y4fAN(*;D4i8 ziep!nMCso^k^4do$w8)W48I<>hgue|t`(emU3i{z#yyR>{OwDwh*-P`=y{m%Qljtj z?3nXk6xZc1_$<~h?HOd<+im&ka)H;OYY$qa&+*$l&zxk}wQ-r^BAL~0NBp}^b-iCU zDPmXRb?sTc=8q&j%NMtp+bDi)5wl{R_e0|S&jrc{3Y-J%99x55Tg}yI-#zut!eU-Y zoqH=9>S}EaI{hwuFA?6D_u%;^UG7_t!na*5tKFx^S<-V{yv<{3BFAxwRFOU@tIo?M zH(lyC?6{b*%s}bfQjgQm$}R^~m>Q|9YL~iN{C8c?;dpQV^<0{ZqN=v(?0KH`DI!<M zTaV}JjhK~z$9veX7GE#hbNGj&<-tq*_e|#%x8F(m(_7(m#Ang5M_ZrFukbnbu|ZO_ zG1=x}QclE~^iPSF!9A_tx6U%}dYJXfJL%e$p7)ziT>7^Au~gLDtM4)lcuu!njN0lv zr}*iOV>+K*TI$WriZTP17H?(vJN1A4)Blk_>o5HOf0*C?&yLNTKmD(H{BOR+i(j07 z>~~l1wl?^G^8f#&^78uUqQC0n-t39_SwHW>|KuH|N&EZDUY)qFwYh1ojaI%>Wt72= z_4l^6Cs-9UW{9i`_~i2Jz*7wqVfG(9dS8V?ze{g%n6z$*c2|-|n!=3p%YM%Nm;X#v zezV8&cSmpXR4iL#@n!cSL6!=KH&acnNzMOY7yNTW<McUd^XB{td}O}6^xhB0*@v>8 zTo%54G<D63RZ*D-?E75|@?7f{%(54p*n92tLaUbT#$gsUk7n50-O#<5Q`=%L=K86} z=c?c$;lCI9wpVne%jUbSJHDsSyvN_M|Fn;P^sVD+w)w`kxib?MR2-bHwP0EcgVw7j z6;E~?)vVj;YE#7J8ON%r_2T$XKK;rkY4SW*tE=a{p5<M2(Bw|*o`hZ3RP<-85$!ss z(J<{^u*&oWzf-D2ZhM6UOpa4;cxd_Xi8n*ns_KTEqvfCO<a%lCvdPQc8g+c*OI@bQ zI_+bp59+Kb`XVuZUw`*B-=1y4cU?~MHkVXv_q)_A&VL~2oq3$8q3o@F@AckH^M1T> zp8W|{>kCC)ORF3w9*ti*+xPtcHILPlc^1@$)+Fbt2e~ELoamgwspDfYahb)dpo#6q zu_ZPeE*o5E$U0DO@!|Dmn_?-odczxk?GA7sV|w|8>14&@@}<q$B0D}wpX~o|;8(!0 z`O@;D;fJ~nSIv?7ziZ)A)hUNXf2Evb{r9>fdy2}eYRe;Wo4h`08g8BB8@hBKzfae3 z2F(K|FPj{*=ASy9G&xx(K1b!H=#7eA_rAKL8fDV!1zdM(uJ;e?kUi2gQTWmo%ahrL z*$kEo{r7%7&$;gZUt^p9wYJ8IyBFCut=zTF?TK;y&$}NEE$hBiVYq|E`O^#EXS+;f zOmEHq>CGk8fAP<SD>Lez>0h>5;BqUyIX!r-x7h6cm1if<f4ZHgu%lUgAz!G%rwiu7 z3a9hiB7*O)V%}hSH@hS?c5(3PIZM)KMT$z*oG+V|{M^|n%yho+hx+vrua?$0x4m}b z`tRVf_(1paIe&uE*w%cMpTuYSW53g!c<-II!ShabnJ8u*J8<r~lxx*h71^x~D^KqC zGPwG~-*vUrFX8W57v-KRSTj$YSv#|Dh1hYK!@8{-&z)1$^eGbf_blIN!^{5i=Yezf zzAk^!q-5Q-aE15%9V@OTIjipOPB7`drrKzfcloh2^N%j3n&+SX?%3jD*l2h$@zKML z=W7`D@7evi_3E*10h`+C-a?DJ_K30xJ60*1*KbcW*mrH^<EojF#{|!C-0&&*GwtAq zKQRmDE_DpM>a6@ay8d+7)T6zTcY|%$NwzY5Z~nPh>Wj?!=s5P2HTKcg?3M5T?b-Ly zaN%>280V!2CvU3qV>?(j&&oKgO+hxv^w`>CkGcATu0)^rQN9{=X6twJeYOQ1*`m{< z{^#H7lTd42`Ma-T?&RYZ)7==;xCEaTA2u<ZrLXbv8DGE)&6hi-X>$svdI>Ac{q@mG z^uwS2tvq)pz2@2QV%<h3iOFvz!v4!&ko;&kKga&!ty@dW4#jjSvg~`-vm`l7PEvG& z=AkwEPZ;zg_-aKuw)|Mm-Pm-bbmxVz2OZYchxUZH@%dXhB;QqF-tHEr5fCzaUJ%cE zM$c<oMEqWOgl?6(wDI?KSqF*A+H&ioZ+8^3d?@`nWtXYj;ew>8$%pbo7N+wp`xO8A zrbKE>29N65WjuF<in*Ta^&fD&lYT^EN@1LPUHBcYy&F%ZzYma4zu~fq<xkbylH}Hg z2>*v-s}3|Dk3Rk7_DjFH`ToZ&xMD7CE>p<=sTT6}><lLEGbf9q=c~@Gt6z7g{jl0u zr=ta$ezPyVyZ0?_UXOo};e6Ffi#h8=gEP#E)o;xb&S6}m->>RfxW8;c$fUkWU8)z7 zXU~yb-}t!d=jG<_{TCBH9kF#h7v<#ne2&=rOIa7cu9+Mt@$u=-id`ORRZ1_{UOrYM z<)b#M`M2{znIENPyZ!B!TRs$a4!m@ssOFWX)}<D<U%MF=Tc30Cu(~!i;K1uAFWQoy zNzV+oU+?j9Q~gD*4`n;6XD5F7GjTez;$Fstz9TAkx-Ydj+ncmpl+jjw+Mc-1?a}nF z2R$}&E<NuU6#rz&%8UD@i@EmBO>L1odqP#^ez(oZ=_ymEG<NH5n9%in;X$j{{VGLI z8@+FQXq@`M`fTxMv%ksfT<6bCn0&iGwIu7;+0H}fR@;bNGESNI?dUSY#%ULAIe&1! znkBosrsCp?c(XnsuJ+2*NjqC#e^lr(;;T8m_4B-2wh`)<DzfPopLF-E+59+#IZgd` zS$IpP#r#_F*G8|pgbuA(_c%J8d7qrYzTOF)7uTlkWuC>s(%F6T+UIR+dUhEuJ+sO- z!g|-^$Wq}iCab0Odmbw;bC`6*EOAb(j9I_hmAay(r#x(XoobS#t1XPK&U(0g{`VBa zrOzvvCd|9c6M6amBg@F(cp>3UX*Kl`EovVFdjHK0&wW?7aI4cg`;1>)*8*nEaB)~0 z_2Iq8nbijS<ZS1@&^Fz_q3&LNMLd52AII|ZP1TaMGZ=O)u)DMM)Yrp@kNp&Bt>8VB zq)>EKY46PA$_uxMdFiflnQmskmuoV|Y%Yf|_c>|1l*5DMm;MtATy1f4zVa>o&ZjHp zO8xzPTx@HX{(jlU)m1SIZp6;G)vr)5`oev}p{ylEtc8c<w>9XP*#^Zs$f(~<=ImU< zd@Jlz#WI_)Ema{CYu5eHf9&<E`Si0Hp8FL)&dK?)GAC*AP0mA=UZ;QmoKknf;eJ;k z`;GtCgd!eRzGAy0_vV3PUElP(UUnh+Ctg}52V1rom8M(jeS5lnx~X3LS-IjY7p~j6 zeeO#a=%(`qNJ;%N*R1K;=c;%4?#yk>Ygf;5JK7@qqP-w;pO@*Zxl1DE&+*-EH#?<f zTlM9tkog78QdScE5&L${OgCC`K*UJ&%f<7WFRN7MpP#UL*RPPa>r40T2?<?MW9k&j z?~?c^q~GnLPKmF3_wk-5E7q*>DPfvhXg9ll$DET6lbh7%8~#oZc)RJ3@=Rv_+1E=` z3iMR{*VNupxwSsmM!9ansjC(X?60luTD^SbwTcVstFHa{!*{pI>R7Yi^!Y+u%TC$I zPu%kL=8F@Xug=eUGS6zCcmGPhV_pH-H!aq`e9+zf-OHCx`JQ%i3sZVq%`cDk;<jfq zEPov`aZak5eCEoJ#CvPHwr@JS!GP8L@pG?5^)ojF&Rr9I=7rQW(|2_q<}Q<aFIyVs zUlJ-;k~lrHU2{%u<n7j#Z=BCQkUM_=`<=qH_reePQtUQ`@>y?)*sA?(Ew^;$1g+H# zg|&8vo*4cuE!NTNy3)TgV}t6pvyYvNGukflS-$U^eJ0YlUZQx-vi-JID<T+;N&@?5 z^JoM;{yfDosc^PovhF69S)wYUhxJVYyiWg*F!SAzA*ZzBtSr~s>PbNt{4`a?ZVBhi zuGCpLmE&KO%Lch^?A#C7r6+%WH*3z1*yZN`ROd;1b6mQ+-eZDSe)+jMwWoM{I?w9s zhAcM?cr$H%TEKt1vZHCK*=m|E7@MX>RdCI;%DpaOEX^_Tx%%OGZd|#6@8x~P<_bzT zHmb8Hs@8^GQg{EQ@~(NFN{P_xqFquy9o|_jTQ4@ZM0azc?3aTrzYi`<6f9#(pTW0S z-OzpM{KY+|%XthUch@YBpI5H1?ATJfEnRYFxE=YJuP>ATeAP!_rj@w;iFN~PyLqQ< z_s;NJKF73h%g?INNco%c?yhS2Cqt$zT6^t@VZp9#g||Eu{;67dTr``%Ms(F@iD$vK ztUDBfcD1kg!g^Q8WUlZ0mV>RPkAE|t4+#_BaeDvMBc)%CeRuz!8(`nvm*Vv)dfw#5 zw(kxbBhS<^hX(T=|Ml1^LPgI1dF(`9zw)&@^BT&ntbh7#JM&20{`I?sda|6mB<lIz zd4BZWe7S6i$8O=z=WcxJJM&q1`pwp$-H#{Q7=5aI=d7z^vHwk(Wq(3q^-~)&*4!RH zbIYq|pBj4mZDgoyzjEP^q;mhc>yMbcqiYPaZhfmv^Z7gD+z$6muG{RiH`(3ZdoQ*0 z=OQbUc}4z<950`>RZ%<LvT@4<2CMFw-j~<RnNcHhRgkeJZc>uzdwHYxGfkK2c*s3_ z^QiNm#paoTb%%b=$^D>cY{j|sfJd2J+18(1eM&?=s6?HtlINXkmoW2_X6x*7C630| zQ_Y>-8lL=YnZ~v3*Rn^FLBf%Nb%pB>h4g<57udaZg}{LssVCdIGW?ae)wcb<aNsr1 z2B&E(<$XKm@74Ls>g5|ZwN1>iQsnx3mha28*VbfJq&|6miet&y@Xa}De6zP^>D{}g z{;$$1`hLc{gWLAC1m1WvKjZo1(=8eY3}-##J-}7W$!PRr<(Es}_PI?`?l4ji4r0E) zzU8P(;>NF$L7#8_VxGV}|CVfnNo5pgU_*n3UR=$p4ui6l^@ppcKl#2V<H*TF&h0&) zzkJ>I;h$mB#Fa|b?WHeX$4pH-W*)S5z2dfuZ=QKIh$ZHV?%mye`ShJTs(NcL?vGbH zQsWu8x3~Shf6d<Cj}m8Z(UIc5vv_XT(?iQmR5sM?lD+e^#E;3tP<q)V-n}zQtxj{w z>Mcr~cgZO>a&C9@-17&dV>h%+io2h#w@@g;ZmxT+CFfLzpau4Gf0T7;+iET`h}z-* z{QSeER<}j9Oa85`f0yHb>Brr;bG5k_;%(ml`hRr$9>2Kh+3bIfC)LeVKK1L80{@QW z4+lQ}IFp!flA-R$)0e;T&N+spNza%MmG4X_+f*>&g~n%%6V_b^17j4sIBX;=x19TV zEY_A|=Yz~EYEjpOHf&VbFy-Pmc8@1Z{@L_xS`f7H*o5gHuCC#ipM3rN*J8a|X1^IV zt%A<07VZ1E>**(}rikY!cW*S@s&Xjyd1bC#^M(oRs-@3wDO-h1h|qCgHv8qKk9qGE zlo$5I%08>#Bmc{a!CCP|TSb7&)SK0ZSSubrW=VOJ#%U~<JGo|O#~BeN|1B@4WPMzC z+m~&|BePj@#h+(>Ul103wtw5zJuBKDN8iazQ>*#Z`)p6z^qD_e^NovNoD7M!6b$?3 zkbLvd>urqd4}M^X7WlDw)xH<Iow-a_rZ(K%WA;^<eWz{t=35eTZ%o#+>pa)ta52Mo zUqnjk6!FDMGvd=^Z~rhZ+adb1(c{{*Eu1ek9Bitj61N0e_3SzF@$%_oYIUY;b_ok5 ztN(?SiZZO&zkV8TeA!Kf<;RR8%xBE7N!@a5%GAS;EMqh!H%aVVx<GN~rwau;7l|39 zo%-e!#<Tr?snzCh8nYVw6m+>)ytFUB-DJJ2A&@`tc#|Fbo!cE|-`Lxlt%O(0uKik| zb#lo?skvX*U0Hi(p`OcDBeC;d>+LLxZ)$aC?az{(zc+1?`j+*^62Cm&^}1i)@BKdG z`J72%$JDZZoIf`G-uj!7A+ZM8w!*y=o6iVq^m4A%t7UWNyVF#bTQSSVp|73kXk3eM z*u7BB{Pwbv!~2)JWdAYywD`*1sSnp0?<tez|7=z1dis3z4vn2+awTjmx87vyKAj}4 z7G9?s@nq+_Lmvb--tpROB5_fy{}<2ujs-hh98UAeDem0IwKc8B_^7R8c!c}W{GxSl zuIxD=9^f3mL~n83Wn0?=v%K7*cU*DZ$#<aO>m=KmPDkemK0k82cf+0I$M<}BcgMj} zz~pk?W0T5WrF$87`+r<?o|6}m!qs9De#!LgC-Fm${#_ev^~_oQy8P7}4<-dKSQ25y z%NqY&@5j*uiQb7bH$J!(_3hmimFvqNx=kr&IPJaQo5L=<qT7~xHk^7dyZ-h4c4=+p zWdX{|9$Pgnd3seK+H%SK)CgbxS^?dy_im*d`CMtf|5Hp|Yj>-#VN7?a(7$C5&d*|r ze%h33J2U&&?PH(Pu85Vt?LNZ3DMoL1UAumIXsr2^DBt?k8?LOpxW9Woi?H|Xs6V0F zX5l<6d?8K?bnbC*3vucwhE6#)`MuDlq>p7DDiWXfPMaX;pSV#kYtkmRIyJRSv6;&a zJU>Uro|$TByCK{B`?_!bf^mP|ZPQ>b+qbZA*P>+hj~6DaNw?hC^Knk@N`=Qa+y3=B zZ=1dNW5E2h$(ve@w{2pW?VP{k%qF{yA4;F>_B=hKbu*ijbVt@|iNKY@S$;npPK&(t zxoLgcF^@B1?oUPblgdE~$4)R@2y65ecRpGsIZ-8hsok{F9|GP}-IZqMA9&mx{Yv4H z(__1n|2$e2zL@37KJUE$!r#F@TPiqCUSZp;w_z_|WlhP~l*Osb-~VK^>aO~-=-BO| z;9CJa6W?{KY4fgMw%4!vLDkH-kIAuIA{#{}{rA|Qwbng_+jGZ6p9veBSASL7rpI&4 z$zS3G+wR%fW|OX3e+&sb80AsI>sLKzuST@r68(Zla{agW${rJnc3gFO=S?qXlk}aJ zgJUL2Y*t!(tUbE#5O45rM>d&QyY$W4fy?)-%UT<5($MR*s%PH+*KIoIrW~r=c5br$ zciD3ZKjroxi~TtxpU>Ou{-s-qe?7Nvnr7vEJw&m8>gG-RH&`w=39@<Kz2&*s)-|6t zyi^tDUpoDGmgiN~<*$vd`NXYeoo&25$!5K(@kyRa^<(`xjXcch4YRcy-h4JQ+!uP3 zNA29&huTKRPQP+UR`CwAs$RNTe#Pc=jo2xP@tyljQ=WQVtxH}tZSJB7ajSPFhg|!W za$nB+nH6?Q#%}sNu^E2HpY7h>YV-W>&bRiy*7xpqX>PqFb6H91%+}*JdOv5Z>#w<R z@?8-7{9|wawH%+RZ8+iCf#(VWp9?1Kx+D~_n&ap6+$A@n+oy3VtzO_Lc}_H-Of){_ z(4lv$`P7qd+1kv~xhfMZINO=|Zboi`yR6W3kymeuodWZiSvwQn-N-8b?x}R;*`BkV z%l(WTdeh^0|Ns1zboI$nxAV!#2JADJ@3vU=A!4h{4%U_2pLO1~UO9S4%SrCs->EA) zo-1$8Ofg`1!LPV={~Vinl^xzXlV*l`s@SiTsk@=5ci#EJGlkDb(iezpW)wdVt241` z+Pkb}^6|4lbFIUtvRE|Cp4jE3v0f-h-d3@6s@|`wOS<iJOhu&s8E^Y@KVs)M$^U=< z+HdrGYy9u`?_W)k^ZA_*JEZ=#zb?Ff(`=!%hyUZwzwbM?<y&NZ?2{c^r4!4i+dd7i z+U788YN>3CcGCtfr5$_zeLw4b?(6Lc)rxgOHuKF68W+yF%_FQ({OO4k*RM!9$C-=I znw*M{ST4Fl_@n8(&Ydr2z2aA1`SoY;na=aiWb|9l1<!w$x^4HDnQ>eZibcW2>wSX~ zZ(WQl)oPqx@3Ygr>6FdF4RaLORpoXF@3Z=36CPyPI`4I?;42n`XFKo6UeV%v_xL2w z*;Jnu>XRjRzO@TUOKA`-opG!tiTMUAi~Zk40zNVunVtL2sD@qP6)tl7U3^M7r?T>~ z_v3z6(-T{in|4hVvOXVVVq0A8)88U*u(4aTEPDRaf`9)i)RPU&rx%uF|2TgvxaEMj zy5@HIGE--_b2i_jrS`Ry%)j>KlH51X1Kqs!l};Pa|NgO1L`%iUaKpW{gByaFwM{>4 z$x2xGVRF6?ch94Ot?Qk+eVl&0Op26q=k{TW%LzH{yyd{0?SE{NGxrOfI`5}p-`?FK zCzkT*YsV>14*OV>Q;$1Nc^=>vnkv(-urBj{>Y^5t_jhXoOiuL}OgXc8)${w)&3lY5 ze?D5+yzzMS^(cG!(-*p?Xn%KOY0B4?U}fyGn#5&p7Sg2?ej|}(3(G;(qd!|tI|Lq^ zB^-BL`i9)oo}!G77e%!d!V5E&=2q?%<^SeevN>?;J*ycauS=FppQEVoj_<Tj<nAeo z_a^;PD&DWLiED1smtNM342|L0GecPVl4K-n&4O$FdQKYN<esP2FSkhC^2kJSM;WGr z3q>E69%D0ETKwpX%Y)7U*$9cEZRL-mqOQ(t&;DI-g-x&WP{4e3n>D^(XXi&g(Ux5B zuCVuT#V5a>`Z=i&ZPNFOXWjGIx?J?O`0a1Lil11ndHHqsOD~?BWe{$5;jpFQjmoLk zH-ueI*c{fg<CWyK5_9UFn0jVgh&QX7pzx8GJEdY$PX2hfRByvhf!Rvi&KAAhbaHia z<euo3or}GXIn3X^^7GYu`a6wQFPj;9rlCGzvzJ?b<DMUXelzE+U3)(3kX_}BuZz-W zo|t#-$^EOY7TwYbjbQ@)3C*IfBTp^*#S#`(x41uhMe6N8leXPT;j3gR*1a8}7}&jW zt(o$jYQgT@DvJumY2trB*p{C$oU<^|uH}AL=(bIzVcYx|^_D!(x%Nl#(U0(HM@~)p z;il91^9=hY{)`f}=k^~8P8?30-{_y#wCV5T6pQsGk4x_Uh!M8w_~)JDqocL<<%6^B zr(QN)4V9XHROG4<)9sv!x<FgjnSbBTeVM*<-;94%`GKq%-xBn$Zr?D^Fj~Xsutic< z+Fiq*8|<&X9e=rs^}(5yF<d7WJFhXA>~HobW?E{{&RX4v1+5x$Qp<17pZ@0fg8x<5 z<=h$?4*rr3UCObpJ=x)Ow2QpWw2rbPmyhg9m5y<7e!DNCpm2Il-l;=*jZ2SA4`#d4 zx!ytcgw8D1YcisHOc_P3-b<d3YC2JzJpZ<q%G>6U*i*aHo3d9*D>(i3Tk)r0pO3Q~ ze?_a&_Lq$uFK@~w#@Prm|8!3~&vq;=fqgrJ(Bjiq0~>ywI4Ibm{!R2DhhOeT>8L2% zNnPIZ^L|h9o_lmtrNsj8qFmp9wO?+mO%3<xpKSEG<loku=I)FmbMLxt_1v^KX4_LG zR+SebO9N8O_Ax7qS=zE&)I7S_K4s30E3EpO?+uES!aaijGpid&yMB0jJo97!;lSIa zP6B(3TIOE(*zC!`|MTl{uJ9+?x$BP~;*&V3saxuLgHz^Kz$})(8hW1!`*v$pAMj36 zT9yA|ZCf(qHYPSVQTu1}KW}+_E3V#UcHZ$%v)N`{`0{IIrPI{985TQ>`oesf%HI|W z&G=dLpt|Dgm6`zNhe<m74(5CQiv93=Mvwg2#RiX$U*@l3s9owEb@#wK*Y26BukNhw z+heQMW~Q1dzF#*e&`)NS*M0F7IX=?6K6(F*VQ{gztQX2EVEypeq6n2|3*6e~NBt;a zY&dCoDm>?<`sKpeMU8uBEt{UQ;9`IKp)U20s&_g3_sjA4oX=)j_UxOp!_(PkFFV9` z+;`)<x3j9A`I^mA$?fanwo6T4rue2J#mpzu;(f+W*4clT-mKUkvH0ilvwAn)r2hR{ zn7pyfSjq70yAK~64^LP2yS(7#$L5CTT`_&r3xWflJ$u5bJ$=WA$2##GnoHVTjKZgg zY?@d3hkHU$q;1VR`Sl*p;`E-zSg(7?{qr2x%cY^m3=TW1Gx<t&+~d%<d-J3D;E7<F zWh-s|AN*kW_qzAW5HXD@+d59Qomwk-|K^r8u5GrFkL{;MY`*$syXB=!owawfUU^?j zTC-F>MCjM@w0|>J`>u@6`jnsZ>FSl~UDH09epTJ}FzeOnm|o9=(Ua?wf3I7%FXYmv zRnvaihFeQs3^{GH@=526sB@cp-7Sng|IIDd-23^$(vmYr{bz8g`akK^{PD-%)u^y` z7xTnRYxOpqxqtpV_vwjiHcR<&$Jn}X8b%-B^i5mLz3zYcnokd17tiyHbCS=z#wuX% zanIGPJ$ix3&r8iW-!-vYvwD5z*mxr{V2AgEOc|#5L$V(v&NUn_W!=)S^rE1{G~rv5 z+uR$@RZbK)pLx)@OyuTV>3P|HeWw*n6(@<kUy}Vc%&u)}f8x{Rh@TR0=CR`&zFID9 zsE_|C6K2=$RLi$`>S@OCrug>5ip?*DcSslAuBl0PUUu_%xoOHQofk7iGy|U(nw{Bt z#yY)XrEa{kpRT@A?l-Hv)ao@k{VTT}pO*6EkkYY#6K=T(y?h;@QF+3y(QoFJH;-3O z-7`%-e)E&9Q4_?gue|RzIQ42;;DaOT9_LGBj6{N!GuRd7HP(uB*VUI^T*I{W?ZFGI zZ}iXUX<fT{H1YF_B-7~SXS=>$)~vC$%1$#COq_Pfp5v{zrh?}o%g}(nC*S|fi2C#+ zBq?RiD$jy9r>7)K^|d+M&v@^2L*hvNVuQatCN<kEyIMW&wywRx!!9kv%C6-2k@bDc zDdsqjNwcHYUz*cfeCx`#6HnHdg{#ffWY~DoSXEg!PImdSi+?sg5jqk+S!t<;rM73* zz1ato>`N6N=dZcB^hVik%i}UZ;b&|Y{%@aDYgR7tU~`g0v{(8h2~nNdPCM$Q86ST> zSyP`qcW$1X{L^^>muFWf+%FWH?(X;bgxAM{JZ3rnyK-)yTuh~G9|kn(8*D8)mZSLp z;087MU3cvtf3{S+eBtG~=W~MEch@}o((uu9!;ZpvcdO=Jh_T;rtvDd!|KHQ|RHipq zf8yBQx!L0XqW6}|{+kFUoSD5eNHzZ4M!pjYQ>V#y?rl=p%<HCjS7`pjEj4b{2e!6# z9_v1JDp_Ac{+xkj@1Z*t>rX6}Idfkw=uy<oHN{7+9G>%Ojt}poY3(evS<B)z*vuv@ zVm|x$L+1ZxUGu#Xj$fFbsQ-Jp*6@!axBgtV8T)@vyBpuv=`MYy@b%0lw$Pm#wh5PO zA8iZs3Ey>`wQi}l&xXsAlk;PC_@CI7Tz(<l^vM2v!;RbQUSHHP;LrcJ_<E()hZarg zKS9R(!cE+-T$%3f|0ua<-i1tM<>U&JeNPzQs44l+)Xy;aF(JBma=^6Dv;Sv){pn?Q zb?vKb|L5GEe{YgY?HsdhKaF3$|Mu<O=^xg1_ix_2TlrD__2pmz1|Ku)hG+XPe!PEo zD_e!Od+zhA=l=EEzj?E#BJgh9+57p=F6Xi*y~uwT@_kix^q=I{SskxFUMT$_xBTIb z2|xcA<@5i4x98S<v;UXg)NkGWclq4C(|231|Mu;B`M1P>`)hXY{;i(<Tl4w<2XPI3 zb7JzY{a*j&G0%m$Sv%|Y_lvijoL{$MAFER>_wmF|{&&)?vX6MZ_dSeX8np89t;CF( zZw^oEG1^+#nYeH34yHVRiQgvA7Cn6Nm9=0=>BWrLsISElhpxn4U*y`?abdDj?bjEK zo9|xO{!Zq$(7n0L-u!zSJ>*)7(%JcDy-hqSbhU137x#MWFNXJK-)bt8dY*p0_wwS; zo1eW@z5n2)qju-@@U_Q|X8v8mn6Sw2>aVNvf=vJBaxg`l6*zWBN#g#7C9<VTFJ?8a zdpEsv@i9NWEl-sE6i;XyyE6W@xZb|KOxX2s^V+p`5k=p>RRl{N+_FYe@Zt8Vg!ir4 zor#^%UDF?Koc!R|t%IhAw|?PS!oB|d%_mjoY*$Qry=ud;BCGa&*NcCp#+|u-ZNL8Q zM0V55ZW*=l#}?kWap&gI_v>~D>)BUy?q}cNv-LOHs*tTBB~CY*r3}m?w#C+`#6&r8 zo1axPogorxnN!1K$anJV)t?_D|9TjotMXaUGN1LO0%ymS9dfrMPOSR&O|7bFw#mT{ zH%iyA&Jq5x*sfUMyz*+*>6RB3_@DpS!_PA{Z)HZK&zbT&cCNL`34bzw*4*lUU7pZu zx50YDr1Rg|_cv;pn5XoHOfG)wdqJwH)xno*!mFnmDLVs>l@>Pr`MAPBmvvJd&*D|{ zrj@?W`W3xxolc%k4EvT9YugU-S(!imTK^|`^`C?H-sI;0`|<I-^#32n=O@NIKJ`q^ z!~f!<G~QLU67v!(s~;aecktP{<2<v=U8`Am_US2>&ipptbkY9HZc|m3MeUz-Vu$Es zX1$Kwzj^E4RBT-vr<=2L?VJBo|E_=Y|E~P^?{RN)*T4Cnf9GGg)P9cJ^?bkQKeM^< z|F(Yon>}~xIiHq&i$6Gj>firsf7HjlNqzeNo!3A6&OiSZr~TKjui3cybJ6-;yZ3&6 zd~bEQw(f`CQ}r>sL~Z}azkPpx(*NDRtE+!oYjBr+`xe*mcl-WH|F!@B_xiW~&Ht;< zPd=@mnB8#n?f>b&^}mIG0|`I*AAG8Q)Bp1CVV~+%|8L(lF@DPL^tM0u@87Ne>2>@6 z?Kl4`cW(al-{xd{S;_kKKbbmSXK!7aT|G}e@Igw%quBVK)+KMF1XnrPy6=ds)82E@ z!8Y$e`PR%H#Z-;BC+9v^c(_M>wB~uc?nUgGuF&tte(;;>sN8wDLwj+}&U4w;BI*)B z5gRM^e>m}yVFG9c%`^^~+ok_>n}S{n-D+u+<yjlW^NBhCgy)o1ue_I4-Q3Flc3tjW z*@*J123%DUtM>f+yMFe4ezX1W<Ldr@d+>X5^ZV$l@2g)+%k!DV*KGNhv-bb{D%W?% zVlGR{?${arYeo6d+0483`wZBEz9%-D|4rf9>eQ#~S1CPZ8;7@1$gkjM4|`?fl=J4~ z2VH8f?09D?cG<;4$xyV>;Y(t~i&-W&Ir6lR?tk}zkK=x~d`V-*qGW}uyfPXkzP!i$ z|6k25UuXH_s>Z3`*RES^;gnkOkD1}9yu5|5L*Pf{d8^!bBEkZy%<{JPXV0+OJn_w^ z*9QdmuKYXi;%S~=TsHD|id5QlFS`ik9RIHLV9Sp+3~tJMo)tSiF;H`S{8U?VgO-&d zn=ae7$H(m386Mo@4?0n_ra<q=JmD6><|`uYtK`qF5o<79-EsG|<vQo4WG_wo7Yd9; z_p@UdPoF%zY`JTjz{&Vcs$V~E7d%t&VAi|RO%h8vmdyM5rtxuKZ+lhD6%D-u0e#1R zo?K~`_vgY4%afT8cwYQ5eiUDvHT(0dLn3q5y|{7E^zbZ?;?N%Hi$@o1b=f%4m|I}V z3?^mgC9cco-OOEOz4pEE38@|%ev#frCBHQ;`VJ-c&OC7FONbY#PcXPWq57qZRMm_V zOJ`maoEde1UDL>JhRo8yJuVL2Nf|QNQhcUV^9Lt=U1L1!`tj&vUw&BdW==>Cs_V-0 zTB7))N`%#~TBOsUxwu5RNbSAQp<_Aaj+RN;Cj3De&%GvfddymsnzXQG&CH0bQ?J*w z?01RsTU@a1)Xos8nNh-9P6i46ay<3WJ0PxfyC0*k#otpUyYy%6ls@^xtwx1uZr3v7 zqdBXhe5dTqxYEC%(^McQR<_UmWmLr>o;J}((mt|^7NKk0kKT(n(U-oS{@#D%x7r5{ z8(Ais%d_l#cEf;Y`K=?7dcpUdylQ6Oa{K$kB9EENH^2FS5Bpq!HQfP56EC^9n=J6~ z3H<UQ_b=1Ow|8zZ+W%Zyw@~h0V#4&Qf4eH9_Wux--!6Ne@7S)AKTM})zt@|+eE&W> zyXxNcO2?(Q$L^Qizcc60-=MvnN0xi+3-tdg%&NGq_L4<b+}stDm@k&PHSe(5AG-BN zZp`-|Z@#F9UjDkbE{6a9il`rI>n>aDwK}+bN8DrewQP(Wt%o*No^0uS^v~44^n1hf zl%1Q6AMs6jFLa61phriFFX2gP<h%BoqjxX7e{xwO@3Z>P6>q<My=;Cwy|n5^ac#7n zwbkyb+B*l@W98qZE6z9Lo87=+D<-=^O{L8JtG6EaA-%eyn>H8y3ssl?s=1UAd(Hpc z&g+*Rb=+Th{Lho?Y?~$KbxRoUw0cq%Cl=l=vuwsLzvhh74_9A*IbFAY@4|T7pRZ;A z?wZ>C(U|ve{wG=9UApaaE{0BC=+|^)n$Q#7!vDP~zpJ|Move8HOaIGjzkf<bHzskE zu1a5B_1}B{Ki&2zQTeB@#RtpZ+Qi?hopgL+jl02Zp=a5L*612m|D9HMSHmn(Y~p^7 z^V^PmHvPJOt=ez@FRS$<Cw`Feu;J&N-go4w?88dyfJZH@oqt?sl&YjHXE{1K;NNtw zf6-Hy$d#t_^L3`ldTuU%zp}OZ=ewSJKR=0c>vw*Q{#B}6FB|mVYGG1LTIA2}s28Wi z{5n6|9{pm%cej4^$@9VS{}x?q&6&HjL5PX>)!GSrYn@fAH$9oX|Kj@klkp~eww8SP z9Ixjc`NjJ&-T%{b|0VB!1>OAXQt?f7-f<JRbux>~uk4S$YCms*(Dd>v_txgDcMNE0 z`P5f;_f))nVE*q-+wCu|`0{kco~8Hhow&De>9@R;>im^^rElJgUi6@M$%E*uzi!+9 zO?y$K{PNSZs2vk^O)`(jSSI;uy*x3=_tjeE>f0*2EGFss9y&U4hY6>+`=d)s#AL2A zoh^4i_kI3~xcIBdGqrwyH?R7x|MOk`5&i!`GM*v3riN6xKQ7lk{$BR$!?14;Ww(}V z-`=g@$ETvcUwOuMF14@k!+O=e{tf%~SGMr{+9&hMuPlzfxwzb9b>4BW&W*n&?O(ya z?#*Gtt<O(=>x;aoxz!{2$tgptW&d_=sz1I)XX>ma*J5w};{2Mg@VnfpRzhQ?;}i)G zuQ>wqc~$1m-4Hg}pkC?BFQLuChf);^Efrt!sg(MAWbrJyvSYzLwoTjBTfRCZ)q2%_ zPr2{ElGXog`6D_1qVt-GZ#~!ecUIdh6tR6>`LplV=alN@E{^^|AErcoPkHV8s#*DW zL0QNCKYf2(7r#7x(Pr_#pPTB_ReWnTeb09^?OXD|@}P{@jOSPOFMM;~a;nU!N%=2g z{r4)L;#YlozVnl_=g;|>CiRmSS30g`-}IIHRiHw3iKA-sM5E{%2^05sG%Z|`&>h8} z`dar@wZ`vzi|(~;+OC_WI7N3$Mu?N8mhP)ujq2M<cHC3vEt6|K)v_p%^XZB$H>OPb zYrXWF&CR&x6-$gNqpmCt_}cL-P2+i5w^!RLl|p}wozEvl^{2d^Zt`C#^^=gQl4qy) zA{Q0S!js<?n3dc-Gj)H*dgZ0d&)@I}IVL>Y@00l6?km=Zj3-sZ3E3)pp7(xLt@69h zQ=~_QRe$C7iErMDCfZJlhz~qDQK!;1z)k3ssOPtdQ{L<kkbjpVU#DpEUS-~XfqCUZ z=fAsjR(p0jFN$=(^oIBEtCR8XSKiyZ=6m^~)9Rkn<5#wB%y}5DWjRgt|MaLI+jAb) zXXgC3?eg`0n)^&Nv#i(u-lB?~tENAEb7IC5fA3v$pSRBaqNEe0UE1!qY>}VLk?keB zx~E%|?pN($4&7as-ojaWNwK9e^Wnj3(`*dac}YiX2wD5fv7Y->TlA}w%dCFo8^5{J z(YUvA`}XaY4W>Ht+U)Mhu>A{QnW%i|eO%6^#Mhp}whwO}zP@p_^1fqA_l`~I7mDLh zo1o`>*VQStc2Z?Hf9lJ>CnBEQGd8J>*FOLJhr^y8v*(L0n#jx-TK{FLS1{)>_Q%V& ztNd74+4eByc-XEtrCfK<urYqx_aM1hZFkK3w+pIywXc7>a%|^}l*2FmzTLUKY}4)F z`y7@}65B1;B+hYJW3&3xgEcj7=kfy5X3ERSMZ_)_UV7xmnoB=EhxHr|d8+)di*stR zS8-nUGL>cX)s`n~7!;a*VlI|H-@lCS9(bD2za@Cvt``RmXCIK9dTh&HC7X!Cc}4e5 zlop>#-4@8WKKnu>^YPi)?+@8L(DYq<gfVgQ_BUTD6N00e4*2E&=@z!iJzlz~a2mJz zn~>t8o2&Cr>3L6*&wQNqEzh!MYvPRrk>0HKkDvF%YHe27H$zlo!c5!Z6WYvwdi+wh zy)#*S!g*oO4PJJaE6cOPEB?Mn<GSB=ZvW9K*Y9S&uR6Eo`U}x>EinqBliqe`R4t5& zdTDr?&A5V3E^yU*VUq)ArOYQ>oEse)%y)f_m94#i-~HWb6H`8%k*@pb*Ymtj*Yo~U zi_oQ~Uzc9;dZWp#FK|NVsC(+OODk7Sy1vFpe4T;!@ng2L`Pcs8+PragR6gHF%ZGL6 z*PMy_cu;;8zma%@-&C&hZPuq=bn|emwR*8iL+XytOzxjrM<;B)SXm;nY|C7E`N_rR z>n%&u?yiWQRZ*Gu$L;>*=vklZ?r&LWaQ^D<rANN*@|<e<aNgaWXGHJM)KWD%eSHn* zs&fwhkCvQ^n$O<(BkpYHHy4@bCL25UsT3!1W~FD@a6EdLxJSe7y&t!%;Cz>K6K9iW zahx;Fi*8oeJzp55;8bI%`sa+s)X0csnUi;x7O$K1N$Tv%kJZIq>)Czpm&}lq*UY+> zadC3W-kdM;tDIl>OkdP^Nnd={Kh2#+j*C=Uj_i4qSa@)Q5NG^+j~~h0&G88?V&cr6 zzr*?>A1!}VnIx8$UgVT@hQr2phx(2c!j=I-Gf!36gqEnta^0wW>FKfa(Wc;<d#%dZ z6Jj>`Exo0}euj0U2b);&ImWbWQ+j@@KVlb7pK1AN^W*O45_Ol8{Dt3UKjK&3Co7vD zd~#C31wV(nV?}v&$3=F<27bJ@@PI|hro3q~=Bv_5O3u_RaM*ey`0Gc-bEnFaYy=x` zu%Gc&+`lx*DeygWvBlgyrl(9F6eI*PB>Xw?FU3xZ<F@y|cUC9W6R+%j*Jb_HgZ(?7 ztev2(vuWmRjY%gDtNBWm1v6fK^5K##XK~Fo;T7v%A4%EC^sUCu=9)sPX5F!!Grcum z{^|=473$q1xn#Te^WF(2?-LgPeK1Gj!LN_1au=&^{#jaBdvni|4;OZMKT;@q$6~rF zNiFWR(Aq%juKO`@SNx-oSNO;CJT}^}cS6Y?#z${H<u)gAo%_Jowe_T2gX^xDd#mi$ zFLqBCabTzq$zg6SIw1e`_mukit7iCfwKmwl`hI`5%sI(FcXtLGPF>G`-r~->w_7G| zRBiftd9L`6Df>>OpA~E0b3t6xy}atkm8$IOSl1=b{9^?EuexE;_vXm{S0|q;+V8Xp z35*f?SIF~!$I*>NKf>J@=6(?VqdnilY0mK}7U~y1xVxq9d0_Qj-{4u^qY0YfZW|9@ z%ZZgYo47G9c!`Jck=gOP*N0kY?OCKDQ6u$AYVGRJpC2FdeEj9pEa$XYiP<JHpWH4l zogaKILqSh+dvnB`xp8Lx>wSC<2$bxZ*x0LcNaEyn!!Y;WYY$yNZ)I#0w|Fbjq~#=V z<#vFf_v>Fv3M1`T6=z(Gwzip=D6xz4>%U*T?bA$4Cz@-W;yL)|(vA$%^M0yR;w`>= zFi*^1fBv2U$IpLjmV3`JyO-`Yx9fM#!Oz{t^v`Zyt#jtH!yU_i8tmUH|2{I(OJko{ z)!y;gyVG=Emv-!4wr%y?OLFI(npJf6R_-y`-5Gl_V@&+`dj)q0*a~uN@cAO}!pVf~ z*IvV(^!9MgX_k{3O^!L7a<ZB5sWijj^2y5276-kX74K;aZ59%pFvsQnqg(s0=+7#B zkSp<Ia>bMn0Xh0jg>n*CY<3@b)9|Zs)0@bKnM~VeYu&M7>Y3dh5Or2+-d)+-rfR%= znU8Cm+A_Fji5y{?RP*E1947Gv8GY;S#}sT(_G3I#_ii6&$Q@<B)0<a!A8tFg?P-5g zpTUPc8GmZ7?(s<YaQg(O56_AQm&SWXE#~aan=5O6=Am_VyItOq*S}VAFb3p1E6>xB z-0_K_HfM&w&ZwWI+$FCRKUVGSm2Q-sD_QjO+nWvn>xFN!M3-?)$vq%2?~y~Z)$^OZ zn!l8Cj)~W;ym93EagTVRtS;}9T-L7_-s!6Rx~cHTw<!@!iou2#cOFcB9@}CSyL$4? z%^Zyxyp!GeQ#AMtM80<}(OOrS-+oE|;C8d7yK*0|rbQ^8yY%bls)HxJLUQ>WYI-Y` z#9t?xohnf^-&p-D_v^x6rpJ^OFU2xH_EA+6JXhRoxLad#&8zjVb*DaYK7D0p<4P_= zizL&&maCHP#SDus3BQp0Z6)te8#nI>Z__nJ5#_a4j*Ik^N}jq`%+@@y(RAXcMgQ8` zmPp^&o2ksya#*Nl^Srl=llopiGUeSMb$ItA`Pu)U$*6oa_2Ij4C?@&P$)YnclMk|d zJkXiYbYkZ7pV`~yd|)};oAkqNGWV$|>)ZbRjk58J?s`_$^{K}8#cIc7tsWsoBhLQJ zOW`TIsvmot)6Cmf`FEA8#UG0f?RtR}`^(GgpSX+rdcKZ-wS=)+<mdI;hYwY}18Ob_ zvIxs&J^b48L9Np0kWgKR>f-q~te3=3X4eR9xa%6+b)vy~anBc)G^TewN-vJiw7-yb zZQa2wKUUAY?eg)a6<f!Tkhv`P&x`k&ZDuffmSe#D`|{Ts)yA^xjrI1rTY{^(m-j3w zllRkHyth^O)Qr4ONosFC&z)D_(fR$#pZOm+^>?>Vx_aT*O5NackD_yhKW7Lh&*oY) z_kWG}=O-J>ip0}ZF13IDp}gnu>sjx%%-m&aT)DgSVC}V^Cfv%_+FE(rK4?bWyIEeF z^Qo%N&AQOW=0}2Lo%i!ux(e5yB?Q~ZaGmd&eRRj8;OED_{mK-JN~rgj-&rOg?-^=P zzH#ec6Pr`dS1*~p$Z`H^(NFP9S1<lHYxOpp+1W);R8oJWe)Lw;Id^r6+)vHV7jLLE z@0{JMeO}@ad*@3-R}-xhg&sf7#I$+}nO)Gk`P$P$!d30?$GzG67FOQ6=qVSW{o>AS z?Vw7p=VI^9O@Dd3lEH-MSf#DRy;T>@%icWqaZITDWuX~;;DqoAf2RZ%3G=YxFIvaG zJ^pN&P+9u#`>Q|iul_y1`t$zF&+D5dzJJ_wTIn3a{L=r2|D8Vb_k8v4^<mHJ?T@<8 zTm5&vz+|%@3-Xhiv<w`#O8<QI;BC&Ib2kGjba&}*J-8>xP2uYPt>^rnHc#RIueE4R z(Bn(#Nk(@R4@ReyZV<TFbSr0Wr2U>d_cm6n<zsyyufD}b@x!Al-)79(zT@wW-|O@5 zOl@1br|5eA|7_)b3D;jTs<8JkO8ZPT+Ozt|UgIvw?W>(;t`h#$<84{|Gs{BC>Amrn z6&Gf9ONX{{Cw+XQ^4jbF`Y-=q{aZfu-}x8+{a-{MwVC$Rw?Xn({r97%=U(~izrHMP zZ^X<0%YVH1)2PU^)q-X3(ZUP7rvF|}p5YM8zRBdM-Quu!R<=<;-NoWR7d9_9;&o*| z>{p{-8Y3j9HrK*g%2u=2e}fZWy7^ka$;%9w#rq}X9$!5CK_Zvw_GiPCb}!$|Esf=> zXKpXLapcmpyKkMgA6|OPHg>_{3lc{=KmL7xk@uF}jeN(i%S|7-cW+N*E|GUTFJF0A zbHn`~j<;WywVF$BySp%gx1I5F-er?D{yE}nwJ*-)o!M8<wu3n|l`G;_LB4C9LRahR zLjf0;w<vPOrQ7UgOz5*-p6tV0`X(ahF4M|311*Ee%QmjJ`LM>cZ5sRM?YBR==S{!$ z;z#pAj>Y$s&c+|J%u+l!x5=TF<*DWbq3AiY#hD~`b<CdW_BZoV;)`G3WONJ{70v2B zEckO~wX!+;H?ODm_P^^(|9_vp`G4{I|M&hEzQ20<YRFHsxBpkY_--z{{&~Kr#eebL z|EvEW{de!xzxPxBr>p*-f9e0?C;w0V-#qER%K!8C|8D8*H0pl^JxgWMfBo-q>;BmL ze+tyTc{cva)akpg&b_3`y4}Ef$`|RRNkQ2r2MpSnU%&Xux@X(D;}?0?yz84=#?bGb z|F`jmOwgPOR*k>^{oD8I%b_fjUF9v|TdhN_Z!xiPzcJu)ligEk7P8Mh=kd`n&gnBG zoXV2eCq9mRKk?hAhs*Z7Dmq&ezEN-en(qv?XMe3{v|WFx;&1=WXYt;j3}!QHZ#-@M zHCX<ZY(&zjS)Z>={=(#x7rtiy&3hatlHM{cKEI}iW%8FzD<iE|$ILymdFkuf4SLW+ zSzCVYa`*L_njJYqzxpb!16iLi-B}sszR3M~(t_{i+j;a>sk&ZU^MNh@=OKysd&h+j zHsmwB{t-8Q;f1{W9PONJmLGO2f8q!UQrPGxo<IL+^|uL48|J-N(%SR*x{xf}Hs-f1 z|5nr||Mi&Oys+3u_MaVhy!{D<J0II0A9#7+MP_-%#bYTg)>F+4WtL5G_^nw{X*yMY zx%`8rM~mWvFJ*EiEy~{=);#Nsg88Q_q8XPaG6jg1$=zK)jp_J253Qz?4lP1#j%@p) zH@<OLA+J}us%h`h<t3LyEZb`ISp&lY<|#EC=Loqd?@__)e(=Ka?uB17GT6NOKhA2| z<`&DdcGKpA3V*8a7{0Y)>@iY$TJdLZ)9r(@(za^@^jLel1zMT16RH);CU6S$7hKsn zu_J8FZ)wLAM!iMfa<i2Ci@vM)Fht#CJd+V|SUSB!pxTdrg`J2a`)rN(`Str9&;Dwh zz&wS`qN`x?%(5Mc5ss%cPcZD#wNVH<-nh_zOVR_|nW=SO-Lu0a6CQ3)$UXc-YL61* ztv$uNlv`vY)asWqboyVAzQglAc|V(<#3dHxe@{K#4_i)dwOn4=TlV_0jLnh_2N(`q zye-#zMc3?PXXG1Jp36OtDrC!DmM|we<k?=AO!%en$@#_iMb#<MFAp5+?eO}<)FEP_ z7ZF&r^7$8cqq!T}PrjJEy9{*TYQ(neZRXF?=eV_XTzYogA>n~mn#giiws{JRjHeeS zC{-@&nDcPk(G-;pQ&kv)HUvF+aiXM}C*;7(!%r@}RPsFZ#4~n52hU4>LAlccUZ)H` zYkxlR`5u3svD0fgxuAZJI;HgE!2%}R-lU~58LdqH&b~oz>l^EUuO;7=Ppdo63k$GT zSX`cSa#fjIp5{xQCr<1;gwFd!+N^GFJJ_dmWvh#4Yb{rZq?MZ8&E|#P*8U3)B~%$Q ztO=CoV@>$PskZg?{d}KgVzT@W>=z?Eb`<EI4ZKliw^pSzNML5zs$_}eUh6y+sa-W9 zjD@PFBXTCSNk4x8I%xHzVAPDo+*bT5Z1XZ5bs9Ibd1yReshZ&O$Kfwiz@*qSH|H>Q zMa|!4nsFs3<4$>|kyEMNZpHTWO}|ui<EuG8hHSj_Wa^cpH<gahnKd!W(81YAS^dQn zohKGcq)YP^m>%|hR9O?Plq0=^?<eo~$7Ujz1H7EhzndVm=E)7NSwAFICRQDK@B2*3 zCnmrwQ1AGX=d;9QEh{pbPIIo$;qH}Z^6gPQJ8|8XCzk|jm#JQDE1GN-Zm1?d@3@2E z-MlE-*2QKgOf;`d+3R-4%T}r6>6FE6p7yC{HkeFj+2d;Msy(YsE=$~r`FUV8!^Ucd zMK_P#uem0$w6IwIdXT|Qxl38BTHNAlH4mDNRzAqsnmhf6p#B|JDXHBR9^!LiE1Q** zoRnl28_er5@k>lywd(ANkj=h7^p9OUlq$(|P=&7{YeibF)2C}mGnO|U_$I*_(kZ)% zH^nne`pTD_yP31E%FOZVlkhI+H`FT8-z;3_rWk2kbA|C_apt=H>YQ^zcRre|6f}vg zYrWy*ycZ>I|1?+<8M7}$iyU>#30>wJE@)NT-{14ddrBrlk)>4Yvs;?)7Dq4BS+VY{ znB>Z@eQMpaUKY>pQacz}#~*Ye!n`3Rn&Zy%w;Y#jE=X?P`zD1Sbjqr;_{A4L4D52& zJ#aca<Jr%q!tGgqThg=^6|XqCR3~(`X;QM&xyC!Lndi2cZS7=OeZYt#bD>7F-NaI# zG9iik4y+sIv@0=jExRe@vibJj@(W_0Yw{;sXt11maNc)M@2-CCr23rQ7b`Z4bNqPP zu(0NNd5M?rJX7Vrj2#V8zRtcgE<4Xrm*kui>OMz>J*2(Upd@!jz%R}(t-f6o=6N`u zj+y>k_wbf?3yylTHOm;crNsO@u+M<)+a0&mkL+?+jgH^E_|Rse5L-*Z3GUmx*(Xd4 z++?TuGkiI=PdG(&xuA-DcfVky*!|z{!k+AxIenIS)ze8aGdy0;IugI@xQCKlbZe`v z%&r%4A8Zq(q<E*S6}sA&ZEc_zW48RVLoQQVU(>YD^A6nLwJz|``oc7!D@#>t&XzoL z)&R>p8*;9HZ@KbAVcG3Z7lJY$TR84=+O=3qtW82{*HO13p~#z8Z09dOvTIM4Y4r*7 znWi}$lSH2P$Q?QH(%7r){;}&SElZu3%V<A36Q%ayaKU7b?z^vlC~q<UX<pEiyYpME zMUj$9qxP>gXEgHWSx?^D8FZ<77Q-{0!dD&|v3ijeb1MvvuiBmUhb`r2@&*1<^OF}d zCkI)|Kk*6S-e1AzJ$2n0ziWA~I&PV)zq!z;!0>s1;YK;uhOXED^Y&@nS{)^R=!-`f z=h8;oKGpmvtEM?+Q{GP3R$m@*(qu|To%3R4fkQ|7Gd>=Sw@;p^a#ua)l+T1MQ!7q- zwVdAc%7NqAw0Zp-t8$)e%&amvc~0ZDw9iDrf+G3f%`ZMz3EVis7_=b3bh^aMQ2V*t zr>J%K{?glf=(qQw9*@VD%nB0Q6ZcFxSZeUA^2SUFh3-S2&V8J~f74pv*@9N3NPpFq zvz8}g)kF@>?h8DAKto_gO<dq>@xxaSf1lN7>~g}<Ls`f;hBdt=M>gcin^g-U-g9kn z*0ww*?%S&|@82Z0!asRj`#o1FW=d`r3)sMMbKUvCUZn%?lz&DtUFa;^X?tdt!R5|7 zcO4$C{^_mvWn$|!zw>&t+j~s~_~t7~>3=q{RX??OgXL}BiBpY}jg=iA&tVSQDVM|j zxb5V20psl_WZ6oy9Pe%LeCRW&$J!%y^%-M6%Lkv_)yic$DjBC~zY&;zymEKqrYs%T z0|(wDq-}M%9cFPnSt!_Gy7%Fg7JrY1gmErAJK5ONT_de2b7#jgXZvjoo8JC#O#PH` z<J2^>J?n~er#m0L*COfJc1>39wYk#$Il?988$T!8POCW~Im0cXfzx_L(2s2Qd4-mh z7Cd%}%H6SAw<LM9_ua^B6pOX1D$<+mBCGyw+m@&;yJP>zJ=k^i$s3uBRo;`DKA+jZ z)gQ#cJnedTl=|Xm9_K5&7dJ+!saW`NTEsnH{cyfYcBaCV&)g@bSVx|;?s_i$Dbs4P zXU&<G+2!#D)0ceI3XtEf$Gdb2vspTa;EXrxZ>pAvzW&3z_tc5V$kf>LmeIR-&fPft zwc7GV=%&k?-Tj(Awx}+7X?iVq+5;sn%X`lx7M@vGbV%yrYK1$_3srePC9m7<zuq8N zd+SQ(o&McnJ2xNO*LC9+-|A~qpC?6e@@@OU!cihq(jLHltS#kOyqRD0`?lb#1&jSk z=B9<GbU5BhJh80w_MO_w6O*E6t#8?Ou!5&+I^W%rEsXVke>t>Y$A(;*o;zRswuafS zEj8|?9xH;>@-%ienWq)*ddjmjXU1B+O5L`O>Dy`}HeWs>bz0IxJGZni@7|<9VI6^5 z>j!`DeVJ8wNbZ)mz{67k+<|kXU(Z>mZ?Uj(n#(ahUA`%I1k#Q5_u6LgUs%6d*Wt^t zQz`;Kg$t$@w{WlaTv!!!vdVtxY^StBgZxHE;X@INJe_qI*(UTKbZn9B;gZj~GR=8< zq{$;cm$d(mXX8xo1?_&iVpC1mzEx4~ejeV*JkMT4zCU=6{a5eTl*_Rh{Wm8%&XbIB zN<S)8$gJy@te>kYSD@K_Xa{%d8)mDSYCE;gM7`+qH#o`mE7Gxb?bYb8&0Ev<uG>Cq z?U#f-`Ei&BtsbsFhjh>?w`?EBPm?7q+kE(CCsoHLODoLVxP?DK+p>$@OIS0rQQ9Yr z*P&#W#bTz#rCow8pi@>~BsFzG&se<<J!4gSALNYHZ6W>#4;g=&-3~fqb<gdGpd(hZ zo6G;l-kCZ1X9wTgTNlr%S2KUQ$>(PCp}n#Bw2G1k{|?b^A^YRA4&C5S$gh-WdfSoW zv07SPg;(^Ktov^N2+NZ{xNemiFzamc>#d3Vw~*`oRRM(w$;Y4V-nlwVSXY4ijBCGt zRN9OWCsft#9=v0JEE7KOR%G9Y2=>EIpLFhf{pI72WxZW}lUAy1GccNWZ06^cXV^sE zHSYAA&6Di3t^ePeGymp0DIW4zK4DSzr|0tCzgwhSmd97Eh;0{`I8P;sSGwQ8`%R^y zh-j7wyKJj-<3>x%K+TeZId{68oBO{T6daCW3ZKPVd+-APoqe1Z$Jo}+=vmuzQ909{ zJ+O93>b;Kn7V~6koc}$qoN+2RIbCF$2jBJQoEN@>4q1Knb65UBiI43P7a|p<Gh1X| z-R!gGia32MFaOEWWAd6mH7927sBW8lXo<M8l2d}slk^ycrLspfBdRu^e0R@M<jaN0 zi75+r?Tup(KAH9@{YHN%xBpV<OEuxMm<(4PZ|gFAdGG!MAuUZcwcKz1clpAv)jmJ= z{pM<ax!-pl$lMBM_`O}-V_)ctIeQgl;}6Z1*%Te`_1y5|Mu`fEU(-sjd@|Uv%75DZ zvZdxz-t5v9Y2I<@fv)eVyxvDTPwwP~MKJ99`o8e#DLp3NfJOT+I^4<cI`H|<oLINL z*CyWX-4dxNzxY7^QQL?9(-QVr&%Elg(fv|_`cC$y?AYbUf3dLgo|aq`tM9yf-HU^p zCS@P;S~7d{)D(8+NgY{+i&xGORbH&nQNH<UALk1J!3(jxdn^M^zMH(|_@x)8%1U}f zx-!>`2Y+V^mwvYWm~MTNiC_PPz7yQ~=XB%k?c24^?w424o?y;taxkLJeWv1-AAJFP z=kM6wop`pGX~l^TYnNQt+`H#v;x#tsJqtY7-O}E;yxngR&qp040jD)bZb`H%B(SZi z{jp<OY|J8e@2sC2lfpjlV?Vq(XzMBWecRVP?k`Gd&3a>RW-)W-9g&B;s@ad{a&qbK ztTH|`ea;P@IjbK^e7USH`@_Lc-i_mlI75N+9gAm|itH8EPF=zGIaPySZb9mn!n52_ zCPqsZ={}lZ)z#sVET+_8>M+Z!EMWSYg?c`2Rwoo}t;A39&GnJ#x7rYTEH-0%{2P_S zo2MHrQk}o@kwPPX@{gJ=b^-er$gj-k?%#P;^>b<O)`Cg8+G!8=haO|Ha;tl+ucone zoy)<aSuuIeD^sUE_AF=VtT?+;GWg;Lp`W>*9^K-+zG;?0nMvlXvtE`KZCkHpuHMKQ zdy3WYd{@>aUiIV(@5+1MS>}eCzgRMH$AL9dFHJhFvem$A!fsU;CRNjuCt_IcT`|{f z_GEo^!P`A-`JSSWzqHPL+a5)~V-r^#?q16syR@XEDDYldHA`9lYkto!pJMH<9c=u1 z=+>53dJ86MRKMhJbmtSul{}Dl&CX&%WXOhnej&|r-<hio9P+Hfc-WSvz7jPHzkAa& z#8UcFsbxT8Kj%TGbc5iAlLldu&8%l1b<JNHBFsFCS>AcagY8p_q>`s{F3nTAoub3H zX8$z~?#E^ueJg)od!{P;sApC#w`@S{gw0yw*`IrxmveYK3Om~_(hdyR&|$#!KkD|{ zeK$P!%UW$Odv<e2w_KOsFUy|O1#>icK40YXUiw~5W%JZ?RYvZMA6vbVUoLL)-o9XN z|I>DVnTQWZ%GsvE4@+Dc%xX#UVTo&dwJ!Xyey~p<?3{#3l-A$R^Zy_D$|~_?>3`;C zx0L%b2PW`o=CED=ZuDQkYns;+30A*7uGgk7;k;U)c>nLr**_|ul;jyMHP)K?V@7uA z?#W$WU1r@1+bVp|^D;+y%Za|=)3>z}k6WmoJF<tNOU_8y!{OuP%=UC6rIG+M`PSTf zn`1op^{r3P@SL|=a_yAFcYL)KvC-io{SQ(@w`CXIt8$$`Ej0S|&wo*>=Klrwk2*Gn zM`zzYc#G{E>+HM&w%a;;v~PYgOFz(bapkG)C6$b-?NcW9Tz9tKwQ_Ci6EW9E>p~w% zFVD^vZSeUPzjOOCy^d?wC)bDd{^47?Z?oC|Rp;c_6Fd{qa(d+Te-9Jue|}<oHc9An z*E`+)k2jw?eQYl0*14yyl=!Jtckey2!s*(w>E?QmRy_UeT$%PuL*6CsH}YACrIj32 zZ=Jag{5sw*y?Mg(hrcEyelfbt=w7hasQyxJ)>M|5ux@pquPf?qRv2u^<7a#CdUVsi z&2_EkpPf9Jw*7ZN!ZF47TTdsb$n{LERQG?&t8?+lu{j&mCfXRbuwL3%c*AC;yuF0n zJ*AM_H|u_UxcW`YwMXpYju{URHO7~1ap+om?8VOhiPz(MFDCb2JbuH^!sN=PTk1c3 z_HUURpyoPf5C4i|W*j%wMOR0y&C~E+b?TXfc5&7GOD}%ya`|I7J=A#VbI=Z1w{B&Z zjFhSspU-7y|Np?#{G#{q#iOf3E8LFh-PovWR+z~7jcrAPHv4P)wbv6~<<5J&Oo(;O zG_$qBr@EA!*SmawpOCO4^xuWA)(;PhsYNdqP{>>Kok^ej)~`O@1->~+!DbsaE}X4* z)ZDWbcJAR6alXj^k%wmYemUm9FoP$PUHw?{<l8DGvyS*rVp-TRZ(XXT`4!QXQ+}o2 z$q3a^dZG5B+EC7UxwnUjh03xc<{PYBjT=B`9zJ=!b;ZH?4M$26wA?>ljE#scS$cKa zo|f)s=N6uMFTONLHhD4g#iz1ST~{~%S=cuv>+#DK7sa+oUO2ezO^)cjxxzo>EK}co zP^!xDV0(U`-YJi7x5)EL(mQvo^;7hi@it-VKeMK-i#ZIcJh#;TH(OF7B4c=OI%B2l z9j>Ie^FuRU&o?p<zP7xgPcv81g?$T$Zmdbv^y^<gKP!Fw<;9;hl~s|^v$uYT?$(ao zDkZ-B>tr^4hR4sJe+_&6_vTSS{a=rsuRZqgD(e)tz(4<QS6_O!tyg9L=kNqe{r)R@ zu7d3PdmrYAc+RUZvs1c#@=Vs(9}Y}Y44#=XWVk<m{(oOBg95{&3==8e3we8vm=&5x zg<dT!3pJGvUtd?-?f3uBGueyP&FuH~?PTWv!tShEVq`Y+@7aqdS)SKdSAPF@_^STB z+xmMl>neVqp1pcD(*uX6X9`<yaBoT9FKKYox*%_9zM}HcxzZETnSup!0<LHLQU9`6 zch{xAS8p{Gn!ohBf7Ykk|KYl)M@~quJ9+k(lPrJL;&+pGN6q_t^Jeb<M_1K<eP;c` zcx-ParyK8d10{)IN#6FZNmeUX{8xYVzw=l9qlB>k@9)m$FTcM1{jPWNd8J?fS8ci@ zedN`DUeQv8SN}g+H0?P3_UhTyx8I%0$eC|@z4rRuHPd(($iLlo_igdv;LGd6{@+T9 zFM09go};W)=j!e9bLROa8uq*_seiTML5gy-5dXTLf@^;on{2nU>NNj(^trJ9F6o4o z2OVNwXa!00J@4|9ey3KJc3*Gp#A_}~A1<ok`BVJr=Y{0-=D4jJKks`aYrXgS_hj)s zFKXZDT)8+u`FYNlTMW(%wLNzB&2@gOyJ-1>PiL37&#`d%o4)Wr=U;yr(@Xy+sc2XK z<^R|7s=jNDX4i%P6*5UHxqs?E*+1dG(?9N?^%GM~!k_<t`Sq9Ao&UyfPCNbkY>_0L zbFuGM0=Mox<JY;9M6+E_)GPjP{Ud(7T=~Cp-AAsc_4yK;|1+`+{GD&{C|;)mB>Df{ z)BhPJ?|MZP|L+g~DbM@YIQGBfr}&8nliWYWhfaAI;P|(`_5b|yh3^ypENon*=(9v^ zSx4I8_x#L$imx)v4n37WaP!ENn`w_1Pj}$W+nN7ho%;*7{Pi`)it~5ftjMVNo87d0 zpU1zJNxyB%RIacK-J5Xh!$!?oR^`L1c2_J(nC!jzVfgRa4dM&$Zhx?8yEvPjDSy%8 z!}ll7efhRtp>opp|16Pp+w05gvbNW!e_WIIL4{?;bd|SDpE6x|a3{}$Db7XU-8Vjl z$JP@QRw<S!|9szmZ^?uE^MX(Q@2&AZ==vn7DdobB`ES=OE$sWf|IPDF^^&(HJjpZj z{<5p_&f9~}KLi^`c%|kZkbIy2dQtY}sV`so8tgm&sO;ON%8VbOq7Rh6RNgu3wq%;q z&VzfhxX!YzTOc6U6Uy6~c36ArnSwXV1eQ7dUw-+2=-Yt*;Vu8pyZ)D#{q=uJT>Tne z_Cs7h?tQ7>bG>|DMauqv#((RhYW{Cuw!(Jd|Kf}P&mKRnS@O-!B<62nSBUBY(FO7= zlFzxkGG$R0_!R%+f6IS=nfuLu(&wF<sQ#bb;hFp2`dMo_{^;u+pa1W1((@bQ#@V%r z>+CCAZ-lI$G4tsT>+4ZgcYhu;`<s;3;UQ(Ae@6d(OKe-6A<y|@i;sWW{=J;=yWrBE zt|^zew_f==+rV{B+oUa<r%yKGld6|_lQzd?PS-)#^O_uK?R%0pyvlrYP_xL&&nTwv z-`}{kR_R6Znu0>@p-*(yNI$<WU1#z5P;2X7{WtZA|G9tmKe6|FvnJ&)|L?y&f9?0q z_`Ckw!+-bpP56K4pYWggv;Qvtw&mdl{WtsH&-s5`OC%)k|H)NdpX{rf9sPq-cl>*} zzvb`o#~*sE5^8n|yytztUH;<yb!MOY|9MJqx=AZ1ymfrE>qcbj#{b7_zO8mNm#Vnj z+TuUs{Xvf0`l#3+ucg8m_vD!DiL(is;g_iEmHz6@v*ODpbG9YxiKqWw_w>Yqo|>TV zqCXCAxt+d4|Mj28smizB6=?e&+MIp(qk4SQw2Ql@RLQQ~x*_Ly>oxr!y3O|!Uj6fb z`Z_XYgZ)BrlSON99~6=E-G0}p)M-!ujt2eA(k1m)2b|b~U#v50+c`&S_tkB@rz02Z zss~48q%8U}<%hSxucWm{6XvTJM>+giBh41MqO3gI@Xgn+56`?fo8EQN{OUrx;Ivtn z-mh$)c<1ueupQr0S6^PV;#cSC4VA2Y%KgqgrG@_&->!fAB~`knUj0Xlv4iY2r&)`b z=3icEGSlPiOOY9mUccPZ87Mx1%jUM7u#(S<dZ+)wzvf%K__Du1cU9I``$$Qi--dtl zHL{%-{+D(Be>#}K_k@mA&aOp2RIW{oTNjYL`oS6hZK-;DF2A_X{`ige>ejfflGf}6 z@^^2Uz4`c~>hZmQiH++W_8Xf9epw}bBjo&b=jM|FS&JOz-d-=2p0#e<J7eo!llV@a zdvCKReCNNSmGmY0mgTBjf1O=|Pd9NT^Zxzkz}h=0z<Ph6_l4C@MRmT^e4ecGeb=Tt zp`X3~y01QWc*2CG(wkS_nSJWhln0g7+uQy=JR#NoAnV1kBk@hK|K)QNxH4ot-+G){ zz^umqkNukm|6lPtJpX@uPs=SUe`m9ww~6)op_KhUPAr(g6vnS3GFSLpqDxSZjLd(N zwBj8fe`hAYNuR{upnkV<mWjA>YL0|*t;wY)6O+F1)Gpx_d$k~c#<DdUpAJo6XHJkf zd8<a{yj%B2C&};P+?5M>wr;%n-gNQlXA8env6<B0ukMIw-7~G?j@0R>#~Un7^4r9> z_HK<?vE$q4r;MTjji<72Rw~r;wYSgRwn^dmEK9aE8+>oATP4A*bM*g-nfEUrD-2}U zoKZW$SWD!oaAe@tx^K08sWXc2&T~6v_x+sN$$75tgRaJ%TfM{ZoxpXy6B}|r3vLUo zWt-rTa&@a2YlW`vZ2g1x4*t4W?rrXHaKVAa)ApG>XLu?7d*knvvkI)c88m;CT#sfn z|B*gX^QPF31)4#!A0NoM><VBj&A;%J>9MNUI;P8wzOM@|KmWluWsmZO7R{sk&NqFz zTE@x29Q3o&-!`1B;h6r^gQ@G?@49NfxYoQs=hB~t^3UzhoUb=nB44-f&%s@P9?E{b z;IRL{vV_>T_G7&dm&~u<7qeDRw!JE0fw#}Z=A}OM7X6(*iR%y7*fITgt7+M$8!VS9 zyJ^{GKHFYRp3ENG%#F)dd};|<^;FDK_3O9)bJ=89E`7;iwBlc?zgWnZy8(f5*_XY1 z4l->!+d4-~ASL<d*6+{QMZTXobKqswW2Rkq*Yf`RrqC35L+OgvRt{sw7c<)f{ZnS; zR<uYg@A%*OV?Kz8xA`aj$};%lzx9k8?z4aV8}A%?<^S5+Kj-yd8u2b?|GP|ImpRnY zlVjJ#Hm<+39A#fNGe2%^U+7%%?^J1S_BQ7G7dhwcaIy`y7WyZ>EiO&a>y3xL**Bh2 zp09QLFHH~dHs^i+{pz{ernfVnWiB|;YyT@gK;e%>$)l(8wO``TYW$F>@w|ECv7Acg z0go<~UmNn}HL_0H89rgoJvd$MO_%erOULFp2(^}FWt#-J7aw?a?)Ro^_Se&%y@(0- z<^7XwA=m#@a)UA(L*ItGVsa9n=k4JwuX6Y|`9)R+b5P4+owdi+w@*!x&9!UZ8uF3F z+b6&8_Qq?HO_h0dl7GcFwRP71X|T0@`PyB$Ju+%WtxAojiOl6gyOYz4m0yOX@49<- z+BKV_nvxPGlWTc21&dzFCE0M=FZdE(n|W)O%IVmhtCuAxn$@3wKDWr2>&})v2YgT2 zT3&4Q;8`Ks&A4^b{F9x3L)>{jI{0hZrvHA|+~Kd?@grpouTV>ivXyQ_`qI@YcV4<D zMyUkOSW|c?aC(^671hT-3#PO_VCFtDcM;=^O@^XQ_0jSdu5$e0{deg`^&O4!$pSmx z{!X|x-S%nFHH|;B3g>6PR?u+S7;O8dELXLqS);9orRkiU(0u#sl=(ORS3OvD#_)cn z)0+EFt7MqJq`xjdDRuW@me18E6&Zz7ez{-0CH-_uMMmzH;MFb;lQXhfqN1j?xh<KS zdQ)>rl;lRYY0G<4Pn=#9CDF(hHuu}q)s;)V%ZqxuU2ZE~-V%`!nQF3z>DJZ8G*=yt z^A#M2x)v4u`;a?xrpOcdvhD*bKCOtBIwABp=fh;5N4+x`dDebae{p1v)H^lFF80lR zFD24@Z){2OyVz3s^law#W2_x(>Su3tSbIixE4yE0(VL`Bg%H<;T3n%lE4rLQMOG<k zNA6_ZGUJiSN$m#{^c|mD1W5@_Wjvn!>MY-+(hWCHEwSv*HRR@uvhgujW4WF9QPp+3 zL1~hYs=M{q`&V0?%T#QhMfUNub*<VLlV=yc?!3xljaBDXl)Unh<O%Ij_0R4)w(@1D zT3i_`=cX<BqLEv!c|W?v^z6{fM_On75_mth<VrnBxS!k0al<~c&vf&&uw&=A6B9YO zog*X;9TE_0+hD-a`lj!WWb7U{nP_3<ScPrh&s`Pz=K3MT{ZPN)>Eym^$NCObM@bhq z^Bxn*?cJm$V&0t`9;>qW8He&|FRd-dEFxsB|4me17T~|Za)m>`Np52158=z{$8rp% z_|KpHS5dQWX05DG&P8kaZ&N>JESXv5KTTQqcEiQo#0(B+X9=l87X;eeCdKS^u=y5p zhS%ig4u`hG{+&;mSE@F}UESK#=VGPTsbjxxs*u8Omz=38CBg42^qtk-oSAPcDRcUh zry`TD0HdU!fx$|rJr69di9|~&Y+X4cX_BN}X7AP$&unE?<%MTuSp@HVYnOlaq+RyT z>hkK+$;|y(m(M8$tUdQ;(Y8{HT$X(YPOK@h-{5X=@u<U!SuMeL1uxDK3{bN_sw}<4 z$AsTaVA=FnnF4m#_PsIrkyYBD-+G`Yzg*zWSCgCBhyG1$h|uS-x4CKH`}5c1S*uT` zNN*3<<k-yhRo!`?L&o9Ac@uR{PmPys{hznw&=rxV{d;G<GhRKfNJ8`c#YGmW6~8~0 zZINwn<M_V+<HDB3(!r4~g==(=sJ?U(-P5{zyKZw&-<GXARRlAeI{MzN6+U*nQ%c@? z-8T2VN~!l}y_~p7W9Lk{-T7BPJ@B(QzS$-y<HZTXoB2-1Z9=t@1TH>ZoBVEHsOaaA z%#OKTm1jCW2zqpw<o&7L;n2{ODP~|jBapdH-@EC-`oC(2`%Z>6FA{NV35m<nuD)~4 zqj;^r%j1^=gE`vf_eHUuT+x)G72;v+(bn!1#d<KKDM9Os-R26PueZ{BU#R*ru9bLw zJixU7LWPgaRtuN^<`@5;egFTOe1A%0s4Ixa+aaf6B{nT{`g?}ycmMlJYW!7v^MBb1 z=e75z|4#VJAL=Uj_x`JU|9h9Lj7~ehx#5`A!g}ZOSod9SAKzVwJRPexXWy}xFJ)i; zpD=Gy!NY`(zn#*TXWkae`demE^yHELuV01jzs@}Tek8w+kw^2&biTR!ji(!g$^YBa z9Q=Ys>h}X7wgV3z{J6^5eE7ysP*doT|6XS;gT%wTE57flDBoT2efMqqg1#@i(~p~m z-}AKDf8<zsocHbv=95gfe*QM~%}hb-cJ+HRHqIA(b!PH)qvaia|4$m(AO3x1&%bXL zPh}gkf91UT#`5-sLdCI4qqm-~|FXX<-*6@B>kS8{4{w-0NDC~Rx}V4L=VI>Vzi*$u zeZTm5NBHL>@z?I&zFygJznJ;PIn7RQ2c{saht`aF45nTStE1BSE9bM!40$5jk^Au9 zw;!v_b*$#wKVT~CKhZH`;Q>WPtq-N^ZyfBtAOEC3BbiSmYHGrln(FTjg$Fr#tS_n8 z?qps+tvWU`aI2lc$LDML>Nr~xwVuC;+h0@vyYJKmx01;x%6FUR$30^@u)0&Iaix0x z1M{!e|77Jt)|XbN-TGGS{_FO!jbE162qgbMs?s^T{C?e!m(!=euiLj*arRS{34BXe zO`J1V^zP#w>ix6AUGDjR*|5hoC}d*C!ym6L0{b&3uYIhd=C7rDq9W#i%p}oSW`e~Y zPuMkQ1y~nuonAaG^pw^qgWeSvP6Qa-KH7S09{bW2N8ZNBg)Do1W?@oUi^+VB_Lb$Y z{KWb-kETa_oHgmoEUPq0xiHST)BSw8F8&D=3NTs`CmN!^!mj((%gJd;A?GdQ)taA~ zI(}|?wmD<oz5@=vnfvsA>{;+LXO{nm^V0$gcc{f}UjBPdN|xhZ)5USOY%iUA_t8*l zx6Q%=#`zi!ZPk)ByuI}^FBtQ=t1tHv+`8hx>zG6TTg%$sPQSY3>yus4&p$m%N<Oc< zc=twm{REBwGHsvFKb?4q{l66d>7P6E->r+=^ZfPWb9FZ(4>HA6SN#2cF3#_S!?9g! z?kM`_n0KaK(s?ZB^WexI?gfE|_Fvk&c+CaJ{M!e#auxk;vpFl{16;%=O?+Zls#WNv z=wHdHu}`Qg%;loXwV$omZXUm;&9P41{W90lbEorcpG$=%i%fbhFgaalO1j+CdJPSc z?b9B<miXR~eE++gQq?hM_bMmrgL6+#SM!;Fch_0(l`HeN+nMieIB&Lp+NxsdMQ_U_ zO_lXdPdg@Zu5YK$?@NCKx5*p+cR29j=~m-<qi`#s8)y5j{1KdFu!pbvuf;O%^Nf2B z*E88{xO3*KYR=D%{yBkqYrZC3_E|RT*_|T6Gs4FiX6y-g;61P8`P#h0EAKLYG2N>t z`9V@c<XrWuol5(sPdy#?KS;WyG3w*~%=gV(Smzh_u>RohlTF(BqN%=)zx2ngEhm@i ztW;Z|D!jm$$7hS)8vkcCo1+**lvh-3w-n#}oO|u<-4V0;e+j)xEnvIn)U@8e=H&DT z*>&ySOO^LB8Em>H=(Cq~!L0?sd^^6M;`DzJART<()KzZIYTKvpH5sPN>VF`7ZT(*H z_xE>P2*38}XK97g+4TaE>p0C5UY&l;>mWU0`U4}ETi1n7ORb%;$90d%zwVD#aZ)O4 z|2#S!sUp|;M}Fe};K_gFw=U;xuXTO$zvIvRmFm@})&D<v_W#S2djYLK?e`vJSaHB8 z=700Q0)<cYuO|OLtaksoxKFU7RY$z&WCKZ-ESY^?7OYj1f5+7RoZV8!bx(vz`_;lJ zTu<u*=E<^z{?;+pVBpEVecU<9`pw_{`|SR|lz;A^+HSTo_w8Xum8hC+%}TExR~i+1 ziAn_RI+pBJH#MN3{YkIxLf;C$AA5?Kze#`5f1S-dk2!7gmPn6X^}7{o@7<7BEokPB zW6-fa?6iHi|E_NT#M@4rWH>d>eoM=a-j%I4{~>F^`plB(R^Im^zdSb`+Bsp$g`-Cm zztk`M-~Fe4;eYuT{~i1{%%7R@zw+1qRbNwer~S7N*gEgR|Ne#lmA}ersqMSOXDY-# zzij^YYrih;FW6d_7N^T_cL!_MY`G=NoRuH8u07t~Ia4g5=kBGq9{Zg(9hLm}<9w}e z<$I-6w;CH}yLpQJ$LiRpxEW94{eP;Ybj8ou=7k3@KdbupCieWP$;X9NH+P+}KV$jB zW=}$7_J4^r8yKvAWEcHknku>0TOuj%=R%DSo44M`iHf(LR36$HJNIST>+G<Cv)@C1 zAO60G`NT~A=wna5rsd9?`r^RDH)(w@uWdb9%Ci5X*7j(LZ1xXDYwBmSRO>Ecmnyte zQ~vw(+ppD}-!^?q*WA0K`m<M&*#RMj_0h8~y|=%A?`~7{lIiQ0Zo3nB&g(5-*Q7Uv zznfW4tk3+HAoh#f{hnmf9J^k<uD{9O3%m~Q*ZSb!Qs33G$@!1``kz5u-%J~8mEHvB z*xMZWr(T<RmFsshNZ@!#oYxm---B~@Z+-OdQ$PU2%rE{M^D@3RB%A(7@n*~1SGnxO z8m^^Vc7}6vcYN^52{@AWK<}iZ)s#cKwY+mzsJO5cKHl}^qk-uGgYeSXJ6GSIb1pmD zO6`l7=)&zqZLE8av$<&)Ead%`@9=vT^YP&C*ZH@X|M%EGdxGYJO#7GTE-y)a@%Lzw z!Hn#xdJBn_p90LL9!<(SCmNw}$An!u*Wi`prkla+tE|kYTud;0$J=%+VNJ#^!&5V? z%YIk9zsPng;Pu%rjcYW#>UCCco_S}!W^?PBRfivLFqg@G9JxQA@7Jk}l)hudrG~RI zKR235yna@*EOpQEqGi!LGOspn`Q;REmG*AVwcS_GY?=64J^9WDv0v@Rw;I;!%j>b7 z*}OSnhuOuA&4xTP<0Rd^J2XFU-uzkN#E}bWYynl)W^6J0cIK)qxcZ7Qic8s$ElN%- zN$|oqPA$<QM%`Xkv1VQM&#MKVZVKCzc`*9ao{#Za8}F}q_V!Gb26v@f^o>31dziN8 zMD_DqU2{<0sJ3Iau+AG!JvP?<lJLBl>qKvg$n<nIg|Qa)nX2z@nw=PUVv|Nn)RA27 zvOP0?mR<0=S*&}8E6&Z8PvoU<r-e&@j`xR6DkmOzMm|>JIj<}&u;&cBQN<+*-o%Da zx7G(XI80rvSJ7oF(CFH<i=%G(?rm&5e+pm3l=@k=%*nDTHe_n3*Lk4kUbckcUd^H< z4V@M@zTa$}ZMuA$<^3BBIuE>+BKGA!%+I#nux$B{*;>oh6AtNc6u;PgZZh9&m!Ip8 z><^yb+`R2OgXu#9zaEETjV)^blI5Li=A3AiaH#yM&+=+<_2kKnYp-%#Q2la9U_t-8 zXg{_uJKP%d78|rF*nZ!;BJb^i*qMiCvfc67b58Eg^M>9XYp!~?a&QM|o^W>#SibD* z_s`iDkqh)K*72rZz8UT&B<|eUE!Y0=tbf=f#+i#Z=&)s{DHZdV%Qa1AdlmI|L0nwj zyM^bRyDmA@##pZ~7Fbrjp;&mO$cFV{%YqK>Taoftj!UWIPyNLI`k(&0{;Rk7f8XtG zLV=jmO)ZAXulr4O=l$njw@T^%{N#WCk9mLm?mBBe+xz8n%O}V&{9~E4&ck%W60i7| zckeaEl)b$x6PD?>IWjrRCFs=IxNq_|is}~La=&Y8|N8v%2dC7t`Cgy=#c7_MynExd zyhjrHvO6>vyuJIXdIx9T*UNQ6`L5?8-@6^3^FHVPi9dqrYhJwjvobrPc>CVM1Apuy zzop%sxR1rz^nKp_z4PPt)>`hZt^NG%?CI<852xl#tb4TZ)ZJRP{FkpjuY1>~b>kiT zj}Py@z59A~b+pBSq|Kgtp1-}iyL|utox2;qpDD5aIIn#Bb@_K6Gz!kQmiMQhS6_7d z_qwSwAFfMi_?6QkAlPUw#nkWO#C4CEBTR_XaY3zCr^rMOtBA=w?_3%Er!wBHdp|30 zjm{O$Q~qJu2kW<LMTb@NJlOig;OJ*1=B!6rr-MK2Z+1#|`Y--#|Aqgp|KzX#zql+! z@9X|Hhqv;_Ue{|nFZ_Rf<-hP;uG+orB{vU-Ci;GPEcMdKA!zrx#FP3l9B-;N-&uR@ z!}|+sf2iECzsq^5{o$h62WBtyS^Sw-lzh-mTrYmQaBby>l!CTJvm*9XMeNxn@kT_@ za$-=N&gu?{S!RiXhU<m3qQ0#3QtA7*FtN~i*%4pc9XA+jf{nLN*4)R>KeMd--?h>P zzbkLV%D-jqdb$4D%<F+hOOroW=6;%yaWXS(Hh+?3@cP-kox8hJ{F1Dy)mTH6RevX@ zSG`@RAh`5ST7KEOt#xziOb))Tu@=%>)DV)Wzb@|>hk-WZ*5#*vEwf!ctK~*mb#}D& z-8#4Y{rUU$WZIP|d~S=WWXf(~HcO7`&#sW2Kf`|J=Y9=$#kZ~#W*(H=_d-7RQ;|r; z&PL4%cl`oCFnwNJ5;yrUr%rQ0)~|DSedK*NJQ6Tmo~+6_S+Qk;eaAvO*Gije$18td zK8=sxSLgrlosE`PMcUqIy}x%kEmYO!#0M2F6m1E(<kfBT+^aiLQKDa`ZE4glmfQNv z+MPsWmAY*MH+SaS>u&Q_Rl3(7oqut8!>no1HgdcdE;80~Ikl+u#kLyG)CzGFUAm$v zDgG*FK&#q{MIxaAZ+>?Lv6f7qozWr{cr+rw)pAuwm5j#BqZ_$?7%uJHBAapiXpZNf zOV_?O-+p&NTz>P@urmc3k?UFN{T9~z*z+!0t|dZT`{d`X6D{v(?8$58DJ%Ma`=wa; z!>4`8W{G^QQ5<`Vbz)0g|CIF1DLq=Fd*@D>YHhg*pG}m$+*IxLQ|#Bz30^-Zy?<(T z|E=TGU%seLP;HtyedWRlr*_Vay!lbbYo(9Ul+Ki*!ihdRespm@5neO*Ot6&kjJ&U# zb~I?DX>@XPB$*Y>m?B#=_0ZE6gI1npA_q-`cJwglsn&aU#=Bjb9`@rI+szj)?^}aB zm(F%6KQ=GyM#lr?aQ>Tz*MACm;q*Xf`u{!bZ!fNDUBVc)nms`?=59jh%$?Q?XE2KH z?-f}WYA*l9=0>7G3Zo5k!Jli@h8%Lb%OV7}<jgq2=vDEda}mcyKT*ZhncT}8IMr)h zew1>qnAR6uvtLy7<nIS*Cq5OHnu>mZ;kmTaP`WhuN{EjF+rL!N$r@dmp{_z*tKvhC z3iQftotDRA>r?h*W9<K@TyNSQ#Bl#EKC@O%+;oX|<f7yHaWgs7*SZxfTeDZR*?3iB zi{Dk=YmcQ4&f4}UjN`^u?Zpzzb)}&qj~yN?+L>7Jc~Y;7z%vb*ZpK+NJJz_rJoVVq zOKZdQ>C;PB#8ldB^kn_=>5y@Z<HUZBr3`;|lr=1$xMZU3p`2J_k?5AmiDyr)uVd)q zSv*ze2FHAMr~Bnve{vs3{1X1LW7<Cs|BTuX=iQ5WOPDJpIYKsn{i-AGXzC%5tWzOr z*?m)af?TJlN@L-+DVi77bA4I9cz@0|@$k=cnSRc4sI&TXTCJ4zM0{S_R_DDsH|)f> z)n9p|du)gQ**i(?j>jsN^wzxF##YW-9q!Hgn?cAua06$%TeZ*M8xI@9>N-O5qJF;H zGNtI7$9jp6o+r`|U*qp=TWq%9S~vAyw5l|hw!+Mu9TAMXFB>uK3thBugWROfw&t%J ztaTqdb}YB~c;w)#8?uLAtYG^b^TFSTb&HvIin5OUY}IP{H-=}YF(;(trkysE=6iVR zbj|{X_ZxNFg9_3Q3-Nypoqnb>O6%<!!!WbuO{+KgwCcZ|eJK3q6z+Lm?EWQ7)#5iW zSPAc6GJVqN3U1lRW2T;6rkQ@GfkLGV*St*1yma)_qD<k1-BWfqnizf5dbXYWTKUbJ z*N(iGO*yk|S>nsPy?-k<vSz(GYT9u8G24XS(YNPBN1t~66263qr(Me9<QB2Hh1WiB zF!|52O;Uk9Rw84*ef_(-ZFW%;Y6PD=a4MazUpMvX#})Px+0I54g{>lO7cVh&ADOsq znXcvkTQ9e1u4TBrX||>4av`JFr*Ac!VvSQ5H_A-SYAsHE#S%7aUfA<+Nu8*Lnog51 zYpu|kS?QCx!(ry#W1dYb*)C4yki2GgC`k5es9)Ke7rhht&oqbVHkn9XS<dxpwer`g zy<b{Zyg#vV9?z}IEN`z){C0}1=g^$@EB@7gTQXgac|}y=<0ab-)zzZdoB4d+q^qyE zDr}OmddY*w`~FAoo|uznJz4+4mHj>2+q3I0O}{*K>%0@O3FZssf5}fa{Ik*M>(?dy z|5*<_|9iWzcV%MTERDMht7hz*K7G+kN7c?N-xZS5Uo2TVdx_%mSzgI2mmIJ^I8WrC zk4eokh2__?f)Adzm!8A-C#v><zgpC1t8jK*`Oi@i-T%%n`oH=9|H!wQhZExk|IT;) z|6Fs+G>NVC=U)C_b7<N54gZVt4PIof;<^6c;ab(A|DO$i*{hpZ>D{<txwF*x-O)K$ z7XDPfZ6wZ;JLSF2*Y{E$x$F5qv7cYnU*f%wH7H*)cZE#$hMwE%$8Q^?Zw+qKS9-JT z&(F-o>(``AJ^tP4$k)_AKGo}mT0|a9=Uekk)WYE0r1<G+UoZS}ax(sUM=`Sah?kX= zUhX^tX4$yMw>?j64v=qj{1sVm$Nv6*%30qf+J{aWywYshw>oxHw}4DbCI3QZ)=jH6 z{oL95iTCcm8(pF`)d~F`&*o}&6wHpk&!#xNS#%2jvQw|CRT$4~&f~MN_~W$M<5H4n zx5@;^rPq!|Hi~#SCF$K)vRpn>wkGm-LF;>0r_+JYx4mi#RuftEPuG4{{fpIaFTFWu zzFMsL+7XV`#&>5_ok-pi<R`WF%xj;}_Tp`;XV*G>Ir7rM*3hcs)zuQ02mU>|t8WUc zw|wjrbZRPfbLV}qWMA~I;OfVre&Qj4Gr7VoLtT0ct(qLxm#|2-OuE*l?#5QA&ugg| zlByGaJ+SYTg<kUW*eNgH9^3b=-oZ6E>4TBsj-6}@Hy5<@*T!*4s{Y)uU3=L)?TD%^ zb~CfruYRa~w`{HH^3>jKnxPKPUjB)@&S~Gg?dLdIHZ$>NpY%bI=Gup%GZ*aHShuBi zcTHf-rAIxF=iK(YVRf==@(~N}hmXHk+lJPBKgy-Q=TPNa^L4_9E>Cgf%kbt{dHErq zW{6Ylnub$)7f$<!W*%t?oKd69kiNtuNyygms^LVYS1TQi+|8XoZaV(obLB+M8`t+K zzu6q1X86tDXQ|J@+W*#vYC9K{a(%W}<ejYhkMql!RU1zDod2R3vSL=~gxUOeCto%D z`@Ad2z_qrgSzz{_cQ>`qPOX0PXAa}m`HQa2a`G_^jj0UsQ+$=Ug3<lN%PmtLgd|;J zF%(r-6XPm%@L9erc=@ED_z9OL{=DS=xpLo~7xMmdr~Ps|cJYFca<u*`)47v&9-lSG zL&xr(i=(@gE3ZeI<d5UFF8}tQ_%HtRztVsEdH+G9i=R*a4{uv_%l^dwaCW8t|0RC@ z*Wi@5y8CBN^^3ydpi@jgwpVOz=$^)T<5jjZ|D@mfCE@jnuRkUVb#WXy&0ezPKEu|s z2S0W%^$u`XnD<?NJ)eMX<CIu&hJCX(=rP=>=Dsd^pXEozLu(6Wx5)P|k{5mL^;&iy z`NM{DjqMhvRtojMmG@|tH2s*=dbjt@ZqDpe(G7Vz25NswVlG<!jr;KQ+vg=e-+h~Y z`}OxpsUG_8?(LZrc7DaODPMk{-(J3*VP5~G_$6`1=~G@#erMx5D>>KhO53RnwbQ>6 z?il~xWO$+NQ-z5{uSo8SZ{m9=+RRA}<#Op%lCBPl{pND;!m&$LHAT_sl2ea(a^DG$ z7nl7YARe$OQFo2k{bb1rjg8Z9@Wp)P@A<+0If?sdq*tC2$1}}8Ofr!Ht4-EV=cxN~ zwR!P|C(+mR`6u1_sNfmMeO-Ru)&-xR&Y5+J-Q!4%qNcmr6Rr~$YD#@;TOR8?ICa|G z>0{W=lE*4{k~XcC$=(@o`BSd<>8%y20Y{h~6s0OYe5LR};LVN>3#WiO;X`_itD<BV zcq#taGp9)Lcgq{$P46={@rTbnvFY`x>$1tane~sDKeEx<v+AVu^{G)+Jg?>|@|{@w zGDK`&_QlLs5)%6DrN5GXCUQO4@p{r6wh%|=){F+H-948Xrmvm!L}URcmxc?Ys%c`} z?hY;WhB-k&qKhXA8fD%VtgM}W^Ol+GZg2Hl-J#|>y=A}88LC`vJmxK{#j-ou{Jsja zmh@Sb9-g{co{l9emUr@TsXk<oJ)RwPUQFEc?W=C*DbxJf`GS9#U5nG$!@4wg@qV@M z;MEW<YIUd8%`k>iUOTsasdlP1oty9@u7ttzb0UNP4c|purpwG-c_y@PLh#!CG7IJZ zZETp`cSDe=;^Rvpre%*3w8O;TEuD4inal@Q_BtPy)8BRfRIX*;c15I~-_SJnUAmup zgzU)(g}cV{5A8ZPd7tU`lD7KgcI@oA?;mCzN}MCHG;Olh|FE`|5v#U%+46EPxL&!) zV4W+EuH>n#+yya8v)UHg+<$XTFZ-nRr7a7sRGMs4W>+5g9{GCuwfpDSKdfQ2FO892 zcjNH9H6@RD4>vYXIQ-$+QG1UCr~4a4n2fZ|ni$XZ&lF+b8D%_S^2aIpD$l)S9tpHt Z98u&e>~Kafo1cIDZyxgh6GH$i0{}N68*cyr diff --git a/dbrepo-search-service/test/test_opensearch_client.py b/dbrepo-search-service/test/test_opensearch_client.py index 51f3a9feaa..906aae0ccc 100644 --- a/dbrepo-search-service/test/test_opensearch_client.py +++ b/dbrepo-search-service/test/test_opensearch_client.py @@ -2,7 +2,8 @@ import datetime import unittest import opensearchpy -from dbrepo.api.dto import Database, User, UserAttributes, Container, Image, Table, Column, ColumnType, Constraints +from dbrepo.api.dto import Database, User, UserAttributes, Container, Image, Table, Column, ColumnType, Constraints, \ + PrimaryKey, TableMinimal, ColumnMinimal from app import app from clients.opensearch_client import OpenSearchClient @@ -56,7 +57,11 @@ class OpenSearchClientTest(unittest.TestCase): routing_key="dbrepo.test_tuw1.test_table", is_public=True, database_id=req.id, - constraints=Constraints(uniques=[], foreign_keys=[], checks=[], primary_key=["id"]), + constraints=Constraints(uniques=[], foreign_keys=[], checks=[], + primary_key=[PrimaryKey(id=1, + table=TableMinimal(id=1, database_id=1), + column=ColumnMinimal(id=1, table_id=1, + database_id=1))]), is_versioned=True, created_by="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", creator=User(id="c6b71ef5-2d2f-48b2-9d79-b8f23a3a0502", diff --git a/dbrepo-ui/components/database/DatabaseCreate.vue b/dbrepo-ui/components/database/DatabaseCreate.vue index a805af0e02..f7e2b00597 100644 --- a/dbrepo-ui/components/database/DatabaseCreate.vue +++ b/dbrepo-ui/components/database/DatabaseCreate.vue @@ -113,7 +113,8 @@ export default { this.loadingContainers = false }) .catch(({code}) => { - this.$toast.error(this.$t(code)) + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingContainers = false }) }, @@ -127,7 +128,8 @@ export default { this.loading = false }) .catch(({code}) => { - this.$toast.error(this.$t(code)) + const toast = useToastInstance() + toast.error(this.$t(code)) this.loading = false }) }, diff --git a/dbrepo-ui/components/dialogs/DropTable.vue b/dbrepo-ui/components/dialogs/DropTable.vue index 9421d1154b..1f62735de5 100644 --- a/dbrepo-ui/components/dialogs/DropTable.vue +++ b/dbrepo-ui/components/dialogs/DropTable.vue @@ -92,7 +92,8 @@ export default { .then(() => { console.info('Deleted table with id ', this.table.id) this.cacheStore.reloadDatabase() - this.$toast.success('Successfully deleted table with id ' + this.table.id) + const toast = useToastInstance() + toast.success('Successfully deleted table with id ' + this.table.id) this.$router.push(`/database/${this.$route.params.database_id}/table`) }) .finally(() => { diff --git a/dbrepo-ui/components/dialogs/EditAccess.vue b/dbrepo-ui/components/dialogs/EditAccess.vue index 90ac7e3169..8132adddf5 100644 --- a/dbrepo-ui/components/dialogs/EditAccess.vue +++ b/dbrepo-ui/components/dialogs/EditAccess.vue @@ -171,7 +171,8 @@ export default { const accessService = useAccessService() accessService.remove(this.$route.params.database_id, this.userId) .then(() => { - this.$toast.success(this.$t('notifications.access.revoked')) + const toast = useToastInstance() + toast.success(this.$t('notifications.access.revoked')) this.$emit('close-dialog', { success: true }) }) .finally(() => { @@ -182,7 +183,8 @@ export default { const accessService = useAccessService() accessService.modify(this.$route.params.database_id, this.userId, this.modify) .then(() => { - this.$toast.success(this.$t('notifications.access.modified')) + const toast = useToastInstance() + toast.success(this.$t('notifications.access.modified')) this.$emit('close-dialog', { success: true }) }) .finally(() => { @@ -193,7 +195,8 @@ export default { const accessService = useAccessService() accessService.create(this.$route.params.database_id, this.userId, this.modify) .then(() => { - this.$toast.success(this.$t('notifications.access.created')) + const toast = useToastInstance() + toast.success(this.$t('notifications.access.created')) this.$emit('close-dialog', { success: true }) }) .finally(() => { diff --git a/dbrepo-ui/components/dialogs/EditTuple.vue b/dbrepo-ui/components/dialogs/EditTuple.vue index 475e44328f..3f40a93175 100644 --- a/dbrepo-ui/components/dialogs/EditTuple.vue +++ b/dbrepo-ui/components/dialogs/EditTuple.vue @@ -280,12 +280,14 @@ export default { this.loading = true tupleService.update(this.$route.params.database_id, this.$route.params.table_id, { data: this.tuple, keys: constraints }) .then(() => { - this.$toast.success(this.$t('success.data.update')) + const toast = useToastInstance() + toast.success(this.$t('success.data.update')) this.$emit('close', { success: true }) this.loading = false }) .catch(({message}) => { - this.$toast.error(message) + const toast = useToastInstance() + toast.error(message) this.loading = false }) .finally(() => { @@ -308,12 +310,14 @@ export default { this.loading = true tupleService.create(this.$route.params.database_id, this.$route.params.table_id, { data: this.tuple }) .then(() => { - this.$toast.success(this.$t('success.data.add')) + const toast = useToastInstance() + toast.success(this.$t('success.data.add')) this.$emit('close', { success: true }) this.loading = false }) .catch(({message}) => { - this.$toast.error(message) + const toast = useToastInstance() + toast.error(message) this.loading = false }) .finally(() => { @@ -321,7 +325,8 @@ export default { }) }, onUpload ({column, s3key}) { - this.$toast.success(this.$t('success.upload.blob')) + const toast = useToastInstance() + toast.success(this.$t('success.upload.blob')) this.tuple[column.internal_name] = s3key } } diff --git a/dbrepo-ui/components/dialogs/Semantics.vue b/dbrepo-ui/components/dialogs/Semantics.vue index fd64efa9e7..a4a7104788 100644 --- a/dbrepo-ui/components/dialogs/Semantics.vue +++ b/dbrepo-ui/components/dialogs/Semantics.vue @@ -243,7 +243,8 @@ export default { this.recommendations = recommendations }) .catch((error) => { - this.$toast.error(this.$t('error.semantics.timeout')) + const toast = useToastInstance() + toast.error(this.$t('error.semantics.timeout')) }) .finally(() => { this.loadingSemantics = false diff --git a/dbrepo-ui/components/identifier/Creators.vue b/dbrepo-ui/components/identifier/Creators.vue index 6c5857d978..706736b21b 100644 --- a/dbrepo-ui/components/identifier/Creators.vue +++ b/dbrepo-ui/components/identifier/Creators.vue @@ -69,13 +69,16 @@ export default { this.creators.push(creator) return } + this.creators.push(creator) + if (!(personOrOrg.affiliation || personOrOrg.affiliation_identifier || personOrOrg.affiliation_identifier_scheme)) { + return + } this.affiliations.push({ name: personOrOrg.affiliation, name_identifier: personOrOrg.affiliation_identifier, name_identifier_scheme: personOrOrg.affiliation_identifier_scheme }) creator.affiliation_index = this.getIndex(creator) + 1 - this.creators.push(creator) }) }, methods: { diff --git a/dbrepo-ui/components/identifier/Persist.vue b/dbrepo-ui/components/identifier/Persist.vue index 2af517a68b..3b9321ad36 100644 --- a/dbrepo-ui/components/identifier/Persist.vue +++ b/dbrepo-ui/components/identifier/Persist.vue @@ -1339,7 +1339,8 @@ export default { }, createOrSave () { if (!this.formValid) { - this.$toast.info(this.$t('error.identifier.form')) + const toast = useToastInstance() + toast.info(this.$t('error.identifier.form')) return } if (!this.identifier.id) { @@ -1355,12 +1356,14 @@ export default { identifierService.save(payload) .then((identifier) => { this.cacheStore.reloadDatabase() - this.$toast.success(this.$t('success.pid.saved')) + const toast = useToastInstance() + toast.success(this.$t('success.pid.saved')) this.identifier = identifier this.loadingSave = false }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingSave = false }) .finally(() => { @@ -1374,13 +1377,15 @@ export default { identifierService.create(payload) .then((identifier) => { this.cacheStore.reloadDatabase() - this.$toast.success(this.$t('success.pid.created')) + const toast = useToastInstance() + toast.success(this.$t('success.pid.created')) this.identifier = identifier this.$router.push(this.nextTo) this.loadingSave = false }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingSave = false }) .finally(() => { @@ -1392,7 +1397,8 @@ export default { const identifierService = useIdentifierService() identifierService.publish(this.identifier.id) .then(() => { - this.$toast.success(this.$t('success.pid.published')) + const toast = useToastInstance() + toast.success(this.$t('success.pid.published')) this.cacheStore.reloadDatabase() this.loadingPublish = false }) @@ -1409,7 +1415,8 @@ export default { identifierService.remove(this.identifier.id) .then(() => { this.cacheStore.reloadDatabase() - this.$toast.success(this.$t('success.pid.deleted')) + const toast = useToastInstance() + toast.success(this.$t('success.pid.deleted')) this.$router.push(this.backTo) this.loadingDelete = false }) diff --git a/dbrepo-ui/components/subset/Builder.vue b/dbrepo-ui/components/subset/Builder.vue index f379a59084..e8e60d6559 100644 --- a/dbrepo-ui/components/subset/Builder.vue +++ b/dbrepo-ui/components/subset/Builder.vue @@ -503,12 +503,14 @@ export default { const queryService = useQueryService() queryService.execute(this.$route.params.database_id, { statement: this.sql }, this.timestamp, 0, 1) .then(async (subset) => { - this.$toast.success(this.$t('success.subset.create')) + const toast = useToastInstance() + toast.success(this.$t('success.subset.create')) await this.$router.push(`/database/${this.$route.params.database_id}/subset/${subset.id}/data`) this.loadingQuery = false }) - .catch((error) => { - this.$toast.error(this.$t(error.message)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingQuery = false }) }, @@ -520,12 +522,14 @@ export default { .then(async (view) => { this.resultId = view.id this.cacheStore.reloadDatabase() - this.$toast.success(this.$t('success.view.create')) + const toast = useToastInstance() + toast.success(this.$t('success.view.create')) await this.$router.push(`/database/${this.$route.params.database_id}/view/${view.id}/data`) this.loadingQuery = false }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingQuery = false }) }, @@ -536,7 +540,8 @@ export default { const queryService = useQueryService() const { error, reason, column, raw, formatted } = queryService.build(this.table.internal_name, this.select, this.clauses) if (error) { - this.$toast.error(this.$t('error.query.' + reason) + ' ' + column) + const toast = useToastInstance() + toast.error(this.$t('error.query.' + reason) + ' ' + column) return } this.query.raw = raw diff --git a/dbrepo-ui/components/subset/Results.vue b/dbrepo-ui/components/subset/Results.vue index c1e700faef..9f0cb366a7 100644 --- a/dbrepo-ui/components/subset/Results.vue +++ b/dbrepo-ui/components/subset/Results.vue @@ -110,7 +110,8 @@ export default { this.loadingExecute = false }) .catch(({code}) => { - this.$toast.error(this.$t(code)) + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingExecute = false }) .finally(() => { @@ -125,7 +126,8 @@ export default { this.loadingExecute = false }) .catch(({code}) => { - this.$toast.error(this.$t(code)) + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingExecute = false }) .finally(() => { @@ -146,7 +148,8 @@ export default { this.loadingCount = false }) .catch(({code}) => { - this.$toast.error(this.$t(code)) + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingCount = false }) .finally(() => { @@ -160,7 +163,8 @@ export default { this.loadingCount = false }) .catch(({code}) => { - this.$toast.error(this.$t(code)) + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingCount = false }) .finally(() => { diff --git a/dbrepo-ui/components/subset/SubsetList.vue b/dbrepo-ui/components/subset/SubsetList.vue index 1a0150f4d6..94c1bff1e8 100644 --- a/dbrepo-ui/components/subset/SubsetList.vue +++ b/dbrepo-ui/components/subset/SubsetList.vue @@ -1,5 +1,10 @@ <template> <div> + <v-card + v-if="!loadingSubsets && queries.length === 0" + variant="flat" + rounded="0" + :text="$t('pages.database.subpages.subsets.empty')" /> <v-card variant="flat" rounded="0"> @@ -8,10 +13,6 @@ lines="two"> <Loading /> </v-list-item> - <v-list-item - v-if="!loadingSubsets && queries.length === 0" - lines="two" - :title="$t('pages.database.subpages.subsets.empty')" /> <div v-for="(item, i) in queries" :key="`q-${i}`"> @@ -79,8 +80,9 @@ export default { .then((queries) => { this.queries = queries }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingSubsets = false }) .finally(() => { diff --git a/dbrepo-ui/components/table/BlobUpload.vue b/dbrepo-ui/components/table/BlobUpload.vue index 67d1ffd4d4..d0b1ceb497 100644 --- a/dbrepo-ui/components/table/BlobUpload.vue +++ b/dbrepo-ui/components/table/BlobUpload.vue @@ -39,8 +39,9 @@ export default { this.value = filename this.$emit('blob', { column: this.column, s3key: filename }) }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) }) } } diff --git a/dbrepo-ui/components/table/TableImport.vue b/dbrepo-ui/components/table/TableImport.vue index 92ddba0ecd..3688c4d9e3 100644 --- a/dbrepo-ui/components/table/TableImport.vue +++ b/dbrepo-ui/components/table/TableImport.vue @@ -401,7 +401,8 @@ export default { const tableService = useTableService() tableService.importCsv(this.$route.params.database_id, this.tableId, this.tableImport) .then(() => { - this.$toast.success(this.$t('success.import.dataset')) + const toast = useToastInstance() + toast.success(this.$t('success.import.dataset')) this.cacheStore.reloadDatabase() tableService.getCount(this.$route.params.database_id, this.tableId, null) .then((rowCount) => { @@ -410,9 +411,9 @@ export default { this.step = this.stepStart + 2 this.loading = false }) - .catch((error) => { - console.error('Failed to import csv', error) - this.$toast.error(this.$t('error.import.dataset')) + .catch(() => { + const toast = useToastInstance() + toast.error(this.$t('error.import.dataset')) this.loading = false }) .finally(() => { @@ -425,11 +426,13 @@ export default { const uploadService = useUploadService() return uploadService.create(this.previousFile) .then((s3key) => { - this.$toast.success(this.$t('success.upload.dataset')) + const toast = useToastInstance() + toast.success(this.$t('success.upload.dataset')) this.analyse(s3key) }) .catch(() => { - this.$toast.error(this.$t('error.upload.dataset')) + const toast = useToastInstance() + toast.error(this.$t('error.upload.dataset')) this.loading = false }) }, @@ -462,12 +465,14 @@ export default { this.suggestedAnalyseLineTerminator = line_termination this.tableImport.location = filename this.step = this.stepStart + 2 - this.$toast.success(this.$t('success.analyse.dataset')) + const toast = useToastInstance() + toast.success(this.$t('success.analyse.dataset')) this.$emit('analyse', {columns: this.columns, filename, line_termination}) this.loading = false }) .catch(({code}) => { - this.$toast.error(this.$t(code)) + const toast = useToastInstance() + toast.error(this.$t(code)) this.loading = false }) } diff --git a/dbrepo-ui/components/view/ViewToolbar.vue b/dbrepo-ui/components/view/ViewToolbar.vue index bf415031b7..e6cb5e09db 100644 --- a/dbrepo-ui/components/view/ViewToolbar.vue +++ b/dbrepo-ui/components/view/ViewToolbar.vue @@ -123,12 +123,14 @@ export default { const viewService = useViewService() viewService.remove(this.$route.params.database_id, this.$route.params.view_id) .then(() => { - this.$toast.success(this.$t('success.view.delete')) + const toast = useToastInstance() + toast.success(this.$t('success.view.delete')) this.cacheStore.reloadDatabase() this.$router.push(`/database/${this.$route.params.database_id}/view`) }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) }) .finally(() => { this.loadingDelete = false diff --git a/dbrepo-ui/composables/database-service.ts b/dbrepo-ui/composables/database-service.ts index 6a4e283f8d..3890477822 100644 --- a/dbrepo-ui/composables/database-service.ts +++ b/dbrepo-ui/composables/database-service.ts @@ -60,7 +60,7 @@ export const useDatabaseService = (): any => { resolve(count); }) .catch((error) => { - console.error('Failed to find databases', error); + console.error('Failed to find databases count', error); reject(axiosErrorToApiError(error)); }); }); @@ -72,7 +72,7 @@ export const useDatabaseService = (): any => { return new Promise<Date>((resolve, reject) => { axios.head<Date>('/api/database') .then((response) => { - const date: Date = Date(response.headers['Date']) + const date: Date = new Date(response.headers['Date']) console.info(`Found ${date} server time`); resolve(date); }) @@ -85,7 +85,7 @@ export const useDatabaseService = (): any => { async function findOne(id: number): Promise<DatabaseDto | null> { const axios = useAxiosInstance(); - console.debug('find databases with id', id); + console.debug('find database with id', id); return new Promise((resolve, reject) => { axios.get<DatabaseDto>(`/api/database/${id}`) .then((response) => { @@ -93,7 +93,7 @@ export const useDatabaseService = (): any => { resolve(response.data); }) .catch((error) => { - console.error('Failed to find databases', error); + console.error('Failed to find database', error); reject(axiosErrorToApiError(error)); }); }); diff --git a/dbrepo-ui/composables/toast-instance.ts b/dbrepo-ui/composables/toast-instance.ts new file mode 100644 index 0000000000..bac439b1cd --- /dev/null +++ b/dbrepo-ui/composables/toast-instance.ts @@ -0,0 +1,25 @@ +import {type ToastPluginApi, type ToastProps, useToast} from 'vue-toast-notification'; + +const props: ToastProps = { + position: 'top-right', + duration: 6000, + dismissible: false /* allow copy of error message */ +} + +export const useToastInstance = () => { + function error(message: string): void { + const toast: ToastPluginApi = useToast(props); + if (document) { + toast.error(message) + } + } + + function success(message: string): void { + const toast: ToastPluginApi = useToast(props); + if (document) { + toast.success(message) + } + } + + return {error, success} +}; diff --git a/dbrepo-ui/locales/de-AT.json b/dbrepo-ui/locales/de-AT.json index 15c2664f0f..2e713bd365 100644 --- a/dbrepo-ui/locales/de-AT.json +++ b/dbrepo-ui/locales/de-AT.json @@ -1212,6 +1212,7 @@ "database": { "upload": "Datenbankbild erfolgreich hochgeladen.", "transfer": "Der Datenbankeigentümer wurde erfolgreich übertragen.", + "visibility": "Die Datenbanksichtbarkeit wurde erfolgreich aktualisiert.", "image": { "update": "Datenbankbild erfolgreich aktualisiert.", "remove": "Datenbankbild erfolgreich entfernt." @@ -1226,7 +1227,9 @@ }, "user": { "info": "Benutzerinformationen erfolgreich aktualisiert.", - "theme": "Benutzerthema erfolgreich aktualisiert." + "theme": "Benutzerthema erfolgreich aktualisiert.", + "password": "Benutzerkennwort erfolgreich aktualisiert.", + "login": "Erfolgreich angemeldet." }, "view": { "create": "Ansicht erfolgreich erstellt.", diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json index 227ae506c2..71d8c09051 100644 --- a/dbrepo-ui/locales/en-US.json +++ b/dbrepo-ui/locales/en-US.json @@ -1212,6 +1212,7 @@ "database": { "upload": "Successfully uploaded database image.", "transfer": "Successfully transferred the database owner.", + "visibility": "Successfully updated the database visibility.", "image": { "update": "Successfully updated database image.", "remove": "Successfully removed database image." @@ -1226,7 +1227,9 @@ }, "user": { "info": "Successfully updated user information.", - "theme": "Successfully updated user theme." + "theme": "Successfully updated user theme.", + "password": "Successfully updated user password.", + "login": "Successfully logged in." }, "view": { "create": "Successfully created view.", diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts index f2d9df53a3..7dbf4914d2 100644 --- a/dbrepo-ui/nuxt.config.ts +++ b/dbrepo-ui/nuxt.config.ts @@ -127,7 +127,7 @@ export default defineNuxtConfig({ storage: 'localStorage' }, i18n: { - lazy: true, + lazy: false, langDir: 'locales', strategy: 'no_prefix', defaultLocale: 'de', diff --git a/dbrepo-ui/pages/database/[database_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/settings.vue index 296f639cae..558f152fef 100644 --- a/dbrepo-ui/pages/database/[database_id]/settings.vue +++ b/dbrepo-ui/pages/database/[database_id]/settings.vue @@ -418,7 +418,8 @@ export default { const databaseService = useDatabaseService() databaseService.updateVisibility(this.$route.params.database_id, this.modifyVisibility) .then((database) => { - this.$toast.success('Successfully updated the database visibility') + const toast = useToastInstance() + toast.success('success.database.visibility') this.cacheStore.setDatabase(database) }) .catch(() => { @@ -434,7 +435,8 @@ export default { uploadService.create(this.fileModel[0]) .then((s3key) => { console.debug('uploaded image', s3key) - this.$toast.success(this.$t('success.database.upload')) + const toast = useToastInstance() + toast.success(this.$t('success.database.upload')) this.modifyImage.key = s3key this.loadingUpload = false }) @@ -448,12 +450,14 @@ export default { databaseService.updateImage(this.$route.params.database_id, this.modifyImage) .then(() => { this.cacheStore.reloadDatabase() - this.$toast.success(this.$t('success.database.image.update')) + const toast = useToastInstance() + toast.success(this.$t('success.database.image.update')) this.modifyImage.key = null this.loadingImage = false }) .catch(() => { - this.$toast.error('Failed to modify image') + const toast = useToastInstance() + toast.error('Failed to modify image') this.loadingImage = false }) .finally(() => { @@ -466,11 +470,13 @@ export default { databaseService.updateImage(this.$route.params.database_id, { key: null }) .then(() => { this.cacheStore.reloadDatabase() - this.$toast.success(this.$t('success.database.image.remove')) + const toast = useToastInstance() + toast.success(this.$t('success.database.image.remove')) this.loadingDeleteImage = false }) - .catch(() => { - this.$toast.error('Failed to delete image') + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingDeleteImage = false }) .finally(() => { @@ -482,7 +488,8 @@ export default { const databaseService = useDatabaseService() databaseService.updateOwner(this.$route.params.database_id, this.modifyOwner.id) .then(() => { - this.$toast.success(this.$t('success.database.transfer')) + const toast = useToastInstance() + toast.success(this.$t('success.database.transfer')) location.reload() }) .catch(() => { @@ -497,20 +504,24 @@ export default { const databaseService = useDatabaseService() databaseService.refreshTablesMetadata(this.$route.params.database_id) .then(() => { - this.$toast.success(this.$t('success.schema.tables')) + const toast = useToastInstance() + toast.success(this.$t('success.schema.tables')) databaseService.refreshViewsMetadata(this.$route.params.database_id) .then(() => { - this.$toast.success(this.$t('success.schema.views')) + const toast = useToastInstance() + toast.success(this.$t('success.schema.views')) this.cacheStore.reloadDatabase() this.loadingSchema = false }) .catch(({code}) => { - this.$toast.error(this.$t(code)) + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingSchema = false }) }) .catch(({code}) => { - this.$toast.error(this.$t(code)) + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingSchema = false }) }, diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue index 7484989fa3..3c90c14858 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue @@ -54,7 +54,9 @@ @click.stop="pick" /> </v-toolbar> <TimeDrift /> - <v-card tile> + <v-card + elevation="0" + tile> <v-card v-if="error" variant="flat"> @@ -326,12 +328,14 @@ export default { const tupleService = useTupleService() wait.push(tupleService.remove(this.$route.params.database_id, this.$route.params.table_id, { keys: constraints }) .catch(({message}) => { - this.$toast.error(message) + const toast = useToastInstance() + toast.error(message) })) } Promise.all(wait) .then(() => { - this.$toast.success(`Deleted ${this.selection.length} row(s)`) + const toast = useToastInstance() + toast.success(`Deleted ${this.selection.length} row(s)`) this.$emit('modified', { success: true, action: 'delete' }) this.selection = [] this.reload() @@ -351,8 +355,9 @@ export default { document.body.appendChild(link) link.click() }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.downloadLoading = false }) .finally(() => { @@ -409,8 +414,9 @@ export default { }).forEach(header => this.headers.push(header)) this.dateColumns = this.table.columns.filter(c => (c.column_type === 'date' || c.column_type === 'timestamp')) console.debug('date columns are', this.dateColumns) - } catch (error) { - this.$toast.error(this.$t(error.code)) + } catch ({code}) { + const toast = useToastInstance() + toast.error(this.$t(code)) } this.loading = false }, @@ -442,8 +448,9 @@ export default { }) this.loadingData = false }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.error = true this.loadingData = false }) @@ -456,8 +463,9 @@ export default { this.total = count this.loadingCount = false }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingCount = false }) }, diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue index 4af6ba09d7..0509cc0c2c 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue @@ -193,7 +193,7 @@ export default { return this.userStore.getRoles }, primaryKeysColumns () { - return this.table.constraints.primary_key.join(', ') + return this.table.constraints.primary_key.map(pk => pk.column.internal_name).join(', ') }, canAssignSemanticInformation () { if (!this.user) { @@ -252,7 +252,8 @@ export default { const { success } = event console.debug('closed dialog', event) if (success) { - this.$toast.success(this.$t('success.table.semantics')) + const toast = useToastInstance() + toast.success(this.$t('success.table.semantics')) this.cacheStore.reloadTable() } this.dialogSemantic = false diff --git a/dbrepo-ui/pages/database/[database_id]/table/create.vue b/dbrepo-ui/pages/database/[database_id]/table/create.vue index 496c3ec3cb..34c87d163d 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/create.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/create.vue @@ -260,8 +260,9 @@ export default { this.cacheStore.reloadDatabase() this.table = table }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loading = false }) .finally(() => { diff --git a/dbrepo-ui/pages/database/[database_id]/table/import.vue b/dbrepo-ui/pages/database/[database_id]/table/import.vue index 5eb02f7f8e..724205c7ba 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/import.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/import.vue @@ -327,7 +327,8 @@ export default { }) .catch((error) => { console.error('Failed to create table', error) - this.$toast.error(this.$t(error.code)) + const toast = useToastInstance() + toast.error(this.$t(error.code)) this.loading = false reject(error) }) @@ -342,12 +343,14 @@ export default { tableService.importCsv(this.$route.params.database_id, table.id, this.tableImport) .then(() => { this.step = 5 - this.$toast.success(this.$t('success.import.dataset')) + const toast = useToastInstance() + toast.success(this.$t('success.import.dataset')) this.cacheStore.reloadDatabase() }) - .catch((error) => { - console.error('Failed to import csv', error) - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + console.error('Failed to import csv') + const toast = useToastInstance() + toast.error(this.$t(code)) this.loading = false }) .finally(() => { diff --git a/dbrepo-ui/pages/login.vue b/dbrepo-ui/pages/login.vue index 5ce0c31a81..71ea9ec604 100644 --- a/dbrepo-ui/pages/login.vue +++ b/dbrepo-ui/pages/login.vue @@ -116,6 +116,8 @@ export default { const userId = userService.tokenToUserId(data.access_token) userService.findOne(userId) .then((user) => { + const toast = useToastInstance() + toast.info(this.$t('success.user.login')) switch (user.attributes.theme) { case 'dark': this.$vuetify.theme.global.name = 'tuwThemeDark' @@ -133,13 +135,14 @@ export default { this.userStore.setUser(user) this.$router.push('/database') }) - .catch(error => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) }) }) - .catch((error) => { - console.error('Failed to login', error) - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loading = false }) .finally(() => { diff --git a/dbrepo-ui/pages/semantic/ontology/_ontology_id/index.vue b/dbrepo-ui/pages/semantic/ontology/_ontology_id/index.vue index ee1d19d985..41cfa20426 100644 --- a/dbrepo-ui/pages/semantic/ontology/_ontology_id/index.vue +++ b/dbrepo-ui/pages/semantic/ontology/_ontology_id/index.vue @@ -182,7 +182,8 @@ export default { .then(() => { this.loadOntology() // this.$store.dispatch('reloadOntologies') - this.$toast.success('Successfully update ontology!') + const toast = useToastInstance() + toast.success('Successfully update ontology!') }) .catch(() => { this.loading = false diff --git a/dbrepo-ui/pages/signup.vue b/dbrepo-ui/pages/signup.vue index aa944e5d9f..9d84f5c0df 100644 --- a/dbrepo-ui/pages/signup.vue +++ b/dbrepo-ui/pages/signup.vue @@ -117,12 +117,14 @@ export default { const userService = useUserService() userService.create(this.createAccount) .then(() => { - this.$toast.success(this.$t('success.signup')) + const toast = useToastInstance() + toast.success(this.$t('success.signup')) this.$router.push('/login') this.loading = false }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loading = false }) .finally(() => { @@ -136,8 +138,9 @@ export default { .then((users) => { this.usernames = users.map(u => u.username) }) - .catch((error) => { - this.$toast.error(this.$t(error.code)) + .catch(({code}) => { + const toast = useToastInstance() + toast.error(this.$t(code)) this.loadingUsers = false }) .finally(() => { diff --git a/dbrepo-ui/pages/user/authentication.vue b/dbrepo-ui/pages/user/authentication.vue index 35fec2fa71..a40fef51e5 100644 --- a/dbrepo-ui/pages/user/authentication.vue +++ b/dbrepo-ui/pages/user/authentication.vue @@ -114,7 +114,8 @@ export default { const userService = useUserService() userService.updatePassword(this.user.id, this.password) .then(() => { - this.$toast.success('Successfully changed the password') + const toast = useToastInstance() + toast.success('success.user.password') this.loadingUpdate = false }) .catch(() => { diff --git a/dbrepo-ui/pages/user/info.vue b/dbrepo-ui/pages/user/info.vue index 3a085b6008..3501818ca0 100644 --- a/dbrepo-ui/pages/user/info.vue +++ b/dbrepo-ui/pages/user/info.vue @@ -219,7 +219,8 @@ export default { userService.update(this.user.id, payload) .then((user) => { console.info('Updated user information') - this.$toast.success(this.$t('success.user.info')) + const toast = useToastInstance() + toast.success(this.$t('success.user.info')) this.userStore.setUser(user) /* language */ this.userStore.setLocale(this.model.language) @@ -286,7 +287,8 @@ export default { }, copy () { navigator.clipboard.writeText(this.model.id) - this.$toast.success(this.$t('success.clipboard.user')) + const toast = useToastInstance() + toast.success(this.$t('success.clipboard.user')) } } } diff --git a/dbrepo-ui/plugins/backend.ts b/dbrepo-ui/plugins/backend.ts deleted file mode 100644 index 6d18d9e218..0000000000 --- a/dbrepo-ui/plugins/backend.ts +++ /dev/null @@ -1,10 +0,0 @@ -export default defineNuxtPlugin((nuxtApp) => { - const config = useRuntimeConfig(); - nuxtApp.provide('backendURL', () => { - if (process.server && !process.dev) { - return config.public.backendURLServer; - } else { - return config.public.backendURL; - } - }); -}) diff --git a/dbrepo-ui/stores/cache.js b/dbrepo-ui/stores/cache.js index bb89295ec5..087ae4e9b9 100644 --- a/dbrepo-ui/stores/cache.js +++ b/dbrepo-ui/stores/cache.js @@ -30,25 +30,33 @@ export const useCacheStore = defineStore('cache', { const messageService = useMessageService() messageService.findAll('active') .then(messages => this.messages = messages) - .catch(() => {}) + .catch((error) => { + console.error('Failed to reload messages', error) + }) }, reloadOntologies () { const ontologyService = useOntologyService() ontologyService.findAll() .then(ontologies => this.ontologies = ontologies) - .catch(() => {}) + .catch((error) => { + console.error('Failed to reload ontologies', error) + }) }, reloadDatabase () { const databaseService = useDatabaseService() databaseService.findOne(this.database.id) .then(database => this.database = database) - .catch(() => {}) + .catch((error) => { + console.error('Failed to reload database', error) + }) }, reloadTable () { const tableService = useTableService() tableService.findOne(this.table.database_id, this.table.id) .then(table => this.table = table) - .catch(() => {}) + .catch((error) => { + console.error('Failed to reload table', error) + }) }, setRouteDatabase (databaseId) { if (!databaseId) { @@ -58,7 +66,9 @@ export const useCacheStore = defineStore('cache', { const databaseService = useDatabaseService() databaseService.findOne(databaseId) .then(database => this.database = database) - .catch(() => {}) + .catch((error) => { + console.error('Failed to set route database', error) + }) }, setRouteTable (databaseId, tableId) { if (!databaseId || !tableId) { @@ -68,7 +78,9 @@ export const useCacheStore = defineStore('cache', { const tableService = useTableService() tableService.findOne(databaseId, tableId) .then(table => this.table = table) - .catch(() => {}) + .catch((error) => { + console.error('Failed to set route table', error) + }) } }, }) diff --git a/helm/dbrepo/values.prod.yaml b/helm/dbrepo/values.prod.yaml new file mode 100644 index 0000000000..93c0bf3d72 --- /dev/null +++ b/helm/dbrepo/values.prod.yaml @@ -0,0 +1,514 @@ +namespace: dbrepo + +hostname: example.com + +metadataDb: + fullnameOverride: metadata-db + image: + debug: false + host: metadata-db + rootUser: + user: root + password: dbrepo + jdbcExtraArgs: "" + db: + name: fda + metrics: + enabled: false + galera: + mariabackup: + user: mariabackup + password: mariabackup + initdbScriptsConfigMap: metadata-db-setup + service: + type: ClusterIP + annotations: { } + loadBalancerIP: "" + loadBalancerSourceRanges: [ ] + persistence: + enabled: true + replicaCount: 3 # uneven 3,5,7 + +authService: + fullnameOverride: auth-service + image: + debug: false + auth: + adminUser: fda + adminPassword: fda + postgresql: + enabled: false # not needed + extraStartupArgs: "--import-realm" + tls: + enabled: true + existingSecret: ingress-cert + usePem: true + metrics: + enabled: true + externalDatabase: + existingSecret: auth-service-secret + existingSecretDatabaseKey: db-name + existingSecretHostKey: db-host + existingSecretPortKey: db-port + existingSecretUserKey: db-username + existingSecretPasswordKey: db-password + client: + id: dbrepo-client + secret: MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG + extraEnvVarsCM: auth-service-config + extraVolumes: + - name: config-map + configMap: + name: auth-service-setup + extraVolumeMounts: + - name: config-map + mountPath: /opt/bitnami/keycloak/data/import + replicaCount: 2 + +authDb: + fullnameOverride: auth-db + host: auth-db-pgpool + port: 5432 + postgresql: + postgresPassword: postgres + username: metrics # implicit requirement for metrics container + password: metrics # implicit requirement for metrics container + repmgrPassword: repmgr # implicit requirement for rolling updates + database: keycloak + replicaCount: 3 + pgpool: + adminUsername: admin + adminPassword: admin + metrics: + enabled: true + service: + type: ClusterIP + annotations: { } + loadBalancerIP: "" + loadBalancerSourceRanges: [ ] + persistence: + enabled: true + size: 10Gi + +dataDb: + fullnameOverride: data-db + image: + debug: false + extraFlags: "--character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci" + rootUser: + user: root + password: dbrepo + metrics: + enabled: true + galera: + mariabackup: + user: mariabackup + password: mariabackup + sidecars: + - name: sidecar + image: s210.dl.hpc.tuwien.ac.at/dbrepo/data-db-sidecar:1.4.2 + imagePullPolicy: Always + securityContext: + runAsUser: 1001 + runAsGroup: 1001 + ports: + - containerPort: 3305 + protocol: TCP + env: + - name: S3_STORAGE_ENDPOINT + value: http://storageservice-s3:9000 + - name: S3_ACCESS_KEY_ID + value: seaweedfsadmin + - name: S3_SECRET_ACCESS_KEY + value: seaweedfsadmin + volumeMounts: + - name: tmp # share between sidecar and galera container + mountPath: /tmp + service: + type: ClusterIP + annotations: { } + #loadBalancerIP: 1.2.3.4 + loadBalancerSourceRanges: [ ] + extraPorts: + - name: "sidecar" + port: 3305 + targetPort: 3305 + protocol: TCP + extraVolumeMounts: + - name: tmp + mountPath: /tmp + extraVolumes: + # - name: tmp + # emptyDir: {} + - name: tmp + persistentVolumeClaim: + claimName: data-db-shared + persistence: + enabled: true + size: 10Gi + replicaCount: 3 # uneven 3,5,7 + +searchdb: + fullnameOverride: search-db + host: search-db + port: 9200 + protocol: http + username: admin + password: admin + clusterName: search-db + masterService: search-db + replicas: 3 # uneven 3,5,7 + image: + debug: false + sysctlInit: + enabled: true + persistence: + enabled: true + size: 10Gi + service: + type: ClusterIP + annotations: { } + loadBalancerSourceRanges: [ ] + extraEnvs: + - name: DISABLE_INSTALL_DEMO_CONFIG + value: "true" + extraVolumeMounts: + - name: node-cert + mountPath: /usr/share/opensearch/config/tls + readOnly: true + extraVolumes: + - name: node-cert + secret: + secretName: search-db-cert + config: + opensearch.yml: | + cluster.name: search-db + network.host: 0.0.0.0 + plugins: + security: + ssl: + transport: + pemcert_filepath: tls/tls.crt + pemkey_filepath: tls/tls.key + pemtrustedcas_filepath: tls/ca.crt + enforce_hostname_verification: false + http: + #enabled: true # uncomment to force ssl connections + pemcert_filepath: tls/tls.crt + pemkey_filepath: tls/tls.key + pemtrustedcas_filepath: tls/ca.crt + allow_unsafe_democertificates: false + allow_default_init_securityindex: true + authcz: + admin_dn: + - CN=search-db + nodes_dn: + - CN=search-db + audit.type: internal_opensearch + enable_snapshot_restore_privilege: true + check_snapshot_restore_write_privileges: true + restapi: + roles_enabled: [ "all_access", "security_rest_api_access" ] + system_indices: + enabled: true + indices: + [ + ".opendistro-alerting-config", + ".opendistro-alerting-alert*", + ".opendistro-anomaly-results*", + ".opendistro-anomaly-detector*", + ".opendistro-anomaly-checkpoints", + ".opendistro-anomaly-detection-state", + ".opendistro-reports-*", + ".opendistro-notifications-*", + ".opendistro-notebooks", + ".opendistro-asynchronous-search-response*", + ] + +searchDbDashboard: + fullnameOverride: search-db-dashboard + opensearchHosts: http://search-db:9200 + extraInitContainers: + - name: init + image: s210.dl.hpc.tuwien.ac.at/dbrepo/search-db-init:1.4.2 + imagePullPolicy: Always + env: + - name: OPENSEARCH_HOST + value: http://search-db:9200 + extraVolumeMounts: + - name: tls + mountPath: /usr/share/opensearch-dashboards/tls + readOnly: true + - name: config + mountPath: /usr/share/opensearch-dashboards/config/opensearch_dashboards.yml + subPath: opensearch_dashboards.yml + readOnly: true + extraVolumes: + - name: tls + secret: + secretName: ingress-cert + - name: config + secret: + secretName: search-db-dashboard-secret + replicaCount: 2 + +uploadService: + enabled: true + image: + registry: docker.io + repository: tusproject/tusd + tag: v1.12 + replicaCount: 2 + +brokerService: + fullnameOverride: broker-service + image: + debug: true + url: http://broker-service:15672 + host: broker-service + port: 5672 + virtualHost: dbrepo + queueName: dbrepo + exchangeName: dbrepo + routingKey: dbrepo.# + connectionTimeout: 60000 + auth: + tls: + enabled: false + sslOptionsVerify: true + failIfNoPeerCert: true + existingSecret: ingress-cert + username: broker + password: broker + extraConfiguration: |- + default_vhost = dbrepo + default_user_tags.administrator = true + default_permissions.configure = .* + default_permissions.read = .* + default_permissions.write = .* + load_definitions = /etc/rabbitmq/definitions.json + log.console = true + listeners.tcp.1 = 0.0.0.0:5672 + auth_backends.1 = rabbit_auth_backend_oauth2 + auth_backends.2 = rabbit_auth_backend_internal + auth_oauth2.resource_server_id = rabbitmq + auth_oauth2.preferred_username_claims.1 = client_id + auth_oauth2.default_key = t2OCeCheJ9uwoBbNQjG_nN6WKiLcceTIAZmiTbGODFM + auth_oauth2.signing_keys.t2OCeCheJ9uwoBbNQjG_nN6WKiLcceTIAZmiTbGODFM = /etc/rabbitmq/cert.pem + auth_oauth2.signing_keys.id2 = /etc/rabbitmq/pubkey.pem + auth_oauth2.algorithms.1 = HS256 + auth_oauth2.algorithms.2 = RS256 + loadDefinition: + enabled: true + file: /etc/rabbitmq/definitions.json + existingSecret: broker-service-secret + extraVolumeMounts: + - name: secret-map + mountPath: /etc/rabbitmq/definitions.json + subPath: definitions.json + readOnly: true + - name: secret-map + mountPath: /etc/rabbitmq/pubkey.pem + subPath: pubkey.pem + readOnly: true + - name: secret-map + mountPath: /etc/rabbitmq/cert.pem + subPath: cert.pem + readOnly: true + extraVolumes: + - name: secret-map + secret: + secretName: broker-service-secret + extraPlugins: rabbitmq_prometheus rabbitmq_auth_backend_oauth2 rabbitmq_auth_mechanism_ssl + persistence: + enabled: false + size: 5Gi + service: + type: ClusterIP + # loadBalancerIP: + replicaCount: 2 + +analyseService: + enabled: true + image: + name: s210.dl.hpc.tuwien.ac.at/dbrepo/analyse-service:1.4.2 + pullPolicy: Always + debug: false + replicaCount: 2 + +metadataService: + enabled: true + image: + name: s210.dl.hpc.tuwien.ac.at/dbrepo/metadata-service:1.4.2 + pullPolicy: Always + debug: false + adminEmail: noreply@example.com + authService: + url: http://auth-service + website: http://example.com + repositoryName: Database Repository + datacite: + enabled: false + url: https://api.datacite.org + prefix: "" + username: "" + password: "" + rates: + deleteStaleFiles: 60 + mirror: 60 + obtainMetadata: 60 + deleteStaleQueries: 60 + replicaCount: 2 + +dataService: + enabled: true + image: + name: s210.dl.hpc.tuwien.ac.at/dbrepo/data-service:1.4.2 + pullPolicy: Always + debug: false + jwt: + pubkey: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqnHQ2BWWW9vDNLRCcxD++xZg/16oqMo/c1l+lcFEjjAIJjJp/HqrPYU/U9GvquGE6PbVFtTzW1KcKawOW+FJNOA3CGo8Q1TFEfz43B8rZpKsFbJKvQGVv1Z4HaKPvLUm7iMm8Hv91cLduuoWx6Q3DPe2vg13GKKEZe7UFghF+0T9u8EKzA/XqQ0OiICmsmYPbwvf9N3bCKsB/Y10EYmZRb8IhCoV9mmO5TxgWgiuNeCTtNCv2ePYqL/U0WvyGFW0reasIK8eg3KrAUj8DpyOgPOVBn3lBGf+3KFSYi+0bwZbJZWqbC/Xlk20Go1YfeJPRIt7ImxD27R/lNjgDO/MwIDAQAB" + consumerConcurrentMin: 1 + consumerConcurrentMax: 5 + requeueRejected: false + replicaCount: 2 + +searchService: + enabled: true + image: + name: s210.dl.hpc.tuwien.ac.at/dbrepo/search-service:1.4.2 + pullPolicy: Always + debug: false + replicaCount: 2 + +storageservice: + master: + enabled: true + filer: + enabled: true + replicas: 2 + enablePVC: false + storage: 25Gi + s3: + enabled: true + allowEmptyFolder: true + port: 9000 + enableAuth: true + skipAuthSecretCreation: true + existingConfigSecret: seaweedfs-s3-secret + volume: + enabled: true + replicas: 2 + s3: + enabled: true + replicas: 2 + port: 9000 + metricsPort: 9091 + enableAuth: true + skipAuthSecretCreation: true + existingConfigSecret: seaweedfs-s3-secret + auth: + username: seaweedfsadmin + password: seaweedfsadmin + +logservice: + fullnameOverride: log-service + config: + outputs: | + [OUTPUT] + Name opensearch + Match kube.* + Host search-db + Port 9200 + HTTP_User admin + HTTP_Passwd admin + Logstash_Format On + Replace_Dots On + Type _doc + Retry_Limit False + Suppress_Type_Name On + + [OUTPUT] + Name opensearch + Match host.* + Host search-db + Port 9200 + HTTP_User admin + HTTP_Passwd admin + Logstash_Format On + Logstash_Prefix node + Replace_Dots On + Type _doc + Retry_Limit False + Suppress_Type_Name On +# Replace_Dots On +# Suppress_Type_Name On + +ui: + enabled: true + image: + name: s210.dl.hpc.tuwien.ac.at/dbrepo/ui:1.4.2 + pullPolicy: Always + debug: false + public: + api: + client: {} + server: {} + title: "Database Repository" + logo: "/logo.svg" + icon: "/favicon.ico" + touch: "/apple-touch-icon.png" + broker: + host: example.com + port: + 5671: true + 5672: false + extra: "128.130.0.0/15" + database: + extra: "128.130.0.0/15" + pid: + default: + publisher: "Example University" + doi: + enabled: false + endpoint: https://doi.org + replicaCount: 2 + extraVolumes: [ ] + # - name: images-map + # configMap: + # name: ui-config + extraVolumeMounts: [ ] + # - name: images-map + # mountPath: /static/logo.svg + # subPath: logo.svg + +ingress: + enabled: true + className: nginx + tls: + enabled: true + secretName: ingress-cert + annotations: + basic: {} +# cert-manager.io/cluster-issuer: letsencrypt-cluster-issuer + secure: +# cert-manager.io/cluster-issuer: letsencrypt-cluster-issuer + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" + upload: +# cert-manager.io/cluster-issuer: letsencrypt-cluster-issuer + nginx.ingress.kubernetes.io/proxy-body-size: 2G + rewriteApi: +# cert-manager.io/cluster-issuer: letsencrypt-cluster-issuer + nginx.ingress.kubernetes.io/use-regex: "true" + nginx.ingress.kubernetes.io/rewrite-target: /api/$1 + rewriteRoot: +# cert-manager.io/cluster-issuer: letsencrypt-cluster-issuer + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" + nginx.ingress.kubernetes.io/use-regex: "true" + nginx.ingress.kubernetes.io/rewrite-target: /$1 + rewritePid: +# cert-manager.io/cluster-issuer: letsencrypt-cluster-issuer + nginx.ingress.kubernetes.io/use-regex: "true" + nginx.ingress.kubernetes.io/rewrite-target: /api/pid/$1 diff --git a/lib/python/Makefile b/lib/python/Makefile index 31371ee1da..7c79a178bf 100644 --- a/lib/python/Makefile +++ b/lib/python/Makefile @@ -1,11 +1,8 @@ -all: build +all: build install clean: rm -rf ./python/dist/* ./docs/build/* ./dist/* -install: - pipenv install - docs: clean sphinx-apidoc -o ./docs/source ./dbrepo sphinx-build -M html ./docs/ ./docs/build/ @@ -16,8 +13,14 @@ check: build: clean python3 -m build --sdist . python3 -m build --wheel . + +install: cp ./dist/dbrepo-* ../../dbrepo-analyse-service/lib/ + (cd ../../dbrepo-analyse-service && pipenv lock) cp ./dist/dbrepo-* ../../dbrepo-search-service/lib/ + (cd ../../dbrepo-search-service && pipenv lock) + cp ./dist/dbrepo-* ../../../dpm2024/lib/ + (cd ../../../dpm2024 && pipenv lock) deploy: build python3 -m twine upload --config-file ~/.pypirc --verbose --repository pypi ./dist/dbrepo-* diff --git a/lib/python/dbrepo/RestClient.py b/lib/python/dbrepo/RestClient.py index 453a7d4315..79e6fd1181 100644 --- a/lib/python/dbrepo/RestClient.py +++ b/lib/python/dbrepo/RestClient.py @@ -62,11 +62,12 @@ class RestClient: logging.debug(f'headers: {headers}') if payload is not None: logging.debug(f'payload: {payload.model_dump()}') + payload = payload.model_dump() if self.username is not None and self.password is not None: logging.debug(f'username: {self.username}, password: (hidden)') return requests.request(method=method, url=url, auth=(self.username, self.password), verify=self.secure, - json=payload.model_dump(), headers=headers, params=params, stream=stream) - return requests.request(method=method, url=url, verify=self.secure, json=payload.model_dump(), + json=payload, headers=headers, params=params, stream=stream) + return requests.request(method=method, url=url, verify=self.secure, json=payload, headers=headers, params=params, stream=stream) def upload(self, file_path: str) -> str: diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py index 0a27cb664d..db84bbbdbc 100644 --- a/lib/python/dbrepo/api/dto.py +++ b/lib/python/dbrepo/api/dto.py @@ -511,6 +511,7 @@ class CreateTableColumn(BaseModel): class CreateTableConstraints(BaseModel): uniques: List[List[str]] = field(default_factory=list) checks: List[str] = field(default_factory=list) + primary_key: List[str] = field(default_factory=list) foreign_keys: List[CreateForeignKey] = field(default_factory=list) @@ -945,7 +946,12 @@ class Table(BaseModel): class TableMinimal(BaseModel): id: int database_id: int - name: str + + +class ColumnMinimal(BaseModel): + id: int + table_id: int + database_id: int class Database(BaseModel): @@ -970,16 +976,17 @@ class Database(BaseModel): class Unique(BaseModel): - uid: int + id: int table: TableMinimal - columns: List[Column] + columns: List[ColumnMinimal] class ForeignKey(BaseModel): + id: int name: str - columns: List[Column] + columns: List[ColumnMinimal] referenced_table: TableMinimal - referenced_columns: List[Column] + referenced_columns: List[ColumnMinimal] on_update: Optional[str] = None on_delete: Optional[str] = None @@ -993,7 +1000,9 @@ class CreateForeignKey(BaseModel): class PrimaryKey(BaseModel): - pkid: int + id: int + table: TableMinimal + column: ColumnMinimal class Constraints(BaseModel): diff --git a/lib/python/tests/test_database.py b/lib/python/tests/test_database.py index 017a17445a..1ac768690c 100644 --- a/lib/python/tests/test_database.py +++ b/lib/python/tests/test_database.py @@ -531,8 +531,7 @@ class DatabaseTest(unittest.TestCase): mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=202) # test client = RestClient(username="a", password="b") - response = client.delete_database_access(database_id=1, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') def test_delete_database_access_malformed_fails(self): with requests_mock.Mocker() as mock: @@ -541,8 +540,7 @@ class DatabaseTest(unittest.TestCase): # test try: client = RestClient(username="a", password="b") - response = client.delete_database_access(database_id=1, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except MalformedError: pass @@ -553,8 +551,7 @@ class DatabaseTest(unittest.TestCase): # test try: client = RestClient(username="a", password="b") - response = client.delete_database_access(database_id=1, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except ForbiddenError: pass @@ -565,8 +562,7 @@ class DatabaseTest(unittest.TestCase): # test try: client = RestClient(username="a", password="b") - response = client.delete_database_access(database_id=1, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except NotExistsError: pass @@ -576,8 +572,7 @@ class DatabaseTest(unittest.TestCase): mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404) # test try: - response = RestClient().delete_database_access(database_id=1, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + RestClient().delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except AuthenticationError: pass -- GitLab