From 590b70ddece8106a509218bcd1f71116e847f75b Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Tue, 21 Jan 2025 10:05:22 +0100 Subject: [PATCH 01/19] Bumped version of DBRepo Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .docs/index.md | 2 +- .docs/kubernetes.md | 2 +- .gitlab-ci.yml | 4 +- Makefile | 4 +- dbrepo-analyse-service/.gitignore | 6 - dbrepo-analyse-service/Pipfile | 2 +- dbrepo-analyse-service/Pipfile.lock | 313 +++++++++--------- .../lib/dbrepo-1.6.0.tar.gz | Bin 39925 -> 0 bytes .../lib/dbrepo-1.6.1.tar.gz | Bin 40022 -> 0 bytes .../lib/dbrepo-1.6.2.tar.gz | Bin 0 -> 40056 bytes dbrepo-data-service/pom.xml | 2 +- dbrepo-data-service/querystore/pom.xml | 4 +- dbrepo-data-service/report/pom.xml | 4 +- dbrepo-data-service/rest-service/pom.xml | 6 +- dbrepo-data-service/services/pom.xml | 6 +- dbrepo-metadata-service/api/pom.xml | 6 +- dbrepo-metadata-service/entities/pom.xml | 4 +- dbrepo-metadata-service/oai/pom.xml | 4 +- dbrepo-metadata-service/pom.xml | 2 +- dbrepo-metadata-service/report/pom.xml | 4 +- dbrepo-metadata-service/repositories/pom.xml | 4 +- dbrepo-metadata-service/rest-service/pom.xml | 4 +- dbrepo-metadata-service/services/pom.xml | 4 +- dbrepo-metadata-service/test/pom.xml | 4 +- dbrepo-search-service/Pipfile | 2 +- dbrepo-search-service/Pipfile.lock | 126 +++---- dbrepo-search-service/init/Pipfile | 2 +- dbrepo-search-service/init/Pipfile.lock | 130 ++++---- .../init/lib/dbrepo-1.6.0.tar.gz | Bin 39925 -> 0 bytes .../init/lib/dbrepo-1.6.1.tar.gz | Bin 40022 -> 0 bytes .../init/lib/dbrepo-1.6.2.tar.gz | Bin 0 -> 40056 bytes dbrepo-search-service/lib/dbrepo-1.6.0.tar.gz | Bin 39925 -> 0 bytes dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz | Bin 40022 -> 0 bytes dbrepo-search-service/lib/dbrepo-1.6.2.tar.gz | Bin 0 -> 40056 bytes dbrepo-upload-service/pom.xml | 2 +- helm/dbrepo/Chart.lock | 2 +- helm/dbrepo/Chart.yaml | 4 +- helm/dbrepo/README.md | 4 +- helm/dbrepo/values.yaml | 16 +- install.sh | 2 +- lib/python/docs/index.rst | 2 +- lib/python/pyproject.toml | 2 +- lib/python/setup.py | 2 +- sonar-project.properties | 2 +- 44 files changed, 345 insertions(+), 344 deletions(-) delete mode 100644 dbrepo-analyse-service/lib/dbrepo-1.6.0.tar.gz delete mode 100644 dbrepo-analyse-service/lib/dbrepo-1.6.1.tar.gz create mode 100644 dbrepo-analyse-service/lib/dbrepo-1.6.2.tar.gz delete mode 100644 dbrepo-search-service/init/lib/dbrepo-1.6.0.tar.gz delete mode 100644 dbrepo-search-service/init/lib/dbrepo-1.6.1.tar.gz create mode 100644 dbrepo-search-service/init/lib/dbrepo-1.6.2.tar.gz delete mode 100644 dbrepo-search-service/lib/dbrepo-1.6.0.tar.gz delete mode 100644 dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz create mode 100644 dbrepo-search-service/lib/dbrepo-1.6.2.tar.gz diff --git a/.docs/index.md b/.docs/index.md index e16f9f5da6..64b807cae2 100644 --- a/.docs/index.md +++ b/.docs/index.md @@ -14,7 +14,7 @@ author: Martin Weise   -Documentation for version: [v1.6.1](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/releases). +Documentation for version: [v1.6.2](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/releases). DBRepo is a repository for data in databases that are used from the beginning until the end of a research project supporting data evolution, -citation and -versioning. It implements the query store of the diff --git a/.docs/kubernetes.md b/.docs/kubernetes.md index cc16bbe210..60f87eb6d4 100644 --- a/.docs/kubernetes.md +++ b/.docs/kubernetes.md @@ -14,7 +14,7 @@ helm upgrade --install dbrepo \ -n dbrepo \ "oci://registry.datalab.tuwien.ac.at/dbrepo/helm/dbrepo" \ --values ./values.yaml \ - --version "1.6.1" \ + --version "1.6.2" \ --create-namespace \ --cleanup-on-fail ``` diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 122597e695..01e1f79c16 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,8 +10,8 @@ variables: SONARQUBE_VERSION: "10.0" BUN_VERSION: "1.1.40" DOC_VERSION: "1.6" - APP_VERSION: "1.6.1" - CHART_VERSION: "1.6.1" + APP_VERSION: "1.6.2" + CHART_VERSION: "1.6.2" CACHE_FALLBACK_KEY: "${CI_DEFAULT_BRANCH}" # This will supress any download for dependencies and plugins or upload messages which would clutter the console log. # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work. diff --git a/Makefile b/Makefile index 2479e382e5..e5d0dbec5f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: all -APP_VERSION ?= 1.6.1 -CHART_VERSION ?= 1.6.1 +APP_VERSION ?= 1.6.2 +CHART_VERSION ?= 1.6.2 REPOSITORY_URL ?= registry.datalab.tuwien.ac.at/dbrepo .PHONY: all diff --git a/dbrepo-analyse-service/.gitignore b/dbrepo-analyse-service/.gitignore index d339f8575c..4ae9f6930d 100644 --- a/dbrepo-analyse-service/.gitignore +++ b/dbrepo-analyse-service/.gitignore @@ -17,12 +17,6 @@ venv/ .venv/ env* -# Libraries -./lib/dbrepo-1.4.4* -./lib/dbrepo-1.4.5* -./lib/dbrepo-1.4.6* -./lib/dbrepo-1.4.7rc* - # LLM *.bin diff --git a/dbrepo-analyse-service/Pipfile b/dbrepo-analyse-service/Pipfile index 831f8e532d..9c7b709520 100644 --- a/dbrepo-analyse-service/Pipfile +++ b/dbrepo-analyse-service/Pipfile @@ -21,7 +21,7 @@ numpy = "*" pandas = "*" minio = "*" pydantic = "*" -dbrepo = {path = "./lib/dbrepo-1.6.1.tar.gz"} +dbrepo = {path = "./lib/dbrepo-1.6.2.tar.gz"} opensearch-py = "*" [dev-packages] diff --git a/dbrepo-analyse-service/Pipfile.lock b/dbrepo-analyse-service/Pipfile.lock index 99d70b6132..9f00d97ca6 100644 --- a/dbrepo-analyse-service/Pipfile.lock +++ b/dbrepo-analyse-service/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "683cc19a3205b9b5f9b99db8b71c0abadadfd652a94dcf710a73aeca92b97227" + "sha256": "5fbd87c094d93565d64444fc1734d9183e7095d47447d30d6493dfc6bb7e8201" }, "pipfile-spec": 6, "requires": { @@ -175,20 +175,20 @@ }, "boto3": { "hashes": [ - "sha256:7d398f66a11e67777c189d1f58c0a75d9d60f98d0ee51b8817e828930bf19e4e", - "sha256:8e49416216a6e3a62c2a0c44fba4dd2852c85472e7b702516605b1363867d220" + "sha256:76cfc9a705be46e8d22607efacc8d688c064f923d785a01c00b28e9a96425d1a", + "sha256:fde1c29996b77274a60b7bc9f741525afa6267bb1716eb644a764fb7c124a0d2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.35.97" + "version": "==1.36.2" }, "botocore": { "hashes": [ - "sha256:88f2fab29192ffe2f2115d5bafbbd823ff4b6eb2774296e03ec8b5b0fe074f61", - "sha256:fed4f156b1a9b8ece53738f702ba5851b8c6216b4952de326547f349cc494f14" + "sha256:a1fe6610983f0214b0c7655fe6990b6a731746baf305b182976fc7b568fc3cb0", + "sha256:bc3b7e3b573a48af2bd7116b80fe24f9a335b0b67314dcb2697a327d009abf29" ], "markers": "python_version >= '3.8'", - "version": "==1.35.97" + "version": "==1.36.2" }, "certifi": { "hashes": [ @@ -412,9 +412,9 @@ }, "dbrepo": { "hashes": [ - "sha256:7cddcbdcb3eade84f67db01fa32e0649ecc01d4c3cc5e7542d3c402ad52efc19" + "sha256:501b53c7e4b32774809f9685a18288da5b938fc1512e94d8b248f531ee8667fc" ], - "path": "./lib/dbrepo-1.6.1.tar.gz" + "path": "./lib/dbrepo-1.6.2.tar.gz" }, "events": { "hashes": [ @@ -829,12 +829,12 @@ }, "minio": { "hashes": [ - "sha256:868dfe907e1702ce4bec86df1f3ced577a73ca85f344ef898d94fe2b5237f8c1", - "sha256:f5c24bf236fefd2edc567cd4455dc49a11ad8ff7ac984bb031b849d82f01222a" + "sha256:5247df5d4dca7bfa4c9b20093acd5ad43e82d8710ceb059d79c6eea970f49f79", + "sha256:c06ef7a43e5d67107067f77b6c07ebdd68733e5aa7eed03076472410ca19d876" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==7.2.14" + "version": "==7.2.15" }, "mistune": { "hashes": [ @@ -944,65 +944,65 @@ }, "numpy": { "hashes": [ - "sha256:059e6a747ae84fce488c3ee397cee7e5f905fd1bda5fb18c66bc41807ff119b2", - "sha256:08ef779aed40dbc52729d6ffe7dd51df85796a702afbf68a4f4e41fafdc8bda5", - "sha256:164a829b6aacf79ca47ba4814b130c4020b202522a93d7bff2202bfb33b61c60", - "sha256:26c9c4382b19fcfbbed3238a14abf7ff223890ea1936b8890f058e7ba35e8d71", - "sha256:27f5cdf9f493b35f7e41e8368e7d7b4bbafaf9660cba53fb21d2cd174ec09631", - "sha256:31b89fa67a8042e96715c68e071a1200c4e172f93b0fbe01a14c0ff3ff820fc8", - "sha256:32cb94448be47c500d2c7a95f93e2f21a01f1fd05dd2beea1ccd049bb6001cd2", - "sha256:360137f8fb1b753c5cde3ac388597ad680eccbbbb3865ab65efea062c4a1fd16", - "sha256:3683a8d166f2692664262fd4900f207791d005fb088d7fdb973cc8d663626faa", - "sha256:38efc1e56b73cc9b182fe55e56e63b044dd26a72128fd2fbd502f75555d92591", - "sha256:3d03883435a19794e41f147612a77a8f56d4e52822337844fff3d4040a142964", - "sha256:3ecc47cd7f6ea0336042be87d9e7da378e5c7e9b3c8ad0f7c966f714fc10d821", - "sha256:40f9e544c1c56ba8f1cf7686a8c9b5bb249e665d40d626a23899ba6d5d9e1484", - "sha256:4250888bcb96617e00bfa28ac24850a83c9f3a16db471eca2ee1f1714df0f957", - "sha256:4511d9e6071452b944207c8ce46ad2f897307910b402ea5fa975da32e0102800", - "sha256:45681fd7128c8ad1c379f0ca0776a8b0c6583d2f69889ddac01559dfe4390918", - "sha256:48fd472630715e1c1c89bf1feab55c29098cb403cc184b4859f9c86d4fcb6a95", - "sha256:4c86e2a209199ead7ee0af65e1d9992d1dce7e1f63c4b9a616500f93820658d0", - "sha256:4dfda918a13cc4f81e9118dea249e192ab167a0bb1966272d5503e39234d694e", - "sha256:5062dc1a4e32a10dc2b8b13cedd58988261416e811c1dc4dbdea4f57eea61b0d", - "sha256:51faf345324db860b515d3f364eaa93d0e0551a88d6218a7d61286554d190d73", - "sha256:526fc406ab991a340744aad7e25251dd47a6720a685fa3331e5c59fef5282a59", - "sha256:53c09385ff0b72ba79d8715683c1168c12e0b6e84fb0372e97553d1ea91efe51", - "sha256:55ba24ebe208344aa7a00e4482f65742969a039c2acfcb910bc6fcd776eb4355", - "sha256:5b6c390bfaef8c45a260554888966618328d30e72173697e5cabe6b285fb2348", - "sha256:5c5cc0cbabe9452038ed984d05ac87910f89370b9242371bd9079cb4af61811e", - "sha256:5edb4e4caf751c1518e6a26a83501fda79bff41cc59dac48d70e6d65d4ec4440", - "sha256:61048b4a49b1c93fe13426e04e04fdf5a03f456616f6e98c7576144677598675", - "sha256:676f4eebf6b2d430300f1f4f4c2461685f8269f94c89698d832cdf9277f30b84", - "sha256:67d4cda6fa6ffa073b08c8372aa5fa767ceb10c9a0587c707505a6d426f4e046", - "sha256:694f9e921a0c8f252980e85bce61ebbd07ed2b7d4fa72d0e4246f2f8aa6642ab", - "sha256:733585f9f4b62e9b3528dd1070ec4f52b8acf64215b60a845fa13ebd73cd0712", - "sha256:7671dc19c7019103ca44e8d94917eba8534c76133523ca8406822efdd19c9308", - "sha256:780077d95eafc2ccc3ced969db22377b3864e5b9a0ea5eb347cc93b3ea900315", - "sha256:7ba9cc93a91d86365a5d270dee221fdc04fb68d7478e6bf6af650de78a8339e3", - "sha256:89b16a18e7bba224ce5114db863e7029803c179979e1af6ad6a6b11f70545008", - "sha256:9036d6365d13b6cbe8f27a0eaf73ddcc070cae584e5ff94bb45e3e9d729feab5", - "sha256:93cf4e045bae74c90ca833cba583c14b62cb4ba2cba0abd2b141ab52548247e2", - "sha256:9ad014faa93dbb52c80d8f4d3dcf855865c876c9660cb9bd7553843dd03a4b1e", - "sha256:9b1d07b53b78bf84a96898c1bc139ad7f10fda7423f5fd158fd0f47ec5e01ac7", - "sha256:a7746f235c47abc72b102d3bce9977714c2444bdfaea7888d241b4c4bb6a78bf", - "sha256:aa3017c40d513ccac9621a2364f939d39e550c542eb2a894b4c8da92b38896ab", - "sha256:b34d87e8a3090ea626003f87f9392b3929a7bbf4104a05b6667348b6bd4bf1cd", - "sha256:b541032178a718c165a49638d28272b771053f628382d5e9d1c93df23ff58dbf", - "sha256:ba5511d8f31c033a5fcbda22dd5c813630af98c70b2661f2d2c654ae3cdfcfc8", - "sha256:bc8a37ad5b22c08e2dbd27df2b3ef7e5c0864235805b1e718a235bcb200cf1cb", - "sha256:bff7d8ec20f5f42607599f9994770fa65d76edca264a87b5e4ea5629bce12268", - "sha256:c1ad395cf254c4fbb5b2132fee391f361a6e8c1adbd28f2cd8e79308a615fe9d", - "sha256:f1d09e520217618e76396377c81fba6f290d5f926f50c35f3a5f72b01a0da780", - "sha256:f3eac17d9ec51be534685ba877b6ab5edc3ab7ec95c8f163e5d7b39859524716", - "sha256:f419290bc8968a46c4933158c91a0012b7a99bb2e465d5ef5293879742f8797e", - "sha256:f62aa6ee4eb43b024b0e5a01cf65a0bb078ef8c395e8713c6e8a12a697144528", - "sha256:f74e6fdeb9a265624ec3a3918430205dff1df7e95a230779746a6af78bc615af", - "sha256:f9b57eaa3b0cd8db52049ed0330747b0364e899e8a606a624813452b8203d5f7", - "sha256:fce4f615f8ca31b2e61aa0eb5865a21e14f5629515c9151850aa936c02a1ee51" + "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f", + "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0", + "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd", + "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2", + "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4", + "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648", + "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be", + "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb", + "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160", + "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd", + "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a", + "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84", + "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e", + "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748", + "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825", + "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60", + "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957", + "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715", + "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317", + "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e", + "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283", + "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278", + "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9", + "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de", + "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369", + "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb", + "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189", + "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014", + "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323", + "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e", + "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49", + "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50", + "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d", + "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37", + "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39", + "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576", + "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a", + "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba", + "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7", + "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826", + "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467", + "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495", + "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc", + "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391", + "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0", + "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97", + "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c", + "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac", + "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369", + "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8", + "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2", + "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff", + "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a", + "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df", + "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==2.2.1" + "version": "==2.2.2" }, "opensearch-py": { "hashes": [ @@ -1427,11 +1427,11 @@ }, "referencing": { "hashes": [ - "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", - "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de" + "sha256:363d9c65f080d0d70bc41c721dce3c7f3e77fc09f269cd5c8813da18069a6794", + "sha256:ca2e6492769e3602957e9b831b94211599d2aade9477f5d44110d2530cf9aade" ], - "markers": "python_version >= '3.8'", - "version": "==0.35.1" + "markers": "python_version >= '3.9'", + "version": "==0.36.1" }, "requests": { "hashes": [ @@ -1553,11 +1553,11 @@ }, "s3transfer": { "hashes": [ - "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", - "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" + "sha256:3f25c900a367c8b7f7d8f9c34edc87e300bde424f779dc9f0a8ae4f9df9264f6", + "sha256:8fa0aa48177be1f3425176dfe1ab85dcd3d962df603c3dbfc585e6bf857ef0ff" ], "markers": "python_version >= '3.8'", - "version": "==0.10.4" + "version": "==0.11.1" }, "setuptools": { "hashes": [ @@ -1612,7 +1612,7 @@ "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" ], - "markers": "python_version >= '3.10'", + "markers": "python_version >= '3.9'", "version": "==2.3.0" }, "werkzeug": { @@ -2079,12 +2079,12 @@ }, "minio": { "hashes": [ - "sha256:868dfe907e1702ce4bec86df1f3ced577a73ca85f344ef898d94fe2b5237f8c1", - "sha256:f5c24bf236fefd2edc567cd4455dc49a11ad8ff7ac984bb031b849d82f01222a" + "sha256:5247df5d4dca7bfa4c9b20093acd5ad43e82d8710ceb059d79c6eea970f49f79", + "sha256:c06ef7a43e5d67107067f77b6c07ebdd68733e5aa7eed03076472410ca19d876" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==7.2.14" + "version": "==7.2.15" }, "opensearch-py": { "hashes": [ @@ -2236,86 +2236,93 @@ "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" ], - "markers": "python_version >= '3.10'", + "markers": "python_version >= '3.9'", "version": "==2.3.0" }, "wrapt": { "hashes": [ - "sha256:09f5141599eaf36d6cc0b760ad87c2ab6b8618d009b2922639266676775a73a6", - "sha256:0aad4f54b3155d673a5c4706a71a0a84f3d415b2fc8a2a399a964d70f18846a2", - "sha256:0eb33799b7582bb73787b9903b70595f8eff67eecc9455f668ed01adf53f9eea", - "sha256:0ee037e4cc9d039efe712b13c483f4efa2c3499642369e01570b3bb1842eea3f", - "sha256:0fdc4e73a3fa0c25eed4d836d9732226f0326957cb075044a7f252b465299433", - "sha256:13887d1415dc0e213a9adeb9026ae1f427023f77110d988fbd478643490aa40c", - "sha256:144ed42a4ec3aca5d6f1524f99ee49493bbd0d9c66c24da7ec44b4661dca4dcc", - "sha256:14f78f8c313884f889c6696af62aa881af302a989a7c0df398d2b541fa53e8a9", - "sha256:15f96fe5e2efdc613983327240ae89cf6368c07eeb0f194d240e9549aa1ea739", - "sha256:162d5f15bdd3b8037e06540902227ef9e0f298496c0afaadd9e2875851446693", - "sha256:169033329022739c6f0d8cd3031a113953b0ba500f3d5978904bdd40baec4568", - "sha256:16b2fdfa09a74a3930175b6d9d7d008022aa72a4f02de2b3eecafcc1adfd3cfe", - "sha256:181a844005c9818792212a32e004cb4c6bd8e35cae8e97b1a39a1918d95cef58", - "sha256:18fb16fb6bb75f4ec6272829007f3129a9a5264d0230372f9651e5f75cfec552", - "sha256:1c119802ae432b8c5d55dd5253825d09c1dca1c97ffc7b32c53ecdb348712f64", - "sha256:20888d886186d19eab53816db2e615950b1ce7dbd5c239107daf2c8a6a4a03c6", - "sha256:21ffcf16f5c243a626b0f8da637948e3d5984e3bc0c1bc500ad990e88e974e3b", - "sha256:27a49f217839bf559d436308bae8fc4a9dd0ac98ffdb9d6aeb3f00385b0fb72c", - "sha256:2b20fcef5a3ee410671a5a59472e1ff9dda21cfbe5dfd15e23ee4b99ac455c8e", - "sha256:2c160bb8815787646b27a0c8575a26a4d6bf6abd7c5eb250ad3f2d38b29cb2cb", - "sha256:2f1bc359f6c52e53565e7af24b423e7a1eea97d155f38ac9e90e95303514710b", - "sha256:30c0c08434fe2af6e40c5c75c036d7e3c7e7f499079fc479e740d9586b09fb0d", - "sha256:3260178f3bc006acae93378bfd6dbf33c9249de93cc1b78d8cc7b7416f4ea99a", - "sha256:3dfd4738a630eddfcb7ff6c8e9fe863df3821f9c991dec73821e05450074ae09", - "sha256:50a4e3b45e62b1ccb96b3fc0e427f1b458ff2e0def34ae084de88418157a09d1", - "sha256:50bbfa7a92da7540426c774e09d6901e44d8f9b513b276ebae03ae244f0c6dbf", - "sha256:52f0907287d9104112dbebda46af4db0793fcc4c64c8a867099212d116b6db64", - "sha256:53e2986a65eba7c399d7ad1ccd204562d4ffe6e937344fe5a49eb5a83858f797", - "sha256:5660e470edfa15ae7ef407272c642d29e9962777a6b30bfa8fc0da2173dc9afd", - "sha256:57e932ad1908b53e9ad67a746432f02bc8473a9ee16e26a47645a2b224fba5fd", - "sha256:589f24449fd58508533c4a69b2a0f45e9e3419b86b43a0607e2fdb989c6f2552", - "sha256:5c2e24ba455af4b0a237a890ea6ed9bafd01fac2c47095f87c53ea3344215d43", - "sha256:5ebea3ebb6a394f50f150a52e279508e91c8770625ac8fcb5d8cf35995a320f2", - "sha256:67c30d3fe245adb0eb1061a0e526905970a0dabe7c5fba5078e0ee9d19f28167", - "sha256:6bb82447ddae4e3d9b51f40c494f66e6cbd8fb0e8e8b993678416535c67f9a0d", - "sha256:6ce4cff3922707048d754e365c4ebf41a3bcbf29b329349bf85d51873c7c7e9e", - "sha256:6d44b14f3a2f6343a07c90344850b7af5515538ce3a5d01f9c87d8bae9bd8724", - "sha256:6fd88935b12b59a933ef45facb57575095f205d30d0ae8dd1a3b485bc4fa2fbd", - "sha256:78da796b74f2c8e0af021ee99feb3bff7cb46f8e658fe25c20e66be1080db4a2", - "sha256:7966f98fa36933333d8a1c3d8552aa3d0735001901a4aabcfbd5a502b4ef14fe", - "sha256:7eca3a1afa9820785b79cb137c68ca38c2f77cfedc3120115da42e1d5800907e", - "sha256:823a262d967cbdf835787039b873ff551e36c14658bdc2e43267968b67f61f88", - "sha256:88623fd957ba500d8bb0f7427a76496d99313ca2f9e932481c0882e034cf1add", - "sha256:889587664d245dae75c752b643061f922e8a590d43a4cd088eca415ca83f2d13", - "sha256:9176057c60438c2ce2284cdefc2b3ee5eddc8c87cd6e24c558d9f5c64298fa4a", - "sha256:93018dbb956e0ad99ea2fa2c3c22f033549dcb1f56ad9f4555dfe25e49688c5d", - "sha256:97eaff096fcb467e0f486f3bf354c1072245c2045859d71ba71158717ec97dcc", - "sha256:997e8f9b984e4263993d3baf3329367e7c7673b63789bc761718a6f9ed68653d", - "sha256:99e544e6ce26f89ad5acc6f407bc4daf7c1d42321e836f5c768f834100bdf35c", - "sha256:9e04f3bd30e0b23c0ca7e1d4084e7d28b6d7d2feb8b7bc69b496fe881280579b", - "sha256:a7aa07603d67007c15b33d20095cc9276f3e127bfb1b8106b3e84ec6907d137e", - "sha256:a992f9e019145e84616048556546edeaba68e05e1c1ffbe8391067a63cdadb0c", - "sha256:b1a4c8edd038fee0ce67bf119b16eaa45d22a52bbaf7d0a17d2312eb0003b1bb", - "sha256:b8bd35c15bc82c5cbe397e8196fa57a17ce5d3f30e925a6fd39e4c5bb02fdcff", - "sha256:b9a58a1cbdc0588ed4c8ab0c191002d5d831a58c3bad88523fe471ea97eaf57d", - "sha256:bac64f57a5a7926ebc9ab519fb9eba1fc6dcd1f65d7f45937b2ce38da65c2270", - "sha256:bca1c0824f824bcd97b4b179dd55dcad1dab419252be2b2faebbcacefa3b27b2", - "sha256:bdf7b0e3d3713331c0bb9daac47cd10e5aa60d060e53696f50de4e560bd5617f", - "sha256:c53ef8936c4d587cb96bb1cf0d076e822fa38266c2b646837ef60465da8db22e", - "sha256:cbead724daa13cae46e8ab3bb24938d8514d123f34345535b184f3eb1b7ad717", - "sha256:cd7649f0c493d35f9aad9790bbecd7b6fd2e2f7141f6cb1e1e9bb7a681d6d0a4", - "sha256:d609f0ab0603bbcbf2de906b366b9f9bec75c32b4493550a940de658cc2ce512", - "sha256:d792631942a102d6d4f71e4948aceb307310ac0a0af054be6d28b4f79583e0f1", - "sha256:d87334b521ab0e2564902c0b10039dee8670485e9d397fe97c34b88801f474f7", - "sha256:da0d0c1c4bd55f9ace919454776dbf0821f537b9a77f739f0c3e34b14728b3b3", - "sha256:e0f0e731e0ca1583befd3af71b9f90d64ded1535da7b80181cb9e907cc10bbae", - "sha256:e5bd9186d52cf3d36bf1823be0e85297e4dbad909bc6dd495ce0d272806d84a7", - "sha256:e72053cc4706dac537d5a772135dc3e1de5aff52883f49994c1757c1b2dc9db2", - "sha256:e8a7b0699a381226d81d75b48ea58414beb5891ba8982bdc8e42912f766de074", - "sha256:ec3e763e7ca8dcba0792fc3e8ff7061186f59e9aafe4438e6bb1f635a6ab0901", - "sha256:f17e8d926f63aed65ff949682c922f96d00f65c2e852c24272232313fa7823d5", - "sha256:f3117feb1fc479eaf84b549d3f229d5d2abdb823f003bc2a1c6dd70072912fa0" + "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f", + "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", + "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", + "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", + "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", + "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", + "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", + "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", + "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8", + "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", + "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061", + "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", + "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb", + "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", + "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", + "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", + "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", + "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", + "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7", + "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", + "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", + "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", + "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", + "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", + "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", + "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", + "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", + "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a", + "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", + "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", + "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9", + "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", + "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82", + "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9", + "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", + "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", + "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", + "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", + "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", + "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", + "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", + "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", + "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", + "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a", + "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3", + "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a", + "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", + "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", + "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", + "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", + "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", + "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", + "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", + "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", + "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", + "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", + "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2", + "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", + "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", + "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", + "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f", + "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9", + "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04", + "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", + "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9", + "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f", + "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", + "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", + "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", + "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", + "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", + "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", + "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", + "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6", + "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", + "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb", + "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119", + "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b", + "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58" ], "markers": "python_version >= '3.8'", - "version": "==1.17.1" + "version": "==1.17.2" } } } diff --git a/dbrepo-analyse-service/lib/dbrepo-1.6.0.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.6.0.tar.gz deleted file mode 100644 index 80c2ba74f662e7b02895122a37e301fde2157b82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39925 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 diff --git a/dbrepo-analyse-service/lib/dbrepo-1.6.1.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.6.1.tar.gz deleted file mode 100644 index 7914db1bb84dddf85611cda3b766c0c0cdc094c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40022 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 diff --git a/dbrepo-analyse-service/lib/dbrepo-1.6.2.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.6.2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..58081673e955d89fccf70c9161037a725b647f71 GIT binary patch literal 40056 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 literal 0 HcmV?d00001 diff --git a/dbrepo-data-service/pom.xml b/dbrepo-data-service/pom.xml index 884824994a..7d5e6941e4 100644 --- a/dbrepo-data-service/pom.xml +++ b/dbrepo-data-service/pom.xml @@ -11,7 +11,7 @@ <groupId>at.tuwien</groupId> <artifactId>dbrepo-data-service</artifactId> <name>dbrepo-data-service</name> - <version>1.6.1</version> + <version>1.6.2</version> <description>Service that manages the data</description> diff --git a/dbrepo-data-service/querystore/pom.xml b/dbrepo-data-service/querystore/pom.xml index 943c115d11..cb712233ce 100644 --- a/dbrepo-data-service/querystore/pom.xml +++ b/dbrepo-data-service/querystore/pom.xml @@ -6,12 +6,12 @@ <parent> <groupId>at.tuwien</groupId> <artifactId>dbrepo-data-service</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>dbrepo-data-service-querystore</artifactId> <name>dbrepo-data-service-querystore</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies/> diff --git a/dbrepo-data-service/report/pom.xml b/dbrepo-data-service/report/pom.xml index ca03190a44..8de452bbf0 100644 --- a/dbrepo-data-service/report/pom.xml +++ b/dbrepo-data-service/report/pom.xml @@ -6,12 +6,12 @@ <parent> <groupId>at.tuwien</groupId> <artifactId>dbrepo-data-service</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>report</artifactId> <name>dbrepo-data-service-report</name> - <version>1.6.1</version> + <version>1.6.2</version> <description> This module is only intended for the pipeline coverage report. See the detailed report in the respective modules diff --git a/dbrepo-data-service/rest-service/pom.xml b/dbrepo-data-service/rest-service/pom.xml index e72392c707..8ff195ea79 100644 --- a/dbrepo-data-service/rest-service/pom.xml +++ b/dbrepo-data-service/rest-service/pom.xml @@ -6,18 +6,18 @@ <parent> <groupId>at.tuwien</groupId> <artifactId>dbrepo-data-service</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>rest-service</artifactId> <name>dbrepo-data-service-rest-service</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies> <dependency> <groupId>at.tuwien</groupId> <artifactId>services</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> </dependency> </dependencies> diff --git a/dbrepo-data-service/services/pom.xml b/dbrepo-data-service/services/pom.xml index 666cda76a4..d0fe72cbeb 100644 --- a/dbrepo-data-service/services/pom.xml +++ b/dbrepo-data-service/services/pom.xml @@ -6,18 +6,18 @@ <parent> <groupId>at.tuwien</groupId> <artifactId>dbrepo-data-service</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>services</artifactId> <name>dbrepo-data-service-services</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies> <dependency> <groupId>at.tuwien</groupId> <artifactId>dbrepo-data-service-querystore</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> </dependency> </dependencies> diff --git a/dbrepo-metadata-service/api/pom.xml b/dbrepo-metadata-service/api/pom.xml index 9baf18ff69..c1e74c5ae9 100644 --- a/dbrepo-metadata-service/api/pom.xml +++ b/dbrepo-metadata-service/api/pom.xml @@ -6,18 +6,18 @@ <parent> <groupId>at.tuwien</groupId> <artifactId>dbrepo-metadata-service</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>dbrepo-metadata-service-api</artifactId> <name>dbrepo-metadata-service-api</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies> <dependency> <groupId>at.tuwien</groupId> <artifactId>dbrepo-metadata-service-entities</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> <scope>compile</scope> </dependency> </dependencies> diff --git a/dbrepo-metadata-service/entities/pom.xml b/dbrepo-metadata-service/entities/pom.xml index 5b1c1d0cf1..9252dd2caa 100644 --- a/dbrepo-metadata-service/entities/pom.xml +++ b/dbrepo-metadata-service/entities/pom.xml @@ -6,12 +6,12 @@ <parent> <groupId>at.tuwien</groupId> <artifactId>dbrepo-metadata-service</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>dbrepo-metadata-service-entities</artifactId> <name>dbrepo-metadata-service-entity</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies/> diff --git a/dbrepo-metadata-service/oai/pom.xml b/dbrepo-metadata-service/oai/pom.xml index a3778f0363..87da814d41 100644 --- a/dbrepo-metadata-service/oai/pom.xml +++ b/dbrepo-metadata-service/oai/pom.xml @@ -6,12 +6,12 @@ <parent> <groupId>at.tuwien</groupId> <artifactId>dbrepo-metadata-service</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>dbrepo-metadata-service-oai</artifactId> <name>dbrepo-metadata-service-oai</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies/> diff --git a/dbrepo-metadata-service/pom.xml b/dbrepo-metadata-service/pom.xml index 2803d9b5f3..04af8a795f 100644 --- a/dbrepo-metadata-service/pom.xml +++ b/dbrepo-metadata-service/pom.xml @@ -11,7 +11,7 @@ <groupId>at.tuwien</groupId> <artifactId>dbrepo-metadata-service</artifactId> <name>dbrepo-metadata-service</name> - <version>1.6.1</version> + <version>1.6.2</version> <description>Service that manages the metadata</description> diff --git a/dbrepo-metadata-service/report/pom.xml b/dbrepo-metadata-service/report/pom.xml index 6234a844d2..756681f202 100644 --- a/dbrepo-metadata-service/report/pom.xml +++ b/dbrepo-metadata-service/report/pom.xml @@ -6,12 +6,12 @@ <parent> <artifactId>dbrepo-metadata-service</artifactId> <groupId>at.tuwien</groupId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>dbrepo-metadata-service-report</artifactId> <name>dbrepo-metadata-service-report</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies> <dependency> diff --git a/dbrepo-metadata-service/repositories/pom.xml b/dbrepo-metadata-service/repositories/pom.xml index 44fde031c1..39e971b901 100644 --- a/dbrepo-metadata-service/repositories/pom.xml +++ b/dbrepo-metadata-service/repositories/pom.xml @@ -6,12 +6,12 @@ <parent> <artifactId>dbrepo-metadata-service</artifactId> <groupId>at.tuwien</groupId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>dbrepo-metadata-service-repositories</artifactId> <name>dbrepo-metadata-service-repositories</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies> <dependency> diff --git a/dbrepo-metadata-service/rest-service/pom.xml b/dbrepo-metadata-service/rest-service/pom.xml index 233d2ac465..9f8055a149 100644 --- a/dbrepo-metadata-service/rest-service/pom.xml +++ b/dbrepo-metadata-service/rest-service/pom.xml @@ -6,12 +6,12 @@ <parent> <artifactId>dbrepo-metadata-service</artifactId> <groupId>at.tuwien</groupId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>dbrepo-metadata-service-rest-service</artifactId> <name>dbrepo-metadata-service-rest</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies> <dependency> diff --git a/dbrepo-metadata-service/services/pom.xml b/dbrepo-metadata-service/services/pom.xml index 2961f680d1..0ec2d62d1d 100644 --- a/dbrepo-metadata-service/services/pom.xml +++ b/dbrepo-metadata-service/services/pom.xml @@ -6,12 +6,12 @@ <parent> <artifactId>dbrepo-metadata-service</artifactId> <groupId>at.tuwien</groupId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>dbrepo-metadata-service-services</artifactId> <name>dbrepo-metadata-service-services</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies> <dependency> diff --git a/dbrepo-metadata-service/test/pom.xml b/dbrepo-metadata-service/test/pom.xml index c58104714a..97768ad4a7 100644 --- a/dbrepo-metadata-service/test/pom.xml +++ b/dbrepo-metadata-service/test/pom.xml @@ -6,12 +6,12 @@ <parent> <groupId>at.tuwien</groupId> <artifactId>dbrepo-metadata-service</artifactId> - <version>1.6.1</version> + <version>1.6.2</version> </parent> <artifactId>dbrepo-metadata-service-test</artifactId> <name>dbrepo-metadata-service-test</name> - <version>1.6.1</version> + <version>1.6.2</version> <dependencies> <dependency> diff --git a/dbrepo-search-service/Pipfile b/dbrepo-search-service/Pipfile index ec74a381be..f7161287a0 100644 --- a/dbrepo-search-service/Pipfile +++ b/dbrepo-search-service/Pipfile @@ -18,7 +18,7 @@ jwt = "~=1.3" testcontainers-opensearch = "*" pytest = "*" rdflib = "*" -dbrepo = {path = "./lib/dbrepo-1.6.1.tar.gz"} +dbrepo = {path = "./lib/dbrepo-1.6.2.tar.gz"} gunicorn = "*" [dev-packages] diff --git a/dbrepo-search-service/Pipfile.lock b/dbrepo-search-service/Pipfile.lock index c0508dd3da..c789904077 100644 --- a/dbrepo-search-service/Pipfile.lock +++ b/dbrepo-search-service/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "a0682b0583cfc91d643a307a7dce7a524e7f7c29dbf2c9c5e9a6f16eb5f5ee91" + "sha256": "729017f537f9f8fb6dcc15703392c7fd79aec494feba4c107e7a1888e8ea955d" }, "pipfile-spec": 6, "requires": { @@ -360,9 +360,9 @@ }, "dbrepo": { "hashes": [ - "sha256:a08b6eb49c108466b231c1b2cae5be501043fe4208a782899ce103105e22e3c6" + "sha256:501b53c7e4b32774809f9685a18288da5b938fc1512e94d8b248f531ee8667fc" ], - "path": "./lib/dbrepo-1.6.1.tar.gz" + "path": "./lib/dbrepo-1.6.2.tar.gz" }, "docker": { "hashes": [ @@ -845,64 +845,64 @@ }, "numpy": { "hashes": [ - "sha256:059e6a747ae84fce488c3ee397cee7e5f905fd1bda5fb18c66bc41807ff119b2", - "sha256:08ef779aed40dbc52729d6ffe7dd51df85796a702afbf68a4f4e41fafdc8bda5", - "sha256:164a829b6aacf79ca47ba4814b130c4020b202522a93d7bff2202bfb33b61c60", - "sha256:26c9c4382b19fcfbbed3238a14abf7ff223890ea1936b8890f058e7ba35e8d71", - "sha256:27f5cdf9f493b35f7e41e8368e7d7b4bbafaf9660cba53fb21d2cd174ec09631", - "sha256:31b89fa67a8042e96715c68e071a1200c4e172f93b0fbe01a14c0ff3ff820fc8", - "sha256:32cb94448be47c500d2c7a95f93e2f21a01f1fd05dd2beea1ccd049bb6001cd2", - "sha256:360137f8fb1b753c5cde3ac388597ad680eccbbbb3865ab65efea062c4a1fd16", - "sha256:3683a8d166f2692664262fd4900f207791d005fb088d7fdb973cc8d663626faa", - "sha256:38efc1e56b73cc9b182fe55e56e63b044dd26a72128fd2fbd502f75555d92591", - "sha256:3d03883435a19794e41f147612a77a8f56d4e52822337844fff3d4040a142964", - "sha256:3ecc47cd7f6ea0336042be87d9e7da378e5c7e9b3c8ad0f7c966f714fc10d821", - "sha256:40f9e544c1c56ba8f1cf7686a8c9b5bb249e665d40d626a23899ba6d5d9e1484", - "sha256:4250888bcb96617e00bfa28ac24850a83c9f3a16db471eca2ee1f1714df0f957", - "sha256:4511d9e6071452b944207c8ce46ad2f897307910b402ea5fa975da32e0102800", - "sha256:45681fd7128c8ad1c379f0ca0776a8b0c6583d2f69889ddac01559dfe4390918", - "sha256:48fd472630715e1c1c89bf1feab55c29098cb403cc184b4859f9c86d4fcb6a95", - "sha256:4c86e2a209199ead7ee0af65e1d9992d1dce7e1f63c4b9a616500f93820658d0", - "sha256:4dfda918a13cc4f81e9118dea249e192ab167a0bb1966272d5503e39234d694e", - "sha256:5062dc1a4e32a10dc2b8b13cedd58988261416e811c1dc4dbdea4f57eea61b0d", - "sha256:51faf345324db860b515d3f364eaa93d0e0551a88d6218a7d61286554d190d73", - "sha256:526fc406ab991a340744aad7e25251dd47a6720a685fa3331e5c59fef5282a59", - "sha256:53c09385ff0b72ba79d8715683c1168c12e0b6e84fb0372e97553d1ea91efe51", - "sha256:55ba24ebe208344aa7a00e4482f65742969a039c2acfcb910bc6fcd776eb4355", - "sha256:5b6c390bfaef8c45a260554888966618328d30e72173697e5cabe6b285fb2348", - "sha256:5c5cc0cbabe9452038ed984d05ac87910f89370b9242371bd9079cb4af61811e", - "sha256:5edb4e4caf751c1518e6a26a83501fda79bff41cc59dac48d70e6d65d4ec4440", - "sha256:61048b4a49b1c93fe13426e04e04fdf5a03f456616f6e98c7576144677598675", - "sha256:676f4eebf6b2d430300f1f4f4c2461685f8269f94c89698d832cdf9277f30b84", - "sha256:67d4cda6fa6ffa073b08c8372aa5fa767ceb10c9a0587c707505a6d426f4e046", - "sha256:694f9e921a0c8f252980e85bce61ebbd07ed2b7d4fa72d0e4246f2f8aa6642ab", - "sha256:733585f9f4b62e9b3528dd1070ec4f52b8acf64215b60a845fa13ebd73cd0712", - "sha256:7671dc19c7019103ca44e8d94917eba8534c76133523ca8406822efdd19c9308", - "sha256:780077d95eafc2ccc3ced969db22377b3864e5b9a0ea5eb347cc93b3ea900315", - "sha256:7ba9cc93a91d86365a5d270dee221fdc04fb68d7478e6bf6af650de78a8339e3", - "sha256:89b16a18e7bba224ce5114db863e7029803c179979e1af6ad6a6b11f70545008", - "sha256:9036d6365d13b6cbe8f27a0eaf73ddcc070cae584e5ff94bb45e3e9d729feab5", - "sha256:93cf4e045bae74c90ca833cba583c14b62cb4ba2cba0abd2b141ab52548247e2", - "sha256:9ad014faa93dbb52c80d8f4d3dcf855865c876c9660cb9bd7553843dd03a4b1e", - "sha256:9b1d07b53b78bf84a96898c1bc139ad7f10fda7423f5fd158fd0f47ec5e01ac7", - "sha256:a7746f235c47abc72b102d3bce9977714c2444bdfaea7888d241b4c4bb6a78bf", - "sha256:aa3017c40d513ccac9621a2364f939d39e550c542eb2a894b4c8da92b38896ab", - "sha256:b34d87e8a3090ea626003f87f9392b3929a7bbf4104a05b6667348b6bd4bf1cd", - "sha256:b541032178a718c165a49638d28272b771053f628382d5e9d1c93df23ff58dbf", - "sha256:ba5511d8f31c033a5fcbda22dd5c813630af98c70b2661f2d2c654ae3cdfcfc8", - "sha256:bc8a37ad5b22c08e2dbd27df2b3ef7e5c0864235805b1e718a235bcb200cf1cb", - "sha256:bff7d8ec20f5f42607599f9994770fa65d76edca264a87b5e4ea5629bce12268", - "sha256:c1ad395cf254c4fbb5b2132fee391f361a6e8c1adbd28f2cd8e79308a615fe9d", - "sha256:f1d09e520217618e76396377c81fba6f290d5f926f50c35f3a5f72b01a0da780", - "sha256:f3eac17d9ec51be534685ba877b6ab5edc3ab7ec95c8f163e5d7b39859524716", - "sha256:f419290bc8968a46c4933158c91a0012b7a99bb2e465d5ef5293879742f8797e", - "sha256:f62aa6ee4eb43b024b0e5a01cf65a0bb078ef8c395e8713c6e8a12a697144528", - "sha256:f74e6fdeb9a265624ec3a3918430205dff1df7e95a230779746a6af78bc615af", - "sha256:f9b57eaa3b0cd8db52049ed0330747b0364e899e8a606a624813452b8203d5f7", - "sha256:fce4f615f8ca31b2e61aa0eb5865a21e14f5629515c9151850aa936c02a1ee51" + "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f", + "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0", + "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd", + "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2", + "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4", + "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648", + "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be", + "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb", + "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160", + "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd", + "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a", + "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84", + "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e", + "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748", + "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825", + "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60", + "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957", + "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715", + "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317", + "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e", + "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283", + "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278", + "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9", + "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de", + "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369", + "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb", + "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189", + "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014", + "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323", + "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e", + "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49", + "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50", + "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d", + "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37", + "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39", + "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576", + "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a", + "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba", + "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7", + "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826", + "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467", + "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495", + "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc", + "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391", + "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0", + "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97", + "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c", + "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac", + "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369", + "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8", + "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2", + "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff", + "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a", + "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df", + "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f" ], "markers": "python_version == '3.11'", - "version": "==2.2.1" + "version": "==2.2.2" }, "opensearch-py": { "hashes": [ @@ -1321,12 +1321,12 @@ }, "rdflib": { "hashes": [ - "sha256:4fc8f6d50b199dc38fbc5256370f038c1cedca6102ccbde4e37c0fd2b7f36e65", - "sha256:5a694a64f48a751079999c37dccf91a6210077d845d09adf7c3ce23a876265a7" + "sha256:5402310a9f0f3c07d453d73fd0ad6ba35616286fe95d3670db2b725f3f539673", + "sha256:f3dcb4c106a8cd9e060d92f43d593d09ebc3d07adc244f4c7315856a12e383ee" ], "index": "pypi", - "markers": "python_version >= '3.9' and python_version < '4'", - "version": "==7.1.2" + "markers": "python_full_version >= '3.8.1' and python_full_version < '4.0.0'", + "version": "==7.1.3" }, "referencing": { "hashes": [ diff --git a/dbrepo-search-service/init/Pipfile b/dbrepo-search-service/init/Pipfile index 77bab3e84c..8ded635ab7 100644 --- a/dbrepo-search-service/init/Pipfile +++ b/dbrepo-search-service/init/Pipfile @@ -9,7 +9,7 @@ opensearch-py = "~=2.2" python-dotenv = "~=1.0" testcontainers-opensearch = "*" pytest = "*" -dbrepo = {path = "./lib/dbrepo-1.6.1.tar.gz"} +dbrepo = {path = "./lib/dbrepo-1.6.2.tar.gz"} rdflib = "*" [dev-packages] diff --git a/dbrepo-search-service/init/Pipfile.lock b/dbrepo-search-service/init/Pipfile.lock index bf53ace7e7..64f5fc6cc6 100644 --- a/dbrepo-search-service/init/Pipfile.lock +++ b/dbrepo-search-service/init/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "9edba52503b8604b267d52e41954beba012143b1e47f56aaae553cdcaf054e55" + "sha256": "39898ff53a7a701c750b2fc2bfe2d7f72704100e41a183feceb1d8bd09c71a00" }, "pipfile-spec": 6, "requires": { @@ -254,10 +254,10 @@ }, "dbrepo": { "hashes": [ - "sha256:251f3c2088bbd289cee86d5394b1e62e29aa081f994dd0845d895e3330f6a106" + "sha256:501b53c7e4b32774809f9685a18288da5b938fc1512e94d8b248f531ee8667fc" ], - "path": "./lib/dbrepo-1.6.1.tar.gz", - "version": "==1.6.1" + "path": "./lib/dbrepo-1.6.2.tar.gz", + "version": "==1.6.2" }, "docker": { "hashes": [ @@ -578,64 +578,64 @@ }, "numpy": { "hashes": [ - "sha256:059e6a747ae84fce488c3ee397cee7e5f905fd1bda5fb18c66bc41807ff119b2", - "sha256:08ef779aed40dbc52729d6ffe7dd51df85796a702afbf68a4f4e41fafdc8bda5", - "sha256:164a829b6aacf79ca47ba4814b130c4020b202522a93d7bff2202bfb33b61c60", - "sha256:26c9c4382b19fcfbbed3238a14abf7ff223890ea1936b8890f058e7ba35e8d71", - "sha256:27f5cdf9f493b35f7e41e8368e7d7b4bbafaf9660cba53fb21d2cd174ec09631", - "sha256:31b89fa67a8042e96715c68e071a1200c4e172f93b0fbe01a14c0ff3ff820fc8", - "sha256:32cb94448be47c500d2c7a95f93e2f21a01f1fd05dd2beea1ccd049bb6001cd2", - "sha256:360137f8fb1b753c5cde3ac388597ad680eccbbbb3865ab65efea062c4a1fd16", - "sha256:3683a8d166f2692664262fd4900f207791d005fb088d7fdb973cc8d663626faa", - "sha256:38efc1e56b73cc9b182fe55e56e63b044dd26a72128fd2fbd502f75555d92591", - "sha256:3d03883435a19794e41f147612a77a8f56d4e52822337844fff3d4040a142964", - "sha256:3ecc47cd7f6ea0336042be87d9e7da378e5c7e9b3c8ad0f7c966f714fc10d821", - "sha256:40f9e544c1c56ba8f1cf7686a8c9b5bb249e665d40d626a23899ba6d5d9e1484", - "sha256:4250888bcb96617e00bfa28ac24850a83c9f3a16db471eca2ee1f1714df0f957", - "sha256:4511d9e6071452b944207c8ce46ad2f897307910b402ea5fa975da32e0102800", - "sha256:45681fd7128c8ad1c379f0ca0776a8b0c6583d2f69889ddac01559dfe4390918", - "sha256:48fd472630715e1c1c89bf1feab55c29098cb403cc184b4859f9c86d4fcb6a95", - "sha256:4c86e2a209199ead7ee0af65e1d9992d1dce7e1f63c4b9a616500f93820658d0", - "sha256:4dfda918a13cc4f81e9118dea249e192ab167a0bb1966272d5503e39234d694e", - "sha256:5062dc1a4e32a10dc2b8b13cedd58988261416e811c1dc4dbdea4f57eea61b0d", - "sha256:51faf345324db860b515d3f364eaa93d0e0551a88d6218a7d61286554d190d73", - "sha256:526fc406ab991a340744aad7e25251dd47a6720a685fa3331e5c59fef5282a59", - "sha256:53c09385ff0b72ba79d8715683c1168c12e0b6e84fb0372e97553d1ea91efe51", - "sha256:55ba24ebe208344aa7a00e4482f65742969a039c2acfcb910bc6fcd776eb4355", - "sha256:5b6c390bfaef8c45a260554888966618328d30e72173697e5cabe6b285fb2348", - "sha256:5c5cc0cbabe9452038ed984d05ac87910f89370b9242371bd9079cb4af61811e", - "sha256:5edb4e4caf751c1518e6a26a83501fda79bff41cc59dac48d70e6d65d4ec4440", - "sha256:61048b4a49b1c93fe13426e04e04fdf5a03f456616f6e98c7576144677598675", - "sha256:676f4eebf6b2d430300f1f4f4c2461685f8269f94c89698d832cdf9277f30b84", - "sha256:67d4cda6fa6ffa073b08c8372aa5fa767ceb10c9a0587c707505a6d426f4e046", - "sha256:694f9e921a0c8f252980e85bce61ebbd07ed2b7d4fa72d0e4246f2f8aa6642ab", - "sha256:733585f9f4b62e9b3528dd1070ec4f52b8acf64215b60a845fa13ebd73cd0712", - "sha256:7671dc19c7019103ca44e8d94917eba8534c76133523ca8406822efdd19c9308", - "sha256:780077d95eafc2ccc3ced969db22377b3864e5b9a0ea5eb347cc93b3ea900315", - "sha256:7ba9cc93a91d86365a5d270dee221fdc04fb68d7478e6bf6af650de78a8339e3", - "sha256:89b16a18e7bba224ce5114db863e7029803c179979e1af6ad6a6b11f70545008", - "sha256:9036d6365d13b6cbe8f27a0eaf73ddcc070cae584e5ff94bb45e3e9d729feab5", - "sha256:93cf4e045bae74c90ca833cba583c14b62cb4ba2cba0abd2b141ab52548247e2", - "sha256:9ad014faa93dbb52c80d8f4d3dcf855865c876c9660cb9bd7553843dd03a4b1e", - "sha256:9b1d07b53b78bf84a96898c1bc139ad7f10fda7423f5fd158fd0f47ec5e01ac7", - "sha256:a7746f235c47abc72b102d3bce9977714c2444bdfaea7888d241b4c4bb6a78bf", - "sha256:aa3017c40d513ccac9621a2364f939d39e550c542eb2a894b4c8da92b38896ab", - "sha256:b34d87e8a3090ea626003f87f9392b3929a7bbf4104a05b6667348b6bd4bf1cd", - "sha256:b541032178a718c165a49638d28272b771053f628382d5e9d1c93df23ff58dbf", - "sha256:ba5511d8f31c033a5fcbda22dd5c813630af98c70b2661f2d2c654ae3cdfcfc8", - "sha256:bc8a37ad5b22c08e2dbd27df2b3ef7e5c0864235805b1e718a235bcb200cf1cb", - "sha256:bff7d8ec20f5f42607599f9994770fa65d76edca264a87b5e4ea5629bce12268", - "sha256:c1ad395cf254c4fbb5b2132fee391f361a6e8c1adbd28f2cd8e79308a615fe9d", - "sha256:f1d09e520217618e76396377c81fba6f290d5f926f50c35f3a5f72b01a0da780", - "sha256:f3eac17d9ec51be534685ba877b6ab5edc3ab7ec95c8f163e5d7b39859524716", - "sha256:f419290bc8968a46c4933158c91a0012b7a99bb2e465d5ef5293879742f8797e", - "sha256:f62aa6ee4eb43b024b0e5a01cf65a0bb078ef8c395e8713c6e8a12a697144528", - "sha256:f74e6fdeb9a265624ec3a3918430205dff1df7e95a230779746a6af78bc615af", - "sha256:f9b57eaa3b0cd8db52049ed0330747b0364e899e8a606a624813452b8203d5f7", - "sha256:fce4f615f8ca31b2e61aa0eb5865a21e14f5629515c9151850aa936c02a1ee51" + "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f", + "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0", + "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd", + "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2", + "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4", + "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648", + "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be", + "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb", + "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160", + "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd", + "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a", + "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84", + "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e", + "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748", + "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825", + "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60", + "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957", + "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715", + "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317", + "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e", + "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283", + "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278", + "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9", + "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de", + "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369", + "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb", + "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189", + "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014", + "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323", + "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e", + "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49", + "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50", + "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d", + "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37", + "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39", + "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576", + "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a", + "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba", + "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7", + "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826", + "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467", + "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495", + "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc", + "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391", + "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0", + "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97", + "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c", + "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac", + "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369", + "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8", + "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2", + "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff", + "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a", + "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df", + "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f" ], "markers": "python_version == '3.11'", - "version": "==2.2.1" + "version": "==2.2.2" }, "opensearch-py": { "hashes": [ @@ -940,7 +940,7 @@ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.9.0.post0" }, "python-dotenv": { @@ -960,11 +960,11 @@ }, "rdflib": { "hashes": [ - "sha256:4fc8f6d50b199dc38fbc5256370f038c1cedca6102ccbde4e37c0fd2b7f36e65", - "sha256:5a694a64f48a751079999c37dccf91a6210077d845d09adf7c3ce23a876265a7" + "sha256:5402310a9f0f3c07d453d73fd0ad6ba35616286fe95d3670db2b725f3f539673", + "sha256:f3dcb4c106a8cd9e060d92f43d593d09ebc3d07adc244f4c7315856a12e383ee" ], "index": "pypi", - "version": "==7.1.2" + "version": "==7.1.3" }, "requests": { "hashes": [ @@ -979,7 +979,7 @@ "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.17.0" }, "testcontainers-core": { diff --git a/dbrepo-search-service/init/lib/dbrepo-1.6.0.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.6.0.tar.gz deleted file mode 100644 index 80c2ba74f662e7b02895122a37e301fde2157b82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39925 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 diff --git a/dbrepo-search-service/init/lib/dbrepo-1.6.1.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.6.1.tar.gz deleted file mode 100644 index 7914db1bb84dddf85611cda3b766c0c0cdc094c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40022 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 diff --git a/dbrepo-search-service/init/lib/dbrepo-1.6.2.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.6.2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..58081673e955d89fccf70c9161037a725b647f71 GIT binary patch literal 40056 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 literal 0 HcmV?d00001 diff --git a/dbrepo-search-service/lib/dbrepo-1.6.0.tar.gz b/dbrepo-search-service/lib/dbrepo-1.6.0.tar.gz deleted file mode 100644 index 80c2ba74f662e7b02895122a37e301fde2157b82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39925 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 diff --git a/dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz b/dbrepo-search-service/lib/dbrepo-1.6.1.tar.gz deleted file mode 100644 index 7914db1bb84dddf85611cda3b766c0c0cdc094c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40022 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 diff --git a/dbrepo-search-service/lib/dbrepo-1.6.2.tar.gz b/dbrepo-search-service/lib/dbrepo-1.6.2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..58081673e955d89fccf70c9161037a725b647f71 GIT binary patch literal 40056 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 literal 0 HcmV?d00001 diff --git a/dbrepo-upload-service/pom.xml b/dbrepo-upload-service/pom.xml index f2bea09236..8f4506e150 100644 --- a/dbrepo-upload-service/pom.xml +++ b/dbrepo-upload-service/pom.xml @@ -11,7 +11,7 @@ <groupId>at.tuwien</groupId> <artifactId>dbrepo-upload-service</artifactId> <name>dbrepo-upload-service</name> - <version>1.6.1</version> + <version>1.6.2</version> <url>https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.6/</url> <developers> diff --git a/helm/dbrepo/Chart.lock b/helm/dbrepo/Chart.lock index b18ee6f5f6..4d2cafe9bd 100644 --- a/helm/dbrepo/Chart.lock +++ b/helm/dbrepo/Chart.lock @@ -4,7 +4,7 @@ dependencies: version: 1.4.0 - name: keycloak repository: https://charts.bitnami.com/bitnami - version: 21.6.1 + version: 21.6.2 - name: mariadb-galera repository: https://charts.bitnami.com/bitnami version: 13.2.7 diff --git a/helm/dbrepo/Chart.yaml b/helm/dbrepo/Chart.yaml index 22d1865df5..802f33888c 100644 --- a/helm/dbrepo/Chart.yaml +++ b/helm/dbrepo/Chart.yaml @@ -7,8 +7,8 @@ description: Helm Chart for installing DBRepo sources: - https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services type: application -version: "1.6.1" -appVersion: "1.6.1" +version: "1.6.2" +appVersion: "1.6.2" keywords: - dbrepo maintainers: diff --git a/helm/dbrepo/README.md b/helm/dbrepo/README.md index 7c613aaebc..e703787206 100644 --- a/helm/dbrepo/README.md +++ b/helm/dbrepo/README.md @@ -11,7 +11,7 @@ sample [ for your deployment and update the variables, especially `hostname`. ```bash -helm install my-release "oci://registry.datalab.tuwien.ac.at/dbrepo/helm/dbrepo" --values ./values.yaml --version "1.6.1" +helm install my-release "oci://registry.datalab.tuwien.ac.at/dbrepo/helm/dbrepo" --values ./values.yaml --version "1.6.2" ``` ## Prerequisites @@ -28,7 +28,7 @@ helm install my-release "oci://registry.datalab.tuwien.ac.at/dbrepo/helm/dbrepo" To install the chart with the release name `my-release`: ```bash -helm install my-release "oci://oci://registry.datalab.tuwien.ac.at/dbrepo/helm" --values ./values.yaml --version "1.6.1" +helm install my-release "oci://oci://registry.datalab.tuwien.ac.at/dbrepo/helm" --values ./values.yaml --version "1.6.2" ``` The command deploys DBRepo on the Kubernetes cluster in the default configuration. The Parameters section lists the diff --git a/helm/dbrepo/values.yaml b/helm/dbrepo/values.yaml index 34db1f569f..84cf7b5ddd 100644 --- a/helm/dbrepo/values.yaml +++ b/helm/dbrepo/values.yaml @@ -116,7 +116,7 @@ authservice: setupJob: image: ## @skip authservice.setupJob.image.name - name: registry.datalab.tuwien.ac.at/dbrepo/auth-service-init:1.6.1 + name: registry.datalab.tuwien.ac.at/dbrepo/auth-service-init:1.6.2 ## @param authservice.setupJob.resourcesPreset The container resource preset resourcesPreset: "nano" ## @param authservice.setupJob.resources Set container requests and limits for different resources like CPU or memory (essential for production workloads) @@ -392,7 +392,7 @@ analyseservice: enabled: true image: ## @skip analyseservice.image.name - name: registry.datalab.tuwien.ac.at/dbrepo/analyse-service:1.6.1 + name: registry.datalab.tuwien.ac.at/dbrepo/analyse-service:1.6.2 ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod podSecurityContext: ## @param analyseservice.podSecurityContext.enabled Enable pods' Security Context @@ -453,7 +453,7 @@ metadataservice: enabled: true image: ## @skip metadataservice.image.name - name: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.1 + name: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.2 ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod podSecurityContext: ## @param metadataservice.podSecurityContext.enabled Enable pods' Security Context @@ -550,7 +550,7 @@ dataservice: endpoint: http://data-service image: ## @skip dataservice.image.name - name: registry.datalab.tuwien.ac.at/dbrepo/data-service:1.6.1 + name: registry.datalab.tuwien.ac.at/dbrepo/data-service:1.6.2 ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod podSecurityContext: ## @param dataservice.podSecurityContext.enabled Enable pods' Security Context @@ -636,7 +636,7 @@ searchservice: endpoint: http://search-service image: ## @skip searchservice.image.name - name: registry.datalab.tuwien.ac.at/dbrepo/search-service:1.6.1 + name: registry.datalab.tuwien.ac.at/dbrepo/search-service:1.6.2 ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod podSecurityContext: ## @param searchservice.podSecurityContext.enabled Enable pods' Security Context @@ -683,7 +683,7 @@ searchservice: init: image: ## @skip searchservice.init.image.name - name: registry.datalab.tuwien.ac.at/dbrepo/search-service-init:1.6.1 + name: registry.datalab.tuwien.ac.at/dbrepo/search-service-init:1.6.2 ## @param searchservice.init.resourcesPreset The container resource preset resourcesPreset: "nano" ## @param searchservice.init.resources Set container requests and limits for different resources like CPU or memory (essential for production workloads) @@ -744,7 +744,7 @@ storageservice: init: image: ## @skip storageservice.init.image.name - name: registry.datalab.tuwien.ac.at/dbrepo/storage-service-init:1.6.1 + name: registry.datalab.tuwien.ac.at/dbrepo/storage-service-init:1.6.2 s3: ## @param storageservice.init.s3.endpoint The S3-capable endpoint the microservice connects to. endpoint: http://storage-service-s3:8333 @@ -853,7 +853,7 @@ ui: enabled: true image: ## @skip ui.image.name - name: registry.datalab.tuwien.ac.at/dbrepo/ui:1.6.1 + name: registry.datalab.tuwien.ac.at/dbrepo/ui:1.6.2 ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod podSecurityContext: ## @param ui.podSecurityContext.enabled Enable pods' Security Context diff --git a/install.sh b/install.sh index 710e9d55a1..3ccfd30b21 100644 --- a/install.sh +++ b/install.sh @@ -1,7 +1,7 @@ #!/bin/bash # preset -VERSION="1.6.1" +VERSION="1.6.2" MIN_CPU=8 MIN_RAM=4 MIN_MAP_COUNT=262144 diff --git a/lib/python/docs/index.rst b/lib/python/docs/index.rst index 0a989f321f..13561e9c13 100644 --- a/lib/python/docs/index.rst +++ b/lib/python/docs/index.rst @@ -6,7 +6,7 @@ Pandas `DataFrame <https://pandas.pydata.org/docs/reference/api/pandas.DataFrame provides an object-oriented API as well as low-level access to DBRepo services. .. note:: - The SDK has been implemented and documented for DBRepo version 1.6.1, earlier versions may be supported but are not tested for compatibility. + The SDK has been implemented and documented for DBRepo version 1.6.2, earlier versions may be supported but are not tested for compatibility. Quickstart ---------- diff --git a/lib/python/pyproject.toml b/lib/python/pyproject.toml index 33d1e52cc0..5b8deb8408 100644 --- a/lib/python/pyproject.toml +++ b/lib/python/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dbrepo" -version = "1.6.1" +version = "1.6.2" description = "DBRepo Python Library" keywords = [ "DBRepo", diff --git a/lib/python/setup.py b/lib/python/setup.py index 027b8a5bb6..c6deff531d 100644 --- a/lib/python/setup.py +++ b/lib/python/setup.py @@ -2,7 +2,7 @@ from distutils.core import setup setup(name="dbrepo", - version="1.6.1", + version="1.6.2", description="A library for communicating with DBRepo", url="https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.6/", author="Martin Weise", diff --git a/sonar-project.properties b/sonar-project.properties index d354d54608..83f00a3a24 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -2,7 +2,7 @@ sonar.projectKey=fair-data-austria-db-repository_fda-services_a57fa043-ab99-4cdd-a721-162d9a916d77 sonar.host.url=https://s39.datalab.tuwien.ac.at # project -sonar.projectVersion=1.6.1 +sonar.projectVersion=1.6.2 # general sonar.qualitygate.wait=true sonar.projectCreation.mainBranchName=master -- GitLab From 49c41d420ea479b6a09d6864b69529b2648e629f Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Wed, 22 Jan 2025 10:52:40 +0100 Subject: [PATCH 02/19] It builds somehow Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- dbrepo-analyse-service/Pipfile.lock | 6 +- .../lib/dbrepo-1.6.2.tar.gz | Bin 40056 -> 40094 bytes .../at/tuwien/endpoints/AccessEndpoint.java | 18 +- .../at/tuwien/endpoints/DatabaseEndpoint.java | 32 +- ...bstractEndpoint.java => RestEndpoint.java} | 2 +- .../at/tuwien/endpoints/SubsetEndpoint.java | 25 +- .../at/tuwien/endpoints/TableEndpoint.java | 58 +- .../at/tuwien/endpoints/ViewEndpoint.java | 41 +- .../tuwien/validation/EndpointValidator.java | 14 +- .../java/at/tuwien/config/MariaDbConfig.java | 223 +----- .../endpoint/AccessEndpointUnitTest.java | 36 +- .../endpoint/DatabaseEndpointUnitTest.java | 60 +- .../endpoint/SubsetEndpointUnitTest.java | 178 ++--- .../endpoint/TableEndpointUnitTest.java | 195 ++--- .../tuwien/endpoint/ViewEndpointUnitTest.java | 62 +- .../MetadataServiceGatewayUnitTest.java | 129 +--- .../DefaultListenerIntegrationTest.java | 6 +- .../listener/DefaultListenerUnitTest.java | 6 +- .../at/tuwien/mvc/SubsetEndpointMvcTest.java | 12 +- .../service/AccessServiceIntegrationTest.java | 36 +- .../ContainerServiceIntegrationTest.java | 109 +++ .../service/CredentialServiceUnitTest.java | 96 +-- .../DatabaseServiceIntegrationTest.java | 676 +++++++++++++++++- .../service/QueueServiceIntegrationTest.java | 14 +- .../service/SchemaServiceIntegrationTest.java | 416 ----------- .../service/SubsetServiceIntegrationTest.java | 56 +- .../service/TableServiceIntegrationTest.java | 377 ++-------- .../service/ViewServiceIntegrationTest.java | 45 +- .../java/at/tuwien/config/CacheConfig.java | 30 +- .../gateway/MetadataServiceGateway.java | 31 +- .../impl/MetadataServiceGatewayImpl.java | 120 ++-- .../at/tuwien/listener/DefaultListener.java | 8 +- .../java/at/tuwien/mapper/MariaDbMapper.java | 12 +- .../java/at/tuwien/mapper/MetadataMapper.java | 28 +- .../java/at/tuwien/service/AccessService.java | 11 +- .../at/tuwien/service/ContainerService.java | 33 + .../at/tuwien/service/CredentialService.java | 20 +- .../at/tuwien/service/DatabaseService.java | 85 ++- .../java/at/tuwien/service/QueueService.java | 4 +- .../java/at/tuwien/service/SchemaService.java | 33 - .../java/at/tuwien/service/SubsetService.java | 36 +- .../java/at/tuwien/service/TableService.java | 64 +- .../java/at/tuwien/service/ViewService.java | 50 +- .../impl/AccessServiceMariaDbImpl.java | 20 +- .../impl/ContainerServiceMariaDbImpl.java | 103 +++ .../service/impl/CredentialServiceImpl.java | 60 +- .../at/tuwien/service/impl/DataConnector.java | 66 ++ .../impl/DatabaseServiceMariaDbImpl.java | 341 ++++++++- .../service/impl/HibernateConnector.java | 53 -- .../impl/QueueServiceRabbitMqImpl.java | 10 +- .../impl/SchemaServiceMariaDbImpl.java | 164 ----- .../impl/SubsetServiceMariaDbImpl.java | 88 +-- .../service/impl/TableServiceMariaDbImpl.java | 169 ++--- .../service/impl/ViewServiceMariaDbImpl.java | 168 +---- .../main/java/at/tuwien/api/CacheableDto.java | 45 ++ .../at/tuwien/api/PrivilegedObjectDto.java | 18 - .../at/tuwien/api/container/ContainerDto.java | 25 +- .../internal/PrivilegedContainerDto.java | 60 -- .../tuwien/api/database/DatabaseBriefDto.java | 3 + .../at/tuwien/api/database/DatabaseDto.java | 52 +- .../java/at/tuwien/api/database/ViewDto.java | 36 +- .../internal/PrivilegedDatabaseDto.java | 88 --- .../database/internal/PrivilegedViewDto.java | 77 -- .../tuwien/api/database/table/TableDto.java | 36 +- .../table/internal/PrivilegedTableDto.java | 115 --- .../api/identifier/IdentifierBriefDto.java | 2 - .../main/java/at/tuwien/api/user/UserDto.java | 14 +- .../api/user/internal/PrivilegedUserDto.java | 58 -- .../java/at/tuwien/mapper/MetadataMapper.java | 72 +- .../at/tuwien/endpoints/DatabaseEndpoint.java | 76 +- .../at/tuwien/endpoints/TableEndpoint.java | 6 +- .../endpoints/DatabaseEndpointUnitTest.java | 8 +- .../gateway/DataServiceGatewayUnitTest.java | 14 +- .../gateway/SearchServiceGatewayUnitTest.java | 18 +- .../tuwien/mapper/MetadataMapperUnitTest.java | 6 - .../tuwien/service/AccessServiceUnitTest.java | 32 +- ...aCiteIdentifierServicePersistenceTest.java | 18 +- .../service/DatabaseServiceUnitTest.java | 16 +- .../IdentifierServicePersistenceTest.java | 4 +- .../service/TableServicePersistenceTest.java | 2 +- .../tuwien/service/TableServiceUnitTest.java | 20 +- .../service/ViewServicePersistenceTest.java | 2 +- .../tuwien/service/ViewServiceUnitTest.java | 4 +- .../tuwien/gateway/SearchServiceGateway.java | 4 +- .../impl/SearchServiceGatewayImpl.java | 8 +- .../java/at/tuwien/test/AbstractUnitTest.java | 50 +- .../main/java/at/tuwien/test/BaseTest.java | 134 ++-- dbrepo-search-service/Pipfile.lock | 2 +- dbrepo-search-service/init/Pipfile.lock | 17 +- .../init/lib/dbrepo-1.6.2.tar.gz | Bin 40056 -> 40094 bytes dbrepo-search-service/lib/dbrepo-1.6.2.tar.gz | Bin 40056 -> 40094 bytes .../table/[table_id]/settings.vue | 2 +- lib/python/dbrepo/api/dto.py | 46 +- 93 files changed, 2616 insertions(+), 3309 deletions(-) rename dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/{AbstractEndpoint.java => RestEndpoint.java} (98%) create mode 100644 dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java delete mode 100644 dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java create mode 100644 dbrepo-data-service/services/src/main/java/at/tuwien/service/ContainerService.java delete mode 100644 dbrepo-data-service/services/src/main/java/at/tuwien/service/SchemaService.java create mode 100644 dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceMariaDbImpl.java create mode 100644 dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java delete mode 100644 dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/HibernateConnector.java delete mode 100644 dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java create mode 100644 dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java delete mode 100644 dbrepo-metadata-service/api/src/main/java/at/tuwien/api/PrivilegedObjectDto.java delete mode 100644 dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/internal/PrivilegedContainerDto.java delete mode 100644 dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedDatabaseDto.java delete mode 100644 dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedViewDto.java delete mode 100644 dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/PrivilegedTableDto.java delete mode 100644 dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/PrivilegedUserDto.java diff --git a/dbrepo-analyse-service/Pipfile.lock b/dbrepo-analyse-service/Pipfile.lock index 9f00d97ca6..ec9b5f13d4 100644 --- a/dbrepo-analyse-service/Pipfile.lock +++ b/dbrepo-analyse-service/Pipfile.lock @@ -412,7 +412,7 @@ }, "dbrepo": { "hashes": [ - "sha256:501b53c7e4b32774809f9685a18288da5b938fc1512e94d8b248f531ee8667fc" + "sha256:19c6bbcf9461e20681f0fb342087c618a91123d2d04d4df2f4fd1da80aa77b76" ], "path": "./lib/dbrepo-1.6.2.tar.gz" }, @@ -1612,7 +1612,7 @@ "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" ], - "markers": "python_version >= '3.9'", + "markers": "python_version >= '3.10'", "version": "==2.3.0" }, "werkzeug": { @@ -2236,7 +2236,7 @@ "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" ], - "markers": "python_version >= '3.9'", + "markers": "python_version >= '3.10'", "version": "==2.3.0" }, "wrapt": { diff --git a/dbrepo-analyse-service/lib/dbrepo-1.6.2.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.6.2.tar.gz index 58081673e955d89fccf70c9161037a725b647f71..02ed2aec31c2b1881165a12d45060ed4a311192d 100644 GIT binary patch delta 39303 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 R=>>ny-)xbb%dmiz0RTX?Dir_# delta 39288 zcmbQYlj+9}rh55q4vxJE{pn2qQ<92O3-Wah_005)^hy$o7~bstU3J@SlEtk3UqvF` zo%H&}>w0&o%kDX`QHzrON~9-tsXzXx?#$w$#Ky?RplW>2=IYOL+<)f12?>qAutP{i z(QDgD{f!<A^sipMTKam`yIW<szw6h1_0RXYYq0nIyeFFv+eOybmzRCxKlJ`bO?i5L z`FrcPm&?6>^!#=BzTo%!eP_?#<G*FFp)IZUd&U1B4;~y}y!h^|i}&t5eE06)t@739 z!~Yw;vn_s-cW}w|Z`<})@A_I_Q_)dh-p^ltWq;Ax_i{4wZ{%g)zLAwb`0Kv)4clLS z)-lh1$=l7EmY+6#_P2lBf3L(b)&IY5?e*#ZU(x^ZT>tM+{!{<{_~OHh@9tfE>s;68 z`q_W}?<u{tC&zq#(qDO5nUoLz_b$Hs*ZIkRYj^A8)$B+9f9L;JzVC9`w4ML<2maf? z%=fp6TUB{N*7LCIB{jzD|L8A!9=&ey+pA@(Zrv|QUsEldo1gw%Ve=&g4mb7NxBL71 zP3!0GTDdc>{&rgI&)eA%8!zwKnUURl+W764SD)5g&5VuRw)J|r`K{dDkyrC(T{jO; z-@g3E4zH!>qTZhKeSKBeZXsJ)_N|#WH_K=<Jos}<s(slZ_mGQ!8uM=7x^P8Sw_zgN zGV?E{cMZ3!do8hM+O=Za+&8mBd8c(hmwK#cx9-!j4QuKXe>SGy?f<xQ+3#76v9sFB z>@VI*n{K*^`)&2R9rhRMuH2FPvthxU+p_$=>K%ulR&gHK^)BO4<X^`HJNx@yW-<tc zY-#FT*5BOtI<h{Xe$U)3QU@LQO<rty|1#j>%V5Pmc2<VX>u*O*p5@12SXR)$vUlan z*eq8E#*BtddnCoV3hEhjFQ>;cJ29W%x_Z{TnUC6i9l{Guew4gvkYjq^=6wC3?#HHE zi$!@Ntk)c#_3hSIj=qaGq<1<PDuz96>9$<;Axz6eB>wgWv&hiz7r(4BsFgb-#FS|H zraF{6X6@?3OOh?`aaFrYeAu$^-HTdbb^hO9kKbivU$e@V&qvDcjNRAO^})Bb(&TiP zsAZ%cX7b3W`qOB7Xj)Ym{{~B=y?SPQ3wM0}t5*7)sY>FJtMg-q9pVSLiy1CInYk}R zaMm7kSqG=<O)Go0tUDm-_jCQb9gA`uV*fk~HM<(w{$D_yf12U3Z8pA3`R_IS<Xo_| zhK1#UT+}{)7XOlCVvN4tO;>JRh!(G(rl7Mvan2>Tc{h!1`Bz=OEtmL5xy-PHm%DA* z<yAt$IhL{2J}VkrS6JpPxOwfup?isPwcEvQlq!E-QS|9zydj?5FZJl`2By%B|Ek04 z-(0MoA$B|D#__9v^BuT6FO;Sqns>0ICpSvo_Pp$&)9)P08L~DyG-%1ld%tR0@S^i< zef>9+*yI-rpRKyc_{l8ka7(K3rUJ*EEHd(G@*0fC82Xi&!*+`Id}%Dqm%p8uU7jH| zZ5`|6iB@m6ulsIzP(<#bqN7g@ho)`}(=_EaMR5jaS@~-MJp8`(7j&cdZ$J7g{6~=J zPEDbN@aIdGc<)o#A-#Z+%lq3g1J+hur`2*S8qxI}&P-AFj<E3@__KuH{ep1_%Qe1~ zb6QC!k7cAdou8jzc=O%kw2oHe%bVpGy7`y2aeUM+<T_w?sqldN`RF7Ifrk69?fm9B zdtW?YH{n-fmBji(>oRr~=-TkDdFPPGW)R7Ef+1q&yq^gdLzG0Bvwm1z^f|EMb!Yqq zmicL=rXP<5)SsGZ7r>Pm-+nG;wis*3=3mp8_o_U%IoZ!T|CQ^s`q1vh{mJ|*XP%ce zJ-@Uqov+k;oqzF?Y8TB8aj9b$zW7u#y*}{MqhmLFmZMeSmxVhu)$TI6J^A`YkF#@? zlCo-$u!CZm&vx;1zce=$={*ikx7o+`WK!s_$sA9({Z0gNvYXY5%@kgg*qp7jNOk(d zwycA{bsnrfwTNTF>)jW&>fYF9zmNUi!I|wBtUGpb6fyPdv%c#1QQ@>!V6)F7z3`|N zU0g+}ujF3-y~mRBjY0J0DR0Bf^`TE@XzkHed#@VZ=CzEM`$BP}WO$+2yVBV&UR*p} z#iuEs+@2hNICO#1T^_;Kih8a;1seta{mojZF=>zYTjqb<iA;SNHzRJcO%;j?c9|B^ z`lnNVhI4%CLxtkwYF!ibPI|;O`PzHgEnDjm>+M<<ny4(O6|kYVf?0OE^*7H`!TrvU zrpNTwc+2RE_&!r_Y<UsEYI`W)&%~>;yOZU`-UocQQ+oZ`>8;0#m}U82Wgpf5b?CBi zZ@CfT<&qY<lcnbMtX*N*Q=bU+y}Vv@BZ<{7v@(Q6sb1!o)m1izGkZC|uxKszoFmf^ z_M20DVg5fmwqF}J-d@N3BkhrtXk6+~L$@vAm+PFia_zRAdRTnc)N69a4^L&9UY!v* zZRV+|z7r<RYEs~h<T&6HCG(PXfy^Nd$NIz4GZ_=yCdhBFyIrv*;Cew!dUx8rcEQKz zbuU!yv6J$9=d!)|WY~G*htpZR?l#|cb(=Tg*`0rf`C7NKnkMx2PUsV!qnGl0@kiS$ ztU62!`*LJCV(l1b%v|yHC1<!!$Zp@z&#L-rsg@U$yDZf29a77PpSd7gcILZq18eDk zvofspI}RLp!qL*j!tm>i!mTTAIcrY6Y23xv%ln0W@)jFLftwfe*0)#5aWGzH`*!*t zYu}?P#@P}l*>@~Dv(+idI4Pxr$;^FGc&*8%i+4(7XOtW=`lQDfb^V&|0dwuuJbW8G zdRN?-T64Ma>g(UT(l5SxQ+c4a#Wgv~>Dx-~`os5GjBiZ-$Szm^D1;+2{Bo4-occ`e zp9fY>SoU-YOW6Fz1>Z|+e0}<)-$^e#FlpD;=fD1mDe|;mEIq!$e&c<E&%4rI_y4=W zRj^{)&2%RQ1GNVJ(k6-UGsy)i!V(Yc%(osc+x75?&yfvBYIr<@ThnG}i7j<mXvCR3 zWxYsZMoV^2x6Q&(!<P--BK7J17rNeYx1MciZ~CE9w$OBoFz+!gzN}llE34YFu6zxc z-!ez{Tx$b=l+*Uk4~u1V`<Jm?b$gM+<J0$o!&K_WYp+fj)7djQ&Ob=GdO<{~rDch! ziLcpd+v{wzJ5H7#-0gR5N$l2>ZdPnwua)<3$Za-Hh|-l%GSUtGI`x`joe*>VRZqJk zx}E=347Gmk3ICPSH#=aen>E+l@@H2*PG~t$vq5HwaGLEeukR-wsdy&O&e-*xQ&KHk z{Z?`K%bUi`5ecCS&9&8RBH0-NYYM!MZ#q0dVbzX9PA?;#UWs+xmN8?6d8lY=lZOe% zbZL3%V#g&1gwt0Bp6xC4zsSazzGKOSCXFcv6P2uPc71LNDB$F^v%8>ismC`Zs_5U& zhwZ_!JNLZW;x}6~qGmqR<Sm?LjAD~tbILHy<Cx6JrOxO(*^o=B{*a^JlRFkK<})`- zigE|DD{nDQ2$T4<US-zQpK4DVXI{E=lHJTDW6z>CA=dWwx{o3zcjU))@!mD~u)F<R zpVve$ZKjgGg-Y5BPi=UA*kwhsQZ?6I>9$0xV-*@#u5ldYICkibz2V%#d2SqHemoA& zuacDVMQ`}{@P9Z~_$#^N<gNPc9<G5Ip%ZR-ovT{La6~5lR7iqyjf~cb7n3#zWcOz5 z)Rd}uam)LrQPNqBYS%n{)>&CQa;j2v{EU1AuUe~57uwn$t9@^eK*N!|P!}%mwbHdG zmv|}5y52Fm6x12XG?CL%CAs}Qi$S#M6{f$l%3e2!e9gRc#{WX#b*InMqe9H;J(kob z3td+dO`rDY*r}4()>(GnB+8!{guajaYIi<qR-tM4fe?;1(}N-5&Ts5C{hFTFF!jkh z-jma-r%R^p$oZXeZ=r<Zg+GQJ%=b-vwh6YJc9<!2Pw=wdjAoGy8orC{Zj`wE5Z>o> zLdn>EW^C(G;m1=sKAze4Ze#7z9d8eQXX>kO5anV$;=XOgi;HdEbBq(Twr^5y>3A-k z<x?woTjH0%-?X5`j_aqtv|Zpo|3AwN3*OC#nlf0AT)pu6$k~wde`<3+-c=TPVRweJ zt7KM?z@K*l+m{PU9WZ(!8!MqOpDVLu!E&>Q^F>#Co{^r;rm57B_-e<pxsi4={dQ#D zzI?CgA7_2V%oDB(j(g`DFi+FTH`(Z9=CIl0nD-0T)osGZuJ4m?+SRyW(Pf3aMXQ!x ztd*R3$M;J%<LUJ#x1v_ZUW_xnwRW4syW3&U&tA=SufDxwXJ$-U)Y)xirHk`U$M3x+ zHGO}U{dT?c4tF=+;QOHdrSI2651mKaZI9G=!v%hNmL6QLmf-V5Xw?aYPm^@SCfv*{ zJvTe_z^|uauRneDef??qGuO|Xg0Je$GGCt0aO&9VYn7`%YfrrJI_&wTn`sZ3dG8%7 zeaxoz=G&ga#j*GA#l&x&Jz1F7zn<Uphi*XE)N{Ij?`_Kbyr*?iIP=%zoE&`D*laeP zyvb?Mv~=x`s3U%20s#wl@yKk>k6ut)diLz9J8##y#q7NF*?8(b^BId8>}qfCQg7{R zc)i%(>0NY(9&?Ywhs<)D+FFBml7jLtrU(Y7MPD)e&T-+&E9brPUmqst3JM9ZvHwu6 z7yf>2>fQ8+fCV=;tz`*GnBgyO{-k1Ylk?*9ybs*Uw;um?Ca}vpvUm5fLg(olET`F| zotYQGH{Yjq{VKWU-aEF1jG}zE%Onqei@jd4CFg4S55bmA)>1EDsJhHe=xO*~a$2t1 zN9tu+{+pIlyqpK8cnI8Kb$Ar|@t~Dit%A%F=aBjfkNYg{wUsP;eXQAa_7YpUeT>=- z%8o4NAD-Rh`mu4D=`Ian0q&;HN#}b50<^myy=4gf{-UTiU+h`lc23<b)&;T8SXKSg zQ#Q}7S+`*OSD#1f2VQYF-F-PLM?m=*+tWMwP9^DlvOKz+vPnE^w>88Tzt4Tws<by% z`X*o2zMR#p^~aiDx84q)CV%rCBlF|l-yPEgj8#<+am@ER{P2gyqN5w9-W6C`{5e;! z=+UR+Cp?_Zygp1?Cj4$ui;V3T5zUkXwTCi{BW;h(FFd&UtKW{7o_8eD_|J%jF`FbS zA1z&1?fl5aY3H;Ep6Q=&d_Hv7=$$3oJf1L1snusXqkMn$zE_W_zsbRF;>gotG4<4+ z#ZJ9lw=@zHS?=tyao#ygjLrSM^!4ijOTP9C&vE)B#^65p!v|K=68`&w8vRB3oQGd? zg{}Bx(6z$cam7`=HB$U%J~&jEu3D&if3wf+52gYF8*WHm-hVvSOITy`r+2lgCP5dB z4=%myA|PBCn)g+6T8Zy%i{kpZrIPv!)&ySsu|GE}W$omdubQhEr!Z*kYiW*V@46B8 zEv<0v58(&T=Ra}Nko?NS@IW}=;<v))uBp%Jf({;Csk2ymfzg$SnKxato_TO2Z`@jy z-=?U&#aT<?<HjV`2Ib?XpB`|X>Uwcboxje}pz(vwPWkhO>fd6QR;!g1y@)GssJFh> zBXQ{Lk#gZ5d#>LTyu*;5GySWr=yR7O4YS}WHLJ25R%~lzO*!-QS$&f5Avs6414qQm z=X0ChE7~FHwI(oVN=Tu|>ic`u^JmHx-sE(;#_ag+vXfsW#}}~%pU|$17m>Tz`z@n` z_f8S4;rw9G;;1=cX`}v%I_{&hFI+P_%fV6Kbi#8j<E+MomVXTeWLB^!h|Q@;k^ere z<=3wHGU2(Rar;mGk9=Cc?vs7^e0}Br&wo9if1$Hz>y<zAo9{kuuK4%--@|+FwEm|{ z&gVA1z-xP9`;K*GOs8($yQQj8r!KX)*YsywN6K1Txkb;vxL8-0GOyqI+S8!Guz~MO zUjC+U$7JhgMXy*nZ^Ejl#j&Sf{oI=HCu63c@B3?C5~Q8YOYS^*$(9@tuq`tE_J!l$ zqM8@E_;2C95ahVHs(Z$B{_O^0H|#EIz1qF%+Rm%@oNkN0-QV_QkK5+2_F4V)v5&J( zcAD9~%>4Fb`OnD<y40LIi?W=#Ro$1GO88In(hko1H092HZPoftE%|$`lUH4N(ROpv zY9mH%)tg><H>XW2QMFsD5`XgIv`H<ZZFN&7^oSmek@Sq*bg*^u=1m@H7gN?6oJ-%f zWUWqxm(QcFwnsX1&Zs`#xlFY*l{fdHivCFhFOP3QOg3VEzk+tY`|@PH&X*|@dDQGw zRa;wYvL8M%_&C$lQ-?cxX}xN1r+?+i<VmViX1b`^Jyo0Rs<h6_OKWn?qBBp7Lc=vC zq)c-)<yN1&vS7;O8ntL0RZd@}{by24RNK~WS+YguY5uz>CLcBRJ#~#NpQ@~Gwb^=R ziD@Xy)G2$^?3Su-RCQh??wvMyRko&QZgAwWL!Nm>l2fO?S*G)Ck!OD9VjbgpPQ93G zOP55Syz$8B(z;2v&fGa!XtZw68WqcrGp!@_a+AEh=lvE++j?e+)kjU;oq9sb@lKQG z{?M71qi(0FdUvHHYpTlqR<k#YJS&R6PM_Sf{Mgs&6Q?ZSvb~^a-j6Ahmn?t6BjSC{ zJJ~f=^}XwlCx<+1f)aI<XX;<EE1vwz=u1z%>*PPryuO`oTDs@S{>aG;n-#QlH8)&& za$wUW7H?m%IrB_rv_2`C#4%rC=7uLr4o#ZO?d@lzV|#PMl_iHiP3HIZb<(ky+<0Zl zk)p}M-u^*4mXRBuEIIn}WVd9`+AS$=nvYI<W`)F@PgOBrbyj!AlND97y*<l%cKu$a zX1mNcWu<5RgoXRpEJ^V=s-&%|X>|9=JX_14np57BLY8mpNjceZGQuF!sMpTRchQtJ zTeqZmxgOWnU1{{>I=6cGvcxTOQoMIw5%b;UvqS5d|Gp_{DVm-$f^2p5C4(<oiTRxp znSW`L@zb7ZQ<rES^|d@XD|1Us)Td>O5~oc#qG{W!s#@%|FSI@=X!(yR)9<XjQS?-$ zvTc`_=E-@2{Ffhj&hJ`Z{qf29jFekRo(qhEisq$U*diJhSCo;oxqhO`MUMhM&XbF} zdPBS}Wt7Y={rDs>b^69BDnYJ7x^sM_-rdlf6S8W<#7UF9r#7COpE7Hk!KX=nmrtFa z-7+ij@3cu%w9d{JniMShxTyZ4O6b&ZojGe(9ZS=hu|=!*W6`AWrSi2CRl;_rrFo?J zDzB296n%Ns^a*dY>{3<s9^313c~ZRbw3|97H)Q%wnIbd$!#@2<D$$c>ig->Foj++( znlr1Zer4I)Tg8Rk`LDk{lezioLQ2++JCUXnznxpO??a`uY~UyVHE+e(ihm|9uC3SJ z@b&ktj0Mqc%Y{1|xnAZ5-kWH5%<}5?p5MATSC9X=e6sBPh3z{oAG&|<*0K)<_P4xe zA1|4H_4KXt&sb8r9(JEu_Db@~kp)q2-NGWRUFUT~cgcw`&plq|ex&Sn!^w^7=g!Gs zzcuaKDV{GL7yGQ4K3O=fsC=_4N^;)jFPa8xm9N$(9H{k|=bJd;9BZy)=DcFo``@y6 z@1Cd?uDoUQ2B+CgFE(5d)e%~t&l7yi!g1>kg;cGpha%F>AAO<w<E-SImIxKcyBnUq z%Qw{U+p_-}XV>NcyPUa3+s}rj8yWd~tjJyb{v?B#_N5Km;+W4fGf4DvM%y;K);ZZ6 z{lffz{nx#>{?+IGd3)m53iWxuJSBc5kIyGYxlMLSjOg06zs!tLGojSKf+OQcwZf(w z*RK7uRlb<}cGYoXi(T7}?s@*DX~!Y!u;+h|)o{OFux&r@vk85P+%8SoidQ#Dga-Wz zlr#%`xADe%(}s6*9b#^L%`n=OVcB!z(@ing4c+BTxt32|1-1NN)SuDxdB5HKdO&hT z`{yaw4ffnS79INU@BK!H+6}7>TS{`@9&YE)sBPG_|J(nJo}(Gj&NIFWvR+;A^?b4L z!Y8++cKS9(*gtypV8zFyC5oB<T1!8E-+1+J$IE#KZ{6}wJMj1($MKwWkLyg&&VI3M z%#feJzNbw!qs~HchlELX$ee=#^}88k<{1ekD27G-NXhLLc8{&x!T7r1Nt0E`dX6g_ zmX&3vnL8;?^;NKE3BCSDkykqLYoNp8DgSw$xtkbg>COB7V!fp8i}uop*Rv1G_CDi# zox|a@Yje-9!l}K&``^C{_f%c|zQJePKZDmFZojx{eecGd80ERShhHd){P<aNy|l63 zd6kv=jU)@3FKm$?mWQp*<lkj$7Wt*+bp%`Myd*hJlTC{=ce`&rym#^4WZ&xCODTQt zxAwm7zAGEh@c;e)>-XjU-Tyz`bn)A_O=?Rst#_->FgR0HQ~XhE*3G#qXZ~H2_AM>z znZ{ztj2Cz99>)J(^7b8L(3_7JKHfLJm1Eeoppv;hWmnTNr7K5Hwq2G9n!nM6YxN?b zR|}0|)_;lF9^`vAShn$q!^4R6cV5c{s0Q9TvEt-YN0zYvd(L^L{B>EhZPi+DtH$H6 z3tiTS8yhEIpSWTJ-~6txTIY_=pIvjpw5?}RN}ts?S-vmlzOMKZ%dfazx%P4lV|Rw} zJ4U7tTUQDPU;9_jykw_*PV8dyex=rH+qQ6XxvQUH{E$$Z>@siL$8!a{|Lw8)pAjt| zdHer8R%UyH?HgX|-uv)u*&CrMVdpR6@ABU`cSo;SX&3Qq@tdfU1tr?ns{G6vC;l@w zB-9CfSDI@1$>V#@&kupW(>yKGN;V(8y`;}?W$~43J1=MJZn1nAX<7ew(XFfHPdK)_ zO>>-Ue)FAD31^e!)B{Dkn6ug1Jr{n<nWEw4ROr6?@cm;azi*G!oF8^j(|;n%+vSZ9 zQxk3*s++f3XT0;sGi(0HzjfQhHXCkc{TX|+toWxrw_0+?t|7`>_TyjQALpO^E1kWK zt2$Y_TrERfoY`&b*8RPWhE0|w^)K0;?R{PMMRSpOddJRf4U>gFHm>*=(7ey?!ws<j z%bala>)S;Q_;#0F&MAK<b$#vbXHRc_4Sl`Ic&=(&Y2NL#vriv$oxJhbho7a3@77h9 z79YEj_+!sqo4tD*FKO6mXZLpRkH7rbJp8TF-BX{ghnold`|zT|zfz)ERY#`P)$ae& z`qIlgbLV_M7r9*K%I(*mS09Tk>=Cf+b?>;H%T=?+pv}{9qJeC}bmQ*(Z@*W{aR;>C zacMq2tM?h-s=<1gDi&{QtY3i-qNKD+n7YwdHHeY5TzOXIb&KmO0`i1(YkV%`+@ zRU2BKFJ#%#=*ag~u;{1R%WF#}ZgLfD;E0I+ZJ$v;?Q7Pp-EGMW%)@pwCZ<(AOyWHd z)BA}3CfAIUzwPDT{t1v~ajyQn-@*T{w*A&e`?q~$-%@?8DeP*qF4vZw-w%KMcrPw( z@#^3A!=KxW?)hK)|3kv_iv0T0|G&+v|6TUoTv&E-4R3Giy#HH@x0L&Q-~V3z&71e{ z-^x7r>F<7g_3!)4(?3N{4v?0Z94pOH|4V64wD#xP(tp2C{SW{5^Zjo7HRtO8i~X&S zIa<{Pn(~?P^#5X0dF%gwUsQazep~SL|8KJ&|Fix-cYX8!jojOJe>c?cx%L0&v7hzH zKYn*e@^8KP<mLBzK7Rhb3IE^A$;rHXBik_NhV8BUH>2PF*Li&St?%{ec~LxTpWXl4 zS@UOhy?j+sYoXM}tb-lvHnNDFN`K(`@e*Ijx(!?AE_=T7-Yk=>_oeq=8Reg0^A23K zzJCA0-?1qkv-f@b%O<;N&(_`T_fH;6<L~?MU4WnKjq}d0TATO3US&JyUD;P=gUt3) ziSV=hJmoSu>lU_4UY_H$DE;}|P%WMB+><vQDz{Nz^}Om;z2k<7i<@thNm*%aIab>J zDb7j1(${&q@H|VyBPONK3X}5Grp@}Q|LV)ZOPBZhgg#3=@aPcdxm|5}v#*C=dU<fu zlaq6g&Iw;;;oDew<%2|%0LQhJGETedyYDV2ce=Wz`uyLFs&Z!W=9iqVNqc^*SQcBT zE_GLJQGc#R=*GM2@*cmg_tIUe=@Pg`vu4UIkzakQG;<klRHUU#Mf?fntGLrH(&(Eb zcSK6Ke`?#6s3dDeJDGnLaozz@EXxgiqhfc99M5r`kTS8Ged2<B-F$Xo_A(xCTsc;M zO6pbLlT}_`QS$Zf-M0K&d#9*QW%{&h&a<y?xIPx9O!tw#YBhV+mT#Aj&8!d7$-OfB z*0uPT-WB&0`*tns5%k(s*Q?L}<z(&}_dN>^bWXW6d*xdJ?Gu^odlx5KM_A<?f0cSY zSa#9t*)Q6Uu^fI;@o2+}6K9(>Z*9vtx8?7n-sLvEkKBD?<rEHi?~!}=`sfCoG&Av| zcb4yoo7%EL@{-Nl*}s|HKFxX_H0}Rg_u_h|BSETWappQUYD`aLWjQ6)&xiyGb<g25 zO|*SbxN1kV<LzY?HOrKxc`6O7&n>*b!1c}R`n$xrwNKbft=?=&s9e7OiHqQiiISSU zS0A+sN`!T(g#IbDh|p0|Osz3!?mgz-=WgxtOzOm!N%FU(=i2>K@`{_+w|wQ27b4Po zPi4=of9JrM@+o+YE0?7AJ1*@LC0D&Xth#j8OlAwQo*J=t+IzDJ8LtbM1*lqv_OJh# z_0jv-4fj~duWMROidUa{7dDq|nazsW+q<65Y2!CjnrvJgzqwZF^Ipkwt{&%;^x_JG z_@AdJ&oNl4B(Nm;mV}Cs9_POT<-nfI`Q=GlxvSQ7>(@_Q8XvxoIlrveQ&#lREJL}z znp@NSWgU7aeP8%~>i_ho|0{p~oBwyO{C9pn{_ubE^Y8pG{#Y%bbMb%ihT@W*fAjbL zeVoj*QtAKse;-f%|E%+GzNVeL<QemVZ07}&rT8rmFy}pde*LL1m$=g<b&-AAZ-jUK zs9yeA_h-wK3i0opC+n9P)jI9A|Fa?f?6<oo*wQ}TZMi$`^=`+6_+9a8E-yY;MHKLz z`mo_d*(V#F%Qk|WCbrrLDr!8fP}-+ge|Y)>g9(jgD$JEjV*hMy`IYyjg8hwUTHJA+ z2NkJ(&wH2bSA3Xtp|4EnYVLK}yMb07LVAu7f~(DrFX-L!qAzZX<L+N;>fijI`qzH? z-}*hf_wWCG_}r;~|Bt=>Z%|Rg`R)I|e;4n)(>wbAt?avopY;yk{@>I3Uq16&{h2-f zrR)l`BZTc;rs;0}UYRFfuYUR25AS&i+HsF(Ht;&jH_ntS6~7b2=VoH+R6Fg1uEavc zzx7-ea(Oo{UVOGPd&Uyhu&vSSdQ9yf)^9!fCsFvlQJ>AulI9cnX)^oE_EvjwKl0Zv zk^bs&SzzlU-v6~ZRg=vmZY}!#Y&pa4?KcB^Cmsu)a!u=-#TDj;Wgi(77V*Yi&AZ|A zK`6IuPU@HN9fhAJ?bTJh?O7E)WzNh+_r%VyI^C{aRjv9i;^Dh#E8k6Zc^8_&b&QK6 zS#TCt{VvTsiEGJy#%W^aJq(j(hB0w}D%>UCCeL!`I`iki`5n%I+Bg4++<fQrPb5<N z^!!8FKhw(JIQr*n7M@!Yy{^h2QZ4qB`pkZtDGd4g3o6(Ttk^NvN2>6T8H3fD-NpCr z<!wLjeEHayA1g1-n8x9=_}HYcH|8p2WaK6WDc|VKI_X=_S}JcK{m%J{w20q9r$mc0 zQx6yvzTfrjOx^+unb$S5ienwQEh>V#jy_m^<~NV|gqT>*cF&}hjK>9!c3bBQy)M*y z?yKZ$^k~mX&a0hI1O0d}C9XK#>|hqszSH%}I{)so6LUA|Z)!-m(^hH!F5l(V*J~3` z=-l1*&){R7+Mf8Sh4o4Ecup68w0Bqg=&$<u-{v1|{5P5Qey)|)R^X{$r>(bsw>$rP zvoAZ&bKkgsz;ws%>^C?6NE>qgcd6dqXm5U>`%|;HaESP;H`g0-qBPp=HsuJh23|6K zKOxq`sP=3HuW;DK<t}@h7_YW?6)y6txuKx?Hg8Gwh8M@an229{HmN3}zQJE2B;VGo zaQ?%e^IuC&xfm@T{Ap(G&)`n>Zt<m@$!A@~*k<p`dDXvNdD(B{-$L2FzCj<a9a&PB z7XOJ&VaE0J)VD{fo|}a2srH;Q|L1o5V&zvZze|-K^`5=6O*VWQ`$9=~E8X542iL}3 zKf<J&=H=X9=pFEK`iUsPfSqx-LZs^b-_K+Ix9-w{#XHv8cjRgB*j7C?eA&C1a`V|Q zUYkEPCZ)u6mi_KE=eG8|`(~TXeq7*S)hz~=8Jl^Ul$S49yq)37k_Fj4=B<BBF8!Im z@W;x0o)dC6L)f0rJb0tnsqg6ioDEN-)9X8SUVHdo>uc!odY1L;w2#=^s_^VRQR?R~ ziTQf{&-jBoU%&AWjbi@dF34Q5=X`!Y<HrC!uAr-v-sCRZZ_DQ4@lskTyd;(_PV3k9 z?XqT>di(WaV+6eUk8bV!<CGS{c~Wcv)0-6&4*d0)?y@-LO=E$~2KAF`C$9LV!<qZW z_}|(M*{SAktxBPV)8Bd=uy0v*iT&yvkDUE3rp2DOx$Bu!Zfi0Ga0n^~C3LkuTYPFg z$6?MFy>C67HFW;YP_0v1$04rsZerf1BYcOtXB}N2l<T^-ChK&0;mrvbJ*y^&@3e^u zmyFo7SHv@J!^I6@o3564*LxlITA{lCkEzlA$hjh!v+jB*pF0uo&SjFh<kN3c3a;uj zEV@yb6yxD)xg*K0-epHl$dqSqEwz?8x4ieiy!(cs%7wdTg%e6!n~h@}R8JmtPuP>| zG0)d$`j_VOqKpNqDSV-8l^RyGXn#3>wxdp=YUR(8$tPpQ4f@ND-a8jpQX}!#Yr%(4 zH!6Mf9~!lPPFUw|)4-a@V0`lc8&_%VhRu_gak-0|pEwi1&9<{qhoyds_D!{WwnCS0 zZu))O|F!-phD*PlOC65CKJK$PA->vPB<%BLE0N_ziN_Bx=UPopv$<rpW6}Hek`vp0 z{IA|Dk<k9}XIjJ3&(lk8RDBY>u*xs`tk&v8M%GiG^to=-@g=k#IOx7Z<Hf8)B@7XZ zIloQ5w<%^O$6~J%t{3%OxrMc>-ql};Za=y#*Tm0%_Vu~XZl*~|dW&|hmM%2)v{93D zin>@*`l9Ws;q=u4M-`T4Ok5*%e^Q;nbd~4(9?x9zGx+3<peMI2ocFk{SXLyobkX|2 zC7q@leKv}{EKa*<wx6Z*%kBmd%|l0&Yi-q)>t%NQx}fyUca0S9&Hrb+pB+xlK6>;* zPW@}u-r!YXYRMgG0XE0<4!gRjKl`R-$nK(Ax^l_t^NTvY%|o^_ZmM6cVk35OE#DW8 z1Xg>OD?6XEzb|0^J~e&j-tRAOEPC9UJKO4Wuwd)i4cQM)Ive_yoH=Z|P@mzWV@^@3 zq0dRh;7a9(!M~30{Jo5+Vx`A>U&nXbjrG3=m%THuPr7k`4QE=#@jjcLk4KceH>F#$ z&2CeVoM?UP*wyntvnQ@Nv$2U~>cP!UA#J5w`}yv1o=G+Enc|nY{ZY)78l@v_aSVcw zP9BNp5j=HLv!A0-YTi_i+MVoe@$Yq%uRJZh_;iBe=7Nk{kp||QD|%uyH=0bU)>>;R zwNTl#N^1ARSvmDxK01LD*z@b3b^QOlRF7pzGpoqO$f-^z=RW^f_C{vOSFyQU_S~y_ zcf;`7a}o9PTPNI{c>m*89dpB^-QuBVkL=j|!b*MLw0$>j&b*hg+4`CC)|iURjqw6{ zuQSCft{&RkS;Nn>VNo>e$21{zw+$aJ6bk+*t=i9BxI*3R)lB)>M_<<;Ex#W8CU=S2 z>4mK`bL>>#7?`;pS(Q+fJ0msuQ_i2s6E_@8?RwYVy>W)Ty`0y|pu<}f>r|tqQZs+O z_{87p-FVp2#egSS#G^>Mh}&`t&(nJwe;oQ5F!!JCtALxaE**lCqZfI%3Yv=ZdKay9 z`8B7%*JkR9-UhMA4-=2t?LPi5q5k=ClYpQZ@n$v)jii%%SsicK`Y|h>o7VLuKTk8} z^-9}BL5a9ybzW>9e?BHzYfaoX-y!n$RWrZ*S-mUw?f7`Y<Km5$udWA=2UTt^U2cEv z_oMXlpZ+LFE=YQ<=^vbUt=jhVwhlp;*#&#oP30*#xs545Q=rb!+u(|D<`gIP#vrHq z=*9CvORDN0^yM~xF@1H&Bcn#fe(r`@o)aJPy__R@`xSpcTPV-n-yQWY4~o50dl;(! z$oGfBMaPbko9z`#0z)<j6r{{w=6rV6$!$|47azMZ!(!DXr(4PeOG4kibUr=LXSMXq zjIFyqIqjUQzFgH$GD|w#eEHhVJ-75DcgOAexWB$5zFG0NrYz^;jjLX33LY&fzsqw< zKACOFpNmOrF7H?TzN%ZFi+%O!{LiwC>SFG(H7i#wi4EUZ{48vF{rsBGd5-NT*JP<J zp7eCpOt1M}SNE~h`=)FNd|K!ht8nR3ao!otQwQcANL>{sQ21Cx<ecM~iJy25S-rX{ z$fV+Ar9E?^R=si9!D*kn4&*vUK0SFzB(PjJEc?*TkD{j~Qf=#<52;-%jCn96ZO(dC z*OMj{acwv4c+LLR8UBua+j6_%yynhA_Nna}YRYbYN4u4ze<f<~y!|b4<MaEwr*Eh+ zl>V_%QZrZFe$LeAcTP<YD7YK=PfeCjc*aB%RlZMU-%>+E{m<N}zd66%WV2ag%69)m zpYyJ_Z_f`k*>^*?%5pXL3YN`W?md3fPt2_UZgcI-=e6>c$`w8R^FKa!y|E)lxqRh` zp!4UCuxiBf@b{l{%hZYSJK4S9fc2@JoG&j3xnEUDGS)Cpw|cs<NbBWM&G+02Ha_oS zr+l)AOw8G^=J4dUmuI?9E!V9-RGa$QTU}`#YsY*+RR^6#HnIv!rufJz*u;f?Q}ByC z%3is#VbK|(lGaIcG~UQudAcKT+1IUWj_&*$IrEPA>OUM=af{vwN51oaBY5KF_Kv6W zOP4O+_}BPg(v)?7?aYpQwOFZMPCaC5yu0?<hSL&<Ll5Z*i%Aq%&%euZrFTxq)V=k8 zS)cxWcR=P<=2fl_MPidh6Q93m^xXd8P<v*L-JQ0uWb4B@%@H48Jg^p=maA&cDRLy? zCj05_?{sI&uT-;IB{)0z_!ia%hM!HcXUg(7Ek3jRTZ?JJ^7*n0<(7)EDz?2Y@rY3L zJh#%}=&3X8_YZCQ%zZ~(Q}23mdyH7ai}E#$-u1$(Y-Y}SFniba*h_h*x9p2sz$eeG z7pw4~q0sE$-xq%_SamZ7So+8QUAW=?u0Lk~-xe3v1@_xld|Gf#$Yj1{a+LGU-FJ3e zTXF8|k=yp2cFxaC79X1~xKJuN@#kE1Cq>b+HW|f9f3`2#aq-Y94V(U=)#|;nXB}rf zO<^{2W_daJRsB5I#U4AEq{{zjORd;5gFWJY!28(?m0m@+-Ml(=b@d_lPet=&u33Cq z!|vdD-{jq+oo)|jN8FlN^^(8aLN>Z{c2Ci-mP;Six^gn_%&3jHS0OcjRil-Gt)F=F zp0j$pt=#;x4t8vkD&m-YbXUWTFV1_HXqRM1Dzp5nlgU`uy!`2ldj1u~>`rOwmr5qC zc=z*Xjz;T38S98$V!^ZiHt@-t`5t~=TEVmTjfq6XyB+ok?n`S7?&wKH+`Ey;Y{~g$ z-?r466PMqJvbq2Kx3THr)aXRVnrTrMDm!L;>OX(kr$NK>7~A=Kso%v^?|nH{vOHYL zO0##;;;)C5a-Lh-@GI_Kn{lB&xGXx6C1lUqDUBCie)>K|<KWe^H+s`M1@vC)<)1S- z{K<Wronh)UQTweN)4cu9TU7FI=`oyHFTY_Xe`amkAv^zid7f{*QGXj`H<!*#m1|x7 z@%FZUy~maHYvV3`Q15eFa^UjgihG`KyE)V~ZqJ%9$;DLt#k`y{#c2tdr5{rpSJ$r& z%?l5&cVR7z{kzejTr+3gyiaT9$Q>%!@x-{U_h-}Lhdpc0{SRZkzVh>a|GcWH8z0*( zf4lU}H2v#8y54@%KmIGoO854kU32Ei%(z!QDgIo#t4;2m!#yW6idL(CsYrXHsCMVZ zRUZ3NM)s{z%k@GZtvDZgajIx~=`p?JiMA(Ich=8~Z>j$|yF}^q&A3bVK1_f2MOP+e z{yxJ!vX?#-FPRs+qwVVKKSD)oP4x3C@7>y~e<J$y<E6n8uQxS+4|a@*<ET&jeqQRt ze6FjfKP(YzHsE<R|K`eFX8d_+mnJQ0{8K5_|68KUfAT56M+sXjO{88WN}lqGyCgTS z-KAm8mZqs;AL_qEF;2WC{N%o2WxZ?mHKSK^9)&-uR^7Kh_hT&6iTF;N^s2vq1TU;q zl2M-=d+BRJqr%i>--9{6uF0vGXT4wOvT9f6tRqV~UjCUk&)J1r>7LpnJ>RhNvu97# zZBCcsS$F>Hm1W{`=hq}_tw`FVW7@p&e0s2<%lGpO;#S1&=eg?SQ2)hfFO%nsX$u`Y z;?l3kMR?SEF^C%O_xT*(nX9$q?Zk(>-|YHUq4CM#{zlp7d}Xy>`uD<ru5DFt`4I5K zgYl=$;@rbucF!~Il7HpCi_i3<P_5`oN!CXx0kfv&=WJG+=`47Oxj$EsziXlQC7IQ} z_FY$d0z}TH{7~52`cj=CXlHBv)Uemw*CH=B7tP2tOm;Zu^*8m4`PaG)HggNp_HKXD zq5SA<)|QJk)#1f%mo&V0t*cqj<rnz8UU|~Wbv5;$45Sa8|Kyz0p)*Z3?yz-iTt@Gu zhe1u574<#0&(<9IcIYJ6p5HHLZ8^1Ko{7IA<HTPv&q{QrtT?c{;@Afzn<)(oK76j1 zDOw_YIZMCj99Q;wmi4nNCbgPc#F|!S{yOq`4vWF&8#VWglSMYoDogNrx!uh8P!v~q z`0mLpTTX2BS+w!F^%<4>M$5Mg>P<?1wJF$F#Me6U?4<npoA0^$-WD*){9co&({XT* ziu@jv-swplKJVXJ-hA~f-O)z>+mWfRTR-hw*HRxnuS({9UZwkPk+8t&WvjfI7M+-= z_v(?>%aG~IgCn?4$S7nKuX47~EZd|XXt$Uxe%gbdL34Aw?LWQtF6$Lk)qf{7+h1a1 z>XsjDALDL4to48VBT+&B=11YTC7hFVZ$1o4iQaO1`o^g<S%vHOy|5I#_-y{?2}OFB zyJY!oeJB02tY7Y&E;mJ?K+yNxF=Zj1dm8(g)SmfTI8WKDmTx-0N~<ZI?=HLW`spG4 zjse*_lyevSZszxS$9HDJQWfuN=P)zPY0G5`zWI9IQ5OqTopb!}(^E^HKL4#|9XBsW z>!HWT6z}u3o-DzOSd62eWW-7E?fR;Garps;zn$_k4zFdu{5i4SwNtmU?nZKy$Z;pb zmHHP1zxv3{78iBcc_pUp=iG;rDyF{??f<%aMtDr-o0)eXmoLa>U1zTOZ2f`DuKx^< zN=EIrwr1sL;SX1O-^cfe@x-2l*`K4!TlR2H+xFkZuII=^|N4#(8$W)S*Sbd2V98un zmdAg!L^jWy^I89j^GB9{pK|N{X8-S968OL8|GBr$|Ai_)t>IdhlAaW(?|aziW#!CG z3b$VS*iE>(hey*^%yNtEK4;VQGq|d2?G#&YN?dlfuWJmt=_qgaH0I~&Gg~+P+@@3d zM>HmLYGq0LEbY&Sm@PXz#JC>L$kv|Vkx;iZF!yog^u%Rd9s0`ILWXn2-5oV2)Vqcy z&oJ2e;o3BoNv@C7XZ7gB*ZfaP(%i?Px8d(h<sV1eWEBGStY0w{ZrZb;dBgTriL=EY zCFixr&bX>#v}&Khy~%TDi=;7bJZ-aTOHici{vVpT+IrhW^u&sfx$6INEAp`pdUKLr zCemp4<H%{=$rYXdo+VaJbenhi^!Z@V;>h_XlRUl3&Cg5=wQ4m#HG7`K<idHD)xrxs zLLM%DcVc%%S4h-dp3om@8lwI8OJ=2h4sLiJc{_LVJ0mGk@y8_^)-NZi?3z^UIXQlL zz4soaS3f7q8jJhJy?PN;5n48VPGuyY?+Sqx#fsGrYrNPlcJKP3$tQGgU+}f5{{G97 z#eA1MX{_07`KgQLi|93p=`YXx{+0Z!OG{J9FK_7*9pgW>dFh9yPi{1ps-Ld1-q=CQ zHO~E3-MbV1NeY&Z^9$R5aOA&wd@<7cV}h>a>aVw)pL;Lsx3QW2<!Q&aA79H%&hg!x z{O<<ehsLJ{I}aS%86>M<Z5fzwvh72xn@q&I({tOtn_30jc;I!(yJ3Ol<r$8KKX(c5 zSnS0-m8+8L;%fffJ=N<sT@j3JKbgi-zqK@~(_`nW!y3h@x>pPIWRjDUWoJjE?@2B; z-mbE3_qiKgK}FY2SX|4R`*-H|4dIes+|I0NdwIBg-px|m!}mKZ&F2~X@zwjjv~8c} zUOmk>y4M~*dT#3*T$eOg>G6Tq<0*gd=yl&*{9Z&NWLlsD|EyKKU#@v~Tn>Ivz03Ev zl44c8|IB%Z7Jc8>lrlwpKl{8wj&EA>s@ZRCovpm0wtr4reB^f3yOb$0!RpgGCuP4* z$=k(pO~CS7feX*wBlAr^y4rrLP_y=&w<G*aK-HteR&jIIinLqLX)4ga|0kvFxKG)m zDIzw!KUSM=oi=~b=c6*&Uyt5!WoT9T`e^SdtqTkO?Auho?a-1bOJ_%CZTI#yo^|@c zEc@dxSZ7;mHJo3(c*S(vGfS2gtckjtX}wC<t3CBc`>c1~*R^jPa7i|CnsP(o^i5AK zoxRh|bboA1c(R~Mcr8oeQ3qL-BN1+lpR`#|2|vEYHuqUk-B+$((zn?rvNc)A?Bsa# z%<G)K#`Bvg_A@8MTb!uZ7CYR!D2I9XipTcFZ9%qA=V={&UhJ`F%Dm|cUyQ1BBi}u} zH+Krtp`J|zj*ior4j3QfHoRxie&F@NigyMkpU!<<@hJAZ@WR)({<lS?#KkPhU(~}n z;p>Li9a>kp+@f|hYR><&nyH^{S>6LdTZu<}{PC~1&A+`N*YerimkjqBCby>fT29S8 zG;>4a@##PLTbOvxEHm~lmHMzqC_1~u>g;L1*RPD;%>SYFT0CtA=PBKQoxc1#H{@IG z6Lnbq|5(#@(H(8c_7~W8NctHGJbr!jsjX5*ThhCYn!ewa-IuZMQWstK^o8KeFP9W2 z*O*1uhntFbRra3<NqVX~f4RvLRfY?{Jkm6dgt@k#yPUkA<<rVsiPICRYl3du@4aYt zFRJgM-LKi&f8S<byuWK(>ZIteWi6jeyzI?=j!W!3@b%T+Q|XsZ9%;7sJTD!5GdE^) zo$4#$nLTHEdtCQ-x=eUfrTVZnbb>s`w!5pZY-@g)@!Gh)lIP?6-m~`F6Rq6dtK{&l zUQ#Z-I+**8bpo684N0**^50)=`mXihl!5)#nJXT0&-lyLo_XtE$cyC-P48vdOlLPp zIlpYn(QL^qE{v_7y>@B($<+ymN)MOqc6nK9_$BK^`|D5A;mohTynnM(v}WIp`C5-_ zxT06PM?8FXntR(5u7mZ(5^N{uO;40q^8Ht?B%7y<O?tv2KBxLe#)}zd?zEVve(KRG zL)Cp;T1P81OiM$yeh$+Y+s7Rfo%1rVvUj7yO^#f>?z>hjOC1+P{+r`vv+7Ar$*fkF zXNIgtziO_W)bL62GKZ+jnk8BdEt8hKT9c42c(hDU)hw?4tc%IsDQEjF>i_$FW?z!g zIO(eV?0x;}O6^+9A8%T2@JHsZ*&L@!|6`@4%6C2aKKbh6xp@(_$G=`ky|Gw4X3euN ziX2LIVx3{L;ySBm>Ysk4>ULDFJ#y=^<3BchThFob)8zTVw|Fb~bZ^MM{TtAx-O~Cm zz@Fjb<W-K!UuH(nYdfhTx<xSPR#3UUwegmOLthrZYhM$&<;sKAD`S^!TJ<?}-nw;B zwvM6`EwzKY=f>N6hx5p~S|9zSIeYd*E9uvZu2qGaZHVlh%xfvZe{Z?=+&O2qFD;3P ze|7EYy~!q)D)rMB9H}u;cl|nD=jr40;!j7aj-S}vwqqmPfqgkgA7u+45B56~z+$w* zOkT8nrcrW~_%3C+O_Q!_N7^o5GfydJVt1`#QP{(2=T(ygqt7cRMxK{s+ICj5==W-& z4QI}F$THQfS!e&cc1>=iaqNzwbw~3Um69*LdS#Y7`+(E;_@sus`aGBHj+Oc`Q8g2% z@3uRd$@Ejn;Btl5qn-SnPdoi%??%7abiIJrp80gY{_3U0|5O-)4h5fDy*+@fo~I$k zzs$_#spS@L9^bQU=F>jw$Lr4dJOA#};!{C&M(>mkE)QK-EyCfF%09iz;flU3&(w!` z|1_>dl=F1okeyZLm9OBwi@9Dg(&l^Mv0Exu6^l-ANd42YdE|LWSwhe@x#IPXh!XAS zT^C=3ZTs~4$9e8M=byQMusd8==5_wG_4Xf@-!}(4M7*mj<6_W%&~^Lf((J^}Q|}A^ zJE^?l7B19&xODH9(|6KE<AeFPa;r*2YpEQJ+jJvH|3%!o8(uFfPloTWU!A1QTF<Wk zI4z+|j`i$)&UxS7wA}OyXn3}4&RhqD`n~EJH8*|+oRj2|FAI*+dM?7s*ScEfgPNbw zf4B1?5p(0ehh!b9S#FeYy#C<4|I*sm?H}*2|L6WnbnQ{Tm>K*$tW&=J?<o58#BO8M zkNwv#w<{~It)CX3ZmA)md_#P%P4Dj)jlu2p%PlsXsMyc;C^h-7eDv}Yi?7dr6~eB@ zIM1=$t87ot<Nv|dwZ9I3ne<oht<Bz;UOmq1MWVY>{-3Wtm?r3|SLpKJC+k|b&1!pB z`H2ekf1^&m3b>nY6Y@QJ(x%h)tIaOHzm)Rk()rcCCBG-By!q39W13RJ^dh&nCO@?* zoBS>*zn@Zni*4$G*N2PWO<Zc^EwW1T)1<?VAL~}nj_dAXY1=Zr%73fH*=Z9Kvkrvt zKL7oaZ_nhG;{sc^1iugZJ@<RYZI|TpC3$xays4Yh`a7Y_^O)?~8&mct*=k=ip8l`% zch2XJJYn;T3g>%dI-k<Z|Cw&`eGR+2*kcBR%L_6u>U&upK3ZD8NJFLdi*Vs|<M{b` zj_(?5_4D3(|Jb-7$7o-<<KZsDiA54;#cOmozBX(T?Xie3*-%&M_*MUs{(%))zm3&z z*w25r!E;-j`GmUHKh8fh3YvNPvcmH{C(IAlO|M@5zWBw4%GmQFnue!CYkz*LXLGr% z`+s@=yZL<4=1M1Y+@p_(POQIpo15cx{GzM_kD4Rb$+&B)&3^6kVNFQu)KwdTL{Ei^ zrg7c*f6Sz{a7M-#nORO@-%p=rs&Jg;B<7;;7(9bdT0gLB*@UaNxjh1BxCd)Yne}0j zY17mPah&g#a=%-t$*pgu{&44!OprFe3dct#+>53w_$@zt%zKKG-{z|7W2@_RcX;2E zzyCX}Y>BLEKw)zAx|+^O>t^Ky`Pdr-#%BNj8^coO`uz{vvg3M8FZyixD>XT1Hu6nn zI%`&H&rs8x@x*wR-id6DvQWOxi4_$+(c$GUewa>Ml_0goZV%^#@?e9lJPX(sD@x>C z+hQ&@Pd~l1l0zw*F>(8hkY`)33!R9n4`_X`S!|h$g5^wSo6oPkeO_}szL}^b&mLAT z;1#{;@|uQ4e;DSjto<`xf#c?d_~l3AoPy&wzi)Z&Bzo=0zrK(b_hQbYuBy5fQhX)? z`a#DzUI{r|t6(_R@pp5X1oxw}CuiHqT)+A0xN=?0WB1}u9pbum&A+6b&kGrQbfr&p zk*fdKza@oFASJFpZ|RDf|F5roKfLyR@44^!e*5-Y{M`5V{HbM^vW*&3_$2;S8%m_z z`|?O|&DZmyC33w@r#izLrM4Z_E4<;hJoxE($-Kq~MSa(9w>`O48YNwP??T${zjpHi z`@Zmboz%4FowD9)QHZwC?33(Y*L~_=J1wws{i{>@PwSOJ_Dwu^Y{Q#a(~GY^nO(jj zm(Kb4uC3o|{bOf7Tlu7Z{IX$1*uL3~k7uZyU{31ts5rFYk<5Z+Hw?C{xe@6zXZrc= zq3XM;-JBVj+a)D_K5zP9boA2d?iYW|pG}io^>?LK{WkqR@%Sr}%Xz#4L-$Sj@`5$$ zR6xHa)4prc&z=9yxLAL_XWl9OV9uwX<gPt^ZvXs(&vyIMuA9E3JdfnB6#FIh|99$r zmh&DZFU=3GzN@FHW_RzY@O_r`W#6w9+1<PUS(RPWsLt!x+c%T!jP19JNc!8y9G|x# zMti=o?Uagt+g1PXFI;}jTxH9Cp`U+3mxz5AeXsiEGtZ?N-*<-;uKBW4q_Y0Z?`p2b zJ=3-}&wNp<_EE<rf7cRSjX2M`NvGAU1*bQj&=0N>yjIwzpjFXulv`XTQ(AJ5=fqGs zImfp%l@)|;)i!;9XCcuWzA59F#gwJX<Ss`=Kj><U+-VV8zu9Ge(W3n``PwXI9iA`! z((uWB<@2>0c*J-NlYUPu+9=>?-|KR+zTsKsncW`g=hJU*oY8z|%Io3<)5=|bzyHk2 zyCpc_%9IJoy#a~S%-{bw+SxaC<HW7Le>csrN@F(VsTE;h;98o0jir6Rv@e6s^OKUc z>Q9~){_<$|U$Mg7I+`o~X`<ix!(o;8Zu#u05zT-9C2`Y@zbh|?{5FewtsHuJ;k#N9 z@x@*Bm(OhFm0cgQl~Jg_N!2X=1K&)Es#|+k@JJLUYu-y&@q3*xeSgup<A)x~O62;# zpY_UhWuo$buQLWdwo3f7dLH=AxoywJckqv#hr&)<y{sEG%#Ob|w{{(UKPB>}^HcRJ z8xxfF{dx80{p9qkx>;^#XSyFxJyf*$dByV6r)LIR)!eGDSddY_^7IVR%1cYk@BIzd z7QJ|Hp7GUQ_UFx|Z&<?4H!YoCxtwun+xC=$e5tkUVF|PE)Knc;`Zq=C$EVu3fJYfV zCrpnneb3VOI@Pq|RNVgco4RzqeauUGCC6;ETpcw2<9a(Xa_#(9*L$CP8uy4*Z;(0T z6A*tn`eq;7g;@s7^^1O3{*GKCA6@<{`PLWf=l4?<2QKxu3-i~V#;;r+B=REc=!S*< zi+We;x33fC>Du;c<_ae%u7j?E6^HM&SJbS3me<n%`Fs*nr;c}4+w$*CH%t5r#fql4 zZ)jWlbk2;1`)Mmrg!7%YKJeZ+B<vdRwdwZh8A>r(vVn{{rtG)W7MUpRs?nI}Z(3vJ z+<ej1YqF5t$#&nJHOkjk#4MMcoaiK5Z&)uME%!{E_dv$tV1ZlP*WWtZm-RM+Z)J#O z>~dKPyX5Wrx6H~<e{J$tYo_Gxw<d9O&C_3-9~61a!q5MF)&Y?=sr*@T=dV_1T~2){ zRkmj8p)GR~=e4!&Z(O5RcFQt8$NQY={Ib$t-usu$UBUhP!If!hqDH=VHx#Vk<KDC~ zyD+pq$64@e=~L^=nXelcow#;$ewIey>rc^*Po#XkuI~CVDJ1muNA{~7cUG^q=lXgw zHEQmE-qN$XegsXuv{Y}O#;N4kI_)d6=|4*UnP1U){m9$&ocimH_gjB?Za7l)$kViU z>($_SxpoH&oBzD8*%@^7uC$`8{>9@5Cp$Uu=vjaDd|O#x`O^Pm-2AT+j-oS^_&PPF z*Il}^q~yYdnSDyi!V@GfO}rvE&rp@KeM(7R3%}EM56AYCQQvI)FUx8c@Akgd_2I^a z-k@EZQ!Lj_N-vyZ(>nV}=3JF6H<w%c*t>r9`8f5$!=OLU!oSW^wB`8nu3>F}_R*O_ z(Sm{A3c3AxPirG9<LkrjO`rNb70eF1H(meg?@fO#m45t>sZ0*LH)GOX!^g`Dj=iZ3 zJSUMBu76*2@{4lLz8P2S<{#&s%<y!nMPd=d<88MW`OR?QKQFoHP-LB;xsT%S@3Xdw zcMAnP3csurx?FmP_SBrEllD4H&6%w~|5B6PelNk(6Tip=nXP@T%29u7P2PO>Ys(xz z2OY_^%Te{bv&C;-@QWn9`5VGL?zF9}J|sVX?w<6|F;*8F7CgOkWcLNDuFjJy&-7pa zlVo&=g=zWI7E#-)>LuNF@%Pkqe9o-xy?jcu`p>D&XW|%}1lMNB%YF~^D%;7exTQD7 zN+|b?pZlI?;?C}ZPo}y0Fx8&-toP|!t)MJZJFj=|o3+=c@a~_`ay53_|D(CrU#6@% z!2fD;YVtXm@Ef+pCqliaTNj(&eck`@&D}`<3A1YLoovr{RzInI+kELH`w}LfZsTSr zYhG^C8T+~oq9<F{C>U{G)>+T<xlLB$0RLy@AamV2>n}WV*FP-PVYj7s|AWs<WnONq z4@io<bmv3*Ci6ch6#Y-xh+1Ad$*^>XoM(9V7PXfayVs`7f5~3X{q6g2i7B2Ze`aJV zb$#e!xj9q)gkNr?Q0$utuRer)x?iZ18Ck}l9B*@4qWwkIxj>r-8WqwP4LiQ>>Tx`= z@0pBi!(xHnDFr>xpO==s`Sq)Fn_T?w*Y$d_pS0_ZG8Z<pKC_&xy{Y%*M$03?_nJF( z`-_yn<}PfGOWIbo?&FLGtyA0DlLa^&^f~9vJ-6w(MVN8hq;wmVhKV)Tw;p#^x1Q{O zwS1M7_S%1HydU!3M2e(^DXiw6HZNH<YXV=X*V^ZI`?<9XS1|wECFn2Rp*mNamB%54 z`{i@)6AUbW>JM`3*W7YwE1T5um~*0gqfO%b&L*y~$iS)vckgW}3fU>JzQj}hPgnSf z57x#W``qS#7S>4B7B7+XN<7BMl9R45<=E{WlkN7Ww+27zWI3wuka(tj<8t2A(rZ)S zhwl9M>e1=R^63W@qW)E~<(N&YUBLKt*BNHsDSY$KT%CLO?HBIJU)@CN--peA{^QEF z?A!l~*Vb1#D}BrSbt7^9n*G++)$R|=YxeElynXlox!*D+IT*Sfzc2WmU-Iw!w=IhE zK8D`6y?5XC*4^KU|95QL9MyCG%<&!H&rICCHQIXB+uY6ax3?=_ndO(u{r;PbuUF)M zcXsw4<==l4xc}{*Uz@;i|E-+NdpWs=`Zu-~Enn+Pw(R~rciZMOU+rs593P}Y&QXbQ z-t*Jv&hzcZ>UZt?@oeU!+4di=zvu{?@HBQ2Z}0xZ{nxG?S=x}m7ZDpUr7EnxS<?9H z*Z1Zn`@P#af8F{n-NzTdt?c`U-MMx%%&H4o_O~bOe^sloJ?`p@*w`(%ch%HRUHA2O zV}1Io!&VI(r`jI&HJ{zOb?dsdzxOQ@GQK>gC9rwf=3imSq8}{wud|kE5^dPFB+F#( z-d&Af7v43wExWdmcUk4(ZKg)|&NAp*KbrpW5noT<?{(i2Ue0=H6OmB$a2>zB>j%g0 zIwlkTtV+A~%Tz1R>ez{+x|*-Je`UtS-!;`gzixfh^!mA4tl^4XO6&6>>$`<#aqcpG z8(!|iu!Dcu^M`^bI1FD4Uhz9{VTbgA3nB$icZ5i&mfUGPFjtt#;aQ=8#DxCf_53S& zni5Jj?Y+yc`Y~{RPNC{#o)d<Ob-USDEs5{+@xBrKB93cre0jN8RC@WH$D;as;x?Qv zEsAiBy_V8af2woEVWky?M>a^y7hEXce*JlPud$`+Yef!0e%WtF9FP2a=Vqlgi=pRA z{<CvQ-C`p5qMElR<k^(pNiRO45vuX)#lGY|4x41&$un;qvYv3_6xZ+NdvtOn>a+Ad z>)pr;-@bL>J=qAqUuD0uS2$fco_%DO&X(VL*ES^H>u=N!k?5-bVGx@tXYao3;d!r& zi4%AahGotDJSQss&E})K8}phxu5M6zecI(0$L$rJ%cX@JOMmJ{P8Sb<^F#6G57RV< z+%^AS$KRHI|7GpHbJzOc|KId;ef?in59Kv$+IYUOSiQcr;aa<-T<)8keYb6I-m2X{ zt#H5A?3&4sUvF;t>~Hk+M@dJ$t!vaZd0(3i0VPXkeY^8rA#QES-l>1LZ~7nh?f$!u z_xj!26Bz&P*z|v2;cx#J5ve!oSl`%3eNOoQR_3kDd)aqy4*jyXvAYw0v;IEM|NHhH zpZ@O!pZW7&@ymZ%d6_>IB`j+azPBH~d$)P2$Pc%l_EM))*Z+Tf_;BW*{|WzJWYnj> zmnb=Sx4D@);(xpLzx|i~hySTx`hUHh_w)a$w-`3R{_oyi&942o{wPSI^H2T%it_sU z@9lF%|Hps)pQ~JOy6C^p!~gB<?!SHhvp=ifQeD&VZ~o%BheiL#A8RlB^8Ve8+S2m! zB=7igndx_@zh2+Tz|iy6Iqcvf27`Cg&CX2z=fzj=`{SH4yPC39%M{z=U#6|PW4<nn z_uBHzH5)H2?mSq`8u2rHSAJZ=ik#5h+e%Mo&YrjQ_BHus+qYd~+>xNvuDn)m+vmRA z6Ga=cA9*kO)wY)ZJ#TEa#YMTSx(8o3TsijZ`~JLI`#Znxy{P&B?8)!$$?tD(eP8{0 z_Itl=-|lDEU%d9eerMd%>df+zS(170*6p2M$X;K~BlS38gW|$_Dbf3^-cG*9`p*6G zLfMC=+cd9#_Nf=B&yoD7{%pD6GmpkHH%*y|r!wC&h?aj(JKuUzcIURMb6eK`S=X(; zs%u)JQmxH>?!~54k6-yOYh5Eczw_hPwyOI+J$>oZ3O4*QI9&01Y6f@xcj?3N{1usV zR`;B3_-U}a@za6L3ogi(O4$89yW{=6jS_`RHMp&IID2tAIw+~$IJ$6G`Ui&KH6KJ` zm9Fy!Zd{&Rab!tf=ib>D1B0%Wz6)5+qr<o{W1j4c@?*{~gkxQ1zum>B#?|7kU}*5i zy39&?_rGA9&%2q6SGdQwDjMz(sjJ_3=J=BZf4*q(TnJ`7DB{7sZvB}>trk58Y?>;! z=4{TFDp%U1s<Up7jN|qGb<2G|-`JJh%f0O*-@OjyiLRV(%$r!gNNG;|Y-M#P+CFuO zXM^F{+hGs-8Dx)EW_2^3nQ|$nB4TaDrOh_>3AJf_(pEFWT;6}xxvjKpUht`}yDUOi z*1vz6kRWlh+v!{=Q*bcDnb%VH|J$+E@vgqMWA-J1r+vQKS1z|tvpuP;x>9lSo6VIB zci(aG-g~;XM`QD%(xO8Vews(-HPnZ>Epu`y{oMAfSJo#fD8Z3Wh_QUZ@8*>|j$N4& z$;i6vf+(lp`Q{jJnZFav1=L<kC}?P_<jCeET4mI)-0QwOz)!08p)$Wg!nv3&Gu)=! z7N5$n<&@mQUw8ki9Gjo8(X8pf=6A;%U-~}|yd3tbBy90FYi03^sx#CLH_UJK_DRg~ zWW3-Q^DNOK^69M8Q(V@TBwkjK70xVge%I%~TVpKsr>oz`|JnUhUm}*xyQY+yKdaYR z!jH9Okz%r`b^Rl`>>kUb<}<FizP++RPPDrE@+*_~>+Z!Ry>&m&b;f$ZzAtJoOu4g< z80^zk3f`7cd+OWSw~Gtw<i0Vse)_#2?bGbO4T~cUBu^$UTioOEzbEO-7vINh58v{Y z^xOViTBs~%m*B9l;@_&$n>&7r%HNOfd*_?-@&oIX@cX4v)A#SQs^4GLx}NFu%saQ= z)&5v^`H#E%o`Xy5|GC*r^>^V;eB1pj^7S{TDKfL#Oy%01-i!|H-pl)XUVQ97m8@Iq zF7v;BC>_Xszom56^zGt5M0`EJExEfV(==mV?WVg&F7s8ZDCCz)>}o2wvew%&V}U{3 zyH{PeoQ|D{-o;?_$n~${g=U@8^=`-GHy%7${wMQB-fQ)LFYdiMy?p)o^-^Cpr0=$> zFDonk{c2AqKd<<XEhp!j`A3WA%xJsGDtKn0b#_Ta<O9x!ll@*h*|}~G?fJRr<~tL; zbvIY+%$3<E+<$X&{Oq0&A3WYn_;U29mMdrci?xP9`4_IdstJ<j{k8OS{Q8&S{r`W> ztgowmGWlcqthW0<t(92k3NtZ>>r7<ZC_eY|RP!xrY&C`^x4!hX&oHlB7|;Jle4*oW zkBk7@*ek!)|Nd(B*Zf*{zPjEudB^kQjC(>8`+izyeV(`dXP3h6=M(l;Iqzp{@{#EL z5YO`5w*9kc@B6q74f9`w&!4Px(DF&aJRzM}zSHOHS)LzXm|)a%^g}#L<aFmU&$SkN zrq&0Yw0H583Y^~mVoJfarxw3|>VExj;(qK!>*@cKbb6itw$<_eSRX7Ee_g!gw8ymn zi{yCj9aat8r`$2`*})&q8(5AB{Lp`O_sL#kJ%t$#lijjEUEH^9l5O0lulHYE&;KKS z*17-u;q0be#~0Ss`+m9k_{&dW&VzR1jrM|bYWbd9o}GC1`HOU^`|gHz+Z-%)?1Ss& zE9-r;jy_rY$7jFYr+E8|8NX|`*k4@p<>#6`3or8h$haR+&wTH`^sD>>zpI@kWD0op zExgCR=ev2!bjIVSrC%i;sLr_96IpXCD&*25)o7Eb?*47A6&o$$qFCy?k0xdc>WH;3 zb+wIH#2)otEdITH;NE>Jj#p^O-@pIjZvLOU_dDY1ghf0fex2C(#rW)fce{G~%Mx~< z4FCB2t$p(Mu7GOwoJsY|%<Q|&GHc|I=3KgA|7qiYpDUjy2A@A~vZ%kt#s2cDUlxLk zdO1!1bstOkY&a*l-t7JRIi2>3I`x@-Id<;PP3&g(zn{|?plGwQeD?P`$H&s2KE_^< zXlu$AP-~MB7I#T^_DdJ{S@qGZUg^XynN8eHsV5S5D!k#dEYt7E=CQoKW8uZBnl0ii zUne-%dM20uxS_w2-F;2@BDwb9^Qw+-3s&fNR>=f&RH;4xY5nT_!@sdYY_fd^y<gSe zeYkgf29KEh;j5beigy1IKfCO7`sc1=&p&;P6*MokG~cVlvrDG=`juxL+bew^OMm<r z8+=kw|L3fGUyoQH`qPp8Z<c}mqZ$9L&pvx!W0t?7y#IThv+}1O9eFF3n2Tj`2S@zS zKeXVy;f-bYYVTFgKKAbO#_gF~?BsK2SAD4D&z)5-I{&=aY`52+FK+8RSEIOmrjuHT z#w@Vl%AlH>=~w0-{+%r}&E-@0Y_})ff){Un`FSfR*fV~L%Bzo-UrSbeZ}@(1;a71E z*BJ(a1wJJl!i>jV+Fv#Y{Y^ck|KGJlmcz34=oFWQHilO_|1T<UtZ*-2lR3xt(D=cR zHy3U&+kLC&OEh>O@#aDXv)!%erm8aw_br{29QSEjl;r2Iyw1)2mg)ank9|0B`~&yF zp9dcPX?;-hpkSY>k4=%!9HDc&WYWRexXSyhT;<++fjGOuJ%7AxeJc3(2c6{9|LOng zA^*L@_L+JACE8alEvPWIHqGRk%&caTv{e3q$`_ksF<a|hqq}BZ+kCC-?xh(kG>pAJ z>dIz6b<r!$n!Su|8voBSlf!Q>MJ~AG-PGCr@8q{__Hqlo*Sr1BPFQy>X8nHN)W5kO zQ@mv+?LHb~6V1=g9__p#`QxqGC$F6-c3&nQvF!i*-_a$j{X!J`e;oNxI_q`hE~6Gv zfx>bFmXk&%F&kG;dQ|T|zir#{Hl?p>r)$LLy_z2_pttnAlf|j#!kGm}_1~UxmDtUw z`E30Y{;S33Hvi4}xT)N*;IU}OH{Y_jsBQ9}0>AfIZwP*0Y@L48Lh{+}<-zZbeHT27 zc4W7|XVYqO$k5AO&)(2r?u5zKi##8-x2w-eENzyMo*yPvUuU9Tx8~E#`pnsqQ-i%W zUwPx=d1hXpp8NeHdtM(1Z98{veQ?c({rz_p%FWLgow1%E?0j1DiA(r-^KNGe%U*+O zbx%3F8MlQ^a=xnVkJwk5e>2Qr=B~z=1nXH_E5E*Y@`lCbWV2b)vNw+BcW58#^SIDB z|KJhry>r^mS6aFhed&vf6NvZe);>{x|BvT$Gq2CV$L8GFxkxGb_P)pL>s;CQSVV3( zt9AHKN&0L*dx<A+cKyC4pLQ-=`MGRWLv83T#aDto8SH5j;x$B97nMzx{@d5ee(%)m zl~bQBJ8N(u;``ziCqJ65J$GVr+b;8+i;w(`)Jn?fzgqrI;(XAFPo~u$H(P4TycOE- zXA@rk>~pu+<Dk5m2f1#XFgfXw%Efs%!t<E+nVH%?!iOH7=xUrAmuOY8^DEbfUp?B- zCNrhEvd5dss;paZ{wWv#E?d8jB`(52Ru`u(G}+l{Wd0|#M`3yM=O+qJzRZ=YauQfl zA{)oM{=?fNUCEA$C#@f(TEtek@0Bs$|JiP9<fbY2kJpz@Tyv>w?vJ>QzCxSK?wV++ z=<{Ab^;C%Sx7kYl?(B(+3_4#=?_yv)BzI)})a4y5iY_x<pIp_Psk&nR9S!rA!v~8m z8(3NDpE(9v)({+#{A%ixOylVa|C}N|`^d*XaVgVWDl{!!$;7zsb@jqYf26+Ok=W66 zCwtq|ZbL1><to<o-F*JLLr-si*}2B_vQuAS2hTl$$ak*dzutJ)Y~8Vc&(Xi7`metz ze7o6X_;ZeH-KUACD~@kzohQ3=W{FLMWUiy9%7PBwbymU!I(NDSiWjMd=LB~5ZTuFZ z^HeF3&z!F;$gALuPQ}@>7by$&JV;SzPPr=mqsU$2W3k`Hysq6%7wUaGi&ikQs_mOv z624@ItZL;#<<GLMg{}J*e*bJCRd1#~lcB{f_?oER^#~bNE++;5)w7pvy3cEv-+Acq zA@^VEUoT#`GWSLP;V!$6%#Y6*$RAOb6tLcO<K<V*HHC?~0mt~~hZbfWSyXhgP{sYx z@*mO08}wecWb9;Bs@3btnp9(0|G!ULK~!Juo!_xzdf#qzv9W52FQ42uNsQ;V#rp@2 z+vZ-=KfBv+^<gC?X*0vd162kqg9X(-cumXbf8YGU_xZBIn|Apnv(gi#W&O)*Ce?Wf zhtD<lz2&z5P<M*f&9HYmho2N#7~Of|aDH9Jd6|D5Z)>{Z8tfl^SN&b9c}OPnyL|n> zj{E;JHgdSVm%rA`p<Gj1byVrhyS3Fz!uS#%7nRG;za;)|&DFF(xi79QeX;WlbM{`8 zbyisURam>D-qk{aJMGB+N1o58*uI?8bwKsrzsei`DrdL0Ka7|AQgm?MgZO_f%8#s$ z95FTJt*=lxWVibBj{d~7nZasjz7{QCdv%|6ZoOjS-bD{OrqyRTE)7>=5{?Uez;5?7 zwfEcGuU{)aFRHw`GdWO6cfYM%Z%$N*tohP8mmDLE-mov+vFptiz5GxQ2iHw=HZt+* zA5wUf@4VvNthbkEU(;U7y6o5ecHIu8D@_%zxsx*Q$C_C8Zf5IVR=N1MxQ&=&nc}_s zd($q4=te%N-^#J(P+DE)y)Dt<)Avk`kBI8dY1zMbch%Wh58kh@+H@rKbMB%mOMlML zsi?jDXK88a?3~ZHjOBI8=Un<$`gobJ*yX(rdz=q^RBe}vZ=ISK&v<h`<CSfCmA-jr zCD#@tn4i6VcJ{NUD~*`V)6ReK3DA2Ya>B`k{nK8f-tgty4JTRFKfb7P%H@+&j?&NU zc*SMiudh|MOlwo#b3;`{RqKFe=e^dg^_h3JNIBd#I4S;6=i$YNeht^>Y2Dbf>tMr= zW0hOpOl?2-ahI(29V_OJ=;Z-XYoxaA+P!P7j?|n>&u(+@1)dEy>|nL{^<nBRmOO_G zbMEflm-w-E2aCbk8x?Om1MFk!r-g4fU+&A9dt0_THn{QgvY+2?U-M;Q+O;RBv4N{V z;KtEuie7PB*PomH>Pzh1-+J%dPXAiOVXAQNmlsE<NmVjm<(dqxl|es4rBhyMUaWd7 zSAKGrk>sM!&tCN~>|QV@&EDsH#vzfoM=s44>8qtLzwzBKdM``u(8svB%5|M9IkoCl zufAH<UZcI_s^8}?XH_S%Z1XvK<{bn7^X(h2)~xiqbykN%sab(n-XlO~*}0mY6!DEx z{kzWVmQ7IqAUow_Lu^im=%JFO`|q1YUgHbZz96tW)ojufllgs<^q&~DFAv#V^D(>W zxW$onwo@}t995jbZN8Xq;`6O0ai+JF*0;VndZ@nlWcTCLVwWtPAHixjMKU_P(jVA< zd2zO}Hm!JxZ_^Y-5iy-3?tDi!@hsgKdH7*O<E((n%N4%CNflN4`ECq_5lwejue4@Z zl6t#hChLp{hfaULdRBGMny-t6O^mOuc&nnh>_Sq&>>0(1D}0x%`CM|@XDd(e-=MC? z0dn1wp8Za!&&xfjxcRPerk~K4V+$U&h%;%wo7!>T;8jZC+<6Z-svni<NZoNLVnT?O z-_L*~Io-ktkD|%vK`X>NEmtJn?%Vhyj<rzaujuIq`{bjy9bDHjz27jUt<tq<an_9K zD*HPW-=&<?KeqmX*{4ZAdNma!gC7`jF(ivLv-p=wURbL*v;Loe%BMX>f$Ud#-1$Uo zk{gOPFPE`kxIABQ$yBKeNBONI4zO-Xun{XheDeB}tZvbSx}9^BZ2tIYF1CsgHq7v{ zd}*vLr)-wHU0J^*C+cp~!~9@w0YiUXR`1vAB0LWqpZnbISLN-<Loa6;`(NUo9qeWm zYkX>jHOtdAn-{5<KUrVDE|dGEcC=DzK-Lk?eJ|YnSA7%W-Vplj@FK-s8+Y8f+2_O9 zo04<v{QAP<6}P3i56mgQtXVBzc8vE%lFyvp<fsR?OZ)=NK3|mob>nGlVbrb-GB&Rg z%qDely;t&8VJ%5pJahS`vpyFVFh5#ZR;atz`oi40Ib5f2{+Z%_bnX$pdae4IUNbLl zl8H)xzV*m%rAL}qxOXx>z8a_^D*XBI9|h5}JU^j*8LN^Pu2V^UvgWnL#B(*9!mVbC z&hAW4?vnKGoOU<XCiKa)N8gthH&5nZJ~hShPwkF!>9*_M{IzvmF%rw`7lrtJs}_2i z{;0!$pJ&1JIE_2Pk6F`i?|U-!l8tf2+xn-z-Ih%U<M?g7Et>qL-(F_BUH#@&UATG5 zqPpkrK7E@r|BCR0U#gS7y_Z^k$oS2Ua)X(dlzX<Go3&ng`}0RO@`)N@K?QcbH`TMx zY_L+XW8qwK=IiemuApbyt2<ub={>2sP^as}8`tx0B@EI#*bTOpn0!%B{}ZS+z5TqZ zqyGZedV$16?}GbZ&Qd+;GyCkH(z#npy40BsuYI!9{_Ezs$J3wvl*bQF_wRL?>KoRd z`yW5||NdS7Q%nEV|M^p&a8U1FOw2J^hRg5jEp5E+{4d&2y~pZ*y`+5Iwtx9{YL*+D z|8{a@Ik1>27aV=~`=Q??f$8DOmoqOvz3%I#Eq%k|ZT{Q()!zRUmX+LNn_{$X<0}Po zQzfH2xksaqJ~eEuSYEoicIn@OEt^+w+`y>8@IPYTvl(h}&9(Lm-<e*kU$Op_^o^a* zPOMKTe_wx_M?YqJ)rCo{o8sK3%H9h-d{Oi0l--*a`keg~%XZrBkydWKP18m74}nXY zeay3	AI5Exb1MPyEUH{~+>z%b))(*1R)xx9#9EaQ*lGy{!C^C;vabyLi#-pZ;Uz zJ#POvmff;p-FZ|<bym{9$L_Nmmwc<p^y+h1nG<2V=@xTu>}TTx$>-eC#GP*(sOkST z`NN4%CqBM^QJq=G#C&7^y{#6!^PHyT*le$EdR}=r`3h&r@uMH?wWeOqtQXdQqj78T z&eo--cMTG5{B!yncg`T-MNg97R(EX)2DiZP2McG1*sm8hpRt$!?(yy>hnrH?Zhp7d z$G*R3#B+J(J+semCZ1yBy?ifQ?uz)X-dj^Mee!3X3u!NFU*zStS>)|yzL@hm+hYYU z_30_|G#2fe{8E;o&?X`_HM67mkJH<Fu>jMi$i}Ht?70`T9kMZ(YcR@G-sZ7AcGqR@ zTjlaP5!)D*83KGJPPnOGQmB}qz$zi~MDbj-=O2T^-&kJpb}wG=x-Yl6?RIJVVMDgh zY?H1`H5RJPGG}Mllhl2*J-NU2<Ro|TgHe4~&c9r7R-v7<{7l})eMb_it=g_G4sAAL zt7px5<j0nuUN!&o7Zx^7p~9TFubH;@`f|ecH?SQK@%zME@3T94@5Z^8zwt6J=7?U@ z)RgePux2H9($c-Ps^9MabG-ELcJ$49?~VWK0^ZfzTKxF<g2Sirt~_6PIe*ye*SEL) z&q~|BKh@H<eD@L1(Iyse|GjViyLj>3y^odm{4cHlpYh-NZ9(e)wSVfh|LxcLKmW;p z?f>_?{tN%#|L^B=U7uYwLI3X`eD|^O)PMfW|L-$O|9=m%J2ZLY*X|u#D%Nk?Ugr5Q zphxD~6KSPMTfbFw1Zt_@y181qVr{H{6XT34&#&!e@4tR;hjVeZ%fU&Hyqb?6KR)Z~ z=j?+&E*j09b#Lv<`h#WyGnW5uG=KN#?BjhOY@OwAXJ=e}>}V#wOFW|Qo0#Ew`Mi&} z%d~NwzQ^7+H)EphzB~TOOUe(bJT>jU^7sam$-9uP`(K`un6T(A<4pgxJue>JxU@2Q z*DuF8b=!1bujVbCWiFn(wy?5I-M)U#R)a0;);>JRz5Q)k=<dw8`!RFx1|5l=>NR(( z!HoA;b?rQJ3$`ztJJZ(6hO_qn%G3*d&m~r7{A1AfJ5s~GtvkEQxol<9^-`W|TyMYj z70$Y_@rhJ&k#k70Z;~vtKF{&pHiFH&`fu-;cKBQM?K82DvUgwTf8O_-`K|k|e8Jeo zO7++Cr1q?v&amTkpW3{u@+>+%A&YZgGJIcIs$}64(ef|(?b53vaeMZ)U%&P_Hn(rN z%z-oe^0Iwj88P0uA-LVE=)UL{tp&v|PMwM<KEv<pYOr>S!=V+C@RRSGS3S!3s5$qU z`#y{7om)?6b3B*Xa-3nsLitG#q<Z&G<Q7qqI@hMM;D=0jMYgEaqV>5Ev)6Eb7yN#j z_jGfl+X9{T+VzVjYb49mhrTUL5Q=K(T2Vjum&f5d%7(%jxrdofWJLXKG(99%9mc=G z&S<Y**~Y>hpZ}_rzOGW6zo4S|F@rOY#y-Ynq7`#8Bxmh2mvsob-n6p+%c28weU0|- zdfc^*G54YQ`b{f?c<)&-*V;uKleqoOWa_>o-kI|gOeDB29xvq8y}z8*?MwY>F<IZ| zAwp@(lS4GZl>H7Wb4h(&U|Ds0@yuTxv3wtI)W56v`nV;@5_GgZbDD#l+ij^NC96x# z@4q#<t3>asZM+?DHDh1y8q2T^T0er$zv{32Qq13PdQ$Gz)xY@;NhcS+JlA5ItXIM^ zW!t_swPWud-xMiqYfKM3X>3rC#8g*rB^v*5*Rcy8XTuhAbZixMcAqYPrzufuM~#Hl zFNZ|Axp^XvK2q0Ndp@XW%HK}5X%V_|rQ(Chmp!F>`#G0=<>1eBIbuI?(_vK(q1pV# z8yO^5{ru3L(3`dE#{CC>JRdF(ox<nwB6jZNRr4N1H(4|9x?rhy!}zeyyk9Pt7qBy) zimE@fq`~gm6*itTpfl~w-dcZ>6>)wu+r&*-Sm7s2_pLH{^UWJw+0R~>m?oukZv7EW zuHSygMUVTfac$E!G&sY2@NZZWFPrt1wiS~;M!cD{O#1qR#|K{V1<eqf_wB)>ySt?3 z^|`1tu4W53aeZalp`IP{ez5u$76pbEuL;~a*Jbmvdgtfse7gl#Z4Pb-%23mcfAnXo zc%px(ZeLtqY>aNrxs}p-qP8I_!Zl5Nfi7LndoF%xTb6!H*hb@{vf?+n6?`vw%ahGn zF9ulo`oEtfux8l{E}@^C3WC+kZl7Kz<?}$uDp2d_k?XTkWHk#sT0XPtmvD2+n@P>| zj#6g3{^XJFu6my7A=0N(dDpGdv9r0^?)3NV4vSC6zaO}45iI;mDp0$(<G@qzJ}Z@< zQvwb@3VF1V_m|Ad6<Spqu^c=x?%&lEcC%~DTK7lva@z|B0kdOXYkZgSdv0W!>MJd_ z=XsOC%11eOUz=T2zL&^n5n|>isJ~;^CAH%`f}IsSe@@(c$NWmAzH*V-!QiWQ5BCeW z@t!rxVcEQOL8z;Vx9Fs|&wgbpag_9L>{Qz5y^B%*<Ow!a+sp5koLlARXWH89y25>? z=&o1yKJ8APzeTPrgYRSUx^??i`R0i1e5T&GM1zkdHaYLpiC2AhH}aoh%DK>gqHAx! z>Yml>Ku6h^{ZQCFeV&@a)I};2GsE-@yPSe69oHJoO+E5Pwo`25CGOQ-T8Adq^Cut3 zU}pGqZ}NjQW&W#M<_Gw?7Wk>|YFV~w>+#7#>AFg~SB}bDzp2FaiP88+G4~di_5Exc ztuMY!*mU)?&yUo`MSmVlPE41q|2eavw64q7wC2!awpA9^+d{U4?6x_}GkH^U&vX_R z%`BdeJHB+@4OjaX<(~La^!zKf?oCUQ?=c<Z;YgS-xo!5Xj)x92OAY2PxY+nFVbU}i z_6dJ<^o~8AXkeVX`_%u`iGOmORO27Dab927`S+-Mk493l_JRxU%F=ha*D<>|Z*HvL zGqw5F)>7_gty6s^O%i5H1*bcHsyu$gLFn%04(%^C54sKsnXcWD6E1n7ZU4%b+g{Iz zdBi+>-}CgSV%g)L9*I6;7JcaMa=}1nnTmVk(^%V0lYh?8S)aL9?vn4Jwy%woAI+LL zSt6pAZPLB#A59Gt-(9%L!CH7|+Q*pt_mz%3`mn41oy1q)Z_*lW*K__}l#gaybinMl z&y2upEYdzf>RpBJZP?D7ySt$A$DOB>a@I@xJ#*m+KXZSANqx`eMGxIHt=0u`J!g*Z zJoJJ4x`Fshr`IW$eCH&{EjPU@&ZVceluK#aW`?zk{^l>)yr_9rS@J5mlTwW)SJV}s z_ZGW9xIFE`-=O-RDSgv?f;(TCmsqi>`F!b+lK)dGEaiEn_E5-79=~W_-!P|`Ng?ga zPOez`JZXvA<zg0p+mn}!&A#}yO*9NBl0V-3z_?oA#1W<th2T)%o|dcr>fetFE}Fcj zJG$b(WT5h+4R^FR7_=v5oLV5;Qz!S#=gS0_gL^j1EOCA&`yu#1z0)+E=|bDi_N5v~ zEZVTQrehbE%e>C^f{@m~i*{7qFugdLMJR}2QbP+%_)$^$EcVcSyH=gJ#w+~wLRQx& zS8rvG>GsDRCHBfT>wBBHm?o>Sx<s?AdAMmt*yY0ym48XHFW4Ly>^>tT@zcbd9Hq}w zzZ~VQRatoIY3TCOTWc7Ke~8pys9E`ZQ<S4r+tsV<HmsQ<I(r7^tmjkIT3<U}H^`b( z!>w32d*x*XzkJWxX6NQ~PTt6P>B1`4XNpUAewpbxmt83?<Fr|+{MpA_?i$3IK2chb za7mL(CMWiQ%sJ1FgCSa9ixYqOheo(|nDw4_kzF{0UE`<HD!#t~%pTt!S6zK`^Fn>A z-r9=Tox0PTj>aA9QgMCF$loD5Ise?J9$lF~F6UIOorFV_0}lAyT^+b1XZrkUb8Ka~ z=1!P&G)C)^B(GWgi!Bblda^eArgbj%Q>u;*(s|X^c;9wQbm%F+OkbYP<P9gdR&R>@ zlz8aUF|RVCT}KTZ4@&U2&J<W^aqz*6x%$iO>$|UCa^ZL`zCg2d<0*~QGP|1=-F=de zy54awTV%3@>FHmYn7}T}gy3ZY(!v}6pPFzXRA+PflBpAuB2{D0Yew%{W0}f-{q0hR za9_Ria+aKGl4?s{nNIVbcFR>DXwO+ihtAli9cD^v74A4IPG);)c=fKj?wJ`&r&&*I zPucQmuIaP-)f`*%_Ak3@Wu_+jFlF|9hK6;m?*v{s-kjj`<Fs1g+t|WYT6tG|u4i6S za^`r%=e#WH{_5TOpJ&{Osa4$|dMMe~@bbY9cj*o1mr5t3?(FvZ^yyyeF|$p*b~E2= z@NtHmI{eM|aEkQi9e0A3O)5XLMXxS<lH{yywHt0`Ci?#1Qt_^jo^xVenvqvaWm^3A z8+G}2($3s)wq_F36ET&)xijgx#JZE4v`?r6m1Ptz`q5?){Q1%C=3fGTcLg!K<m>ng zJhW#F+vgUMxKZPX$12bF>gB;5$(5bk92R~|)C*jyIEjJv#L){1O?+vsbJfbuFV{;n zdF1Dk^xtt!oZ<bLT}wMQ)tB_hUEgASTK%#_vt`w-=nLN&>xFmTcp~sl{mewid6Fgz zC+BpoXzaOo!8@8)ea>ekdznLQeT;K`BG++k72EY#*=18_U6?@PRJZ8tt=hL%yY0T( z=YI^_G5Br&WA-AB!Pngq;c{ZR;L>`1HMb1G`^Hmp8w!qIvTM-$p%LfnutJ6T`e`xV zhLT-PE4en8c7AL&UweA~i>}$L$}aA)tYr=`kK1}KdTxBDNc?uI>1WNjqqC0OI&?cM zYWeo(z0!MBU;J8iH{}-F?Z4X#)BDmFM{UV=x1N7SdV*h>!Q*+>pL9$nb12#_2-ETU z;w<CEm1^HmdAe4<#NEX?=1zp2r^fxaQI~2DwFvjGUl7Y~dO+#r6_4dl<+DD-`7oLl z<-Pn~(&pxyy^w8D<E|GQjDJT?-k2+Ga4>pysHAFeZ%(3$O>gk^%WT053i!`#d*^AW zmhk(<d9OGBIR%({N<L}sQmg%InD>A3qg>7UYa$A6LZL@XuB^Xx!@i*Y=Gn<Js}^l< zIS{(?#R{_^E#uNpPb!~QvfNOR_<P8E@0E`(D^xibw=qS^FsH{|=zg5_#8Ng=nzyD{ zX-`mrk#m&@JCnTS;Rfd7TRRU&9uVu<$K03i^C+1)=*+w|P0R+)ZG1dm*Lr{6_M~s$ zvhMeb|JMhKEh}5}zQ@>`;rJy5=?51LuAKj6bn4n)K~CjkY<DboN4VWO#O^rb;Wd?* zut>da+5SHc``8^c=Jh+D5d7nztJ<PSj*krU9UZx(JDh8Gw)=Md@;vUx&;81+txSS- z(qkXj_Rf2W&u!Q{EzK+TThC!iD|yMOwa@qaZQ&IGjz)s@<u~S6$h`CT{#pI7LRf9i z#{QCX2h+Jc{>5g@*jHM(>LRaSoU>h#^O;{$tlk^QWv@2fvG1*P)=49sx%rQJZcd7F z-WYS8-H1bq&GOQbjc2!sH_KI?=@L0GzxVutokGHm+yOlG_ZjY$C-d+5YV>Hz#jB++ z3YG2FEZE-op!$j2dHH(prh6}+*r|WgnPgY8C|zN}>sQ=|{2ML^ST!eZztKE*mF|Qn z&wDPjI;0AxESaJ0BPg4Cto4(f<jq;j%4hld`v~w~^pRQS&Bwm$_j8ravZA}b*_W^j zd`ft)6+FK-;;r!hyE|R;dtRICJM?uF>G$Q-|9mG~_4HZ3cmvmsSuGX^>-%=O|BPMn zP;%a@=QlRT7G$5h;W&L)KI>Y)#IMI3c3->Wu+o0j)^b~Kem~1>M>Q>%77pRrjp9vh zWe%VJE!??2rpdLt=;z$#HUCaI+MVV)zcpUs^|aUZB97W~4*px~+%$jV%tKX&K7I_S zJw8+X>b;sr-YxlTZ$h)%e_R&7{*h6-zV<|mz+Wd>9bF3xwY4uR9cE}hEa!-eGI`4q zwz!t{u*0H-dZNZ>i&q|&6MQ%`@`9tVuax{72K`JXeV!_-0Cn3rqCOkH*xJjfcN%=% zU2wK;-b5aw{6=?glb}jJrtj7zwgumR?7mRmvPV}=DSr8h<-bZEDnyn2?fV}iEw#h$ zQ`X%7Z{MG~sNGl~Xy(_yP{!0dKDd}g?W5kZcb#2!+7k^AhsCHSM3tHKBpuJ)yk$aj zxX1OX;_|!B^F?mDiuG#wwJo;W)2s2in&I5J^anP^am^;aE0@fAqT$9^a6dCD!Rwsb z;YUh`e7DcCo!nPgq#b%&ao(aRzm(wXp6kxt-L>9iU4z=Ao~J7$Csj^6#x^-NNU(4B zjt430j{jXUS-Z%XNw|7)LXn2asmO09bo6$}t5=Ban>58X>d?mTa?3ZcFT2Kmdvce@ z8sWC9)hnGRrFkXqiHm>W6l22K@%8cKJw-~qYv%1}sM<e&j>a~($L*6}7s)eaU7pNS z?8?WexT5t-R)J!!N!|ay<&)!!b=Bgx{raQaQhlrU*nHF3zxGZ%;{PM1<C=)d^1cRz zJMSHs;}+?z_**x5Q?XsW{ei6q>X@#wa_up>!Yb9ybHYB$?BxAE?`>OZQ~lH)uZw0+ zRq}|{ndHkB8}mM+scv=m$?D?OrBV@T*{iu{u*SqMD`pP#*ng(8#PEkrP*ca_ih1g# zA3DCbY`hlwri!ajWyNI1<DANM8htH>$M;_Okd&7m^ZRagVgt*A|G)nC*Z;j*|JVK! zuR52<q(?g%3m6#>ryQ9d|G#aQU{R^^(Ng7SuhK*=^S*i!cj&|OeFaS~mRzk@*iq?h z(4-<B-JImI`SS0Lor`#nS{|_5YR2<=m*rQrv#p6HyC)W|nvl;q<M9&9V4s$$5|_=i z-skc6BuuuC5^;?Z*UQ))bNZ-o$!^hlwvyG-(vr8CmNb>W2->o1gIK)vm&uh(J!>-f zjLg=R8=3k99nWAip3hMCc&n4jqrL$CBMk9bTxzH8>{rgqyScYK;q2jgPQ7y9rFJh) zK4Dt$R{X<>$G5((GTh#E>5t&K-9Hb@OwWDcc-{6^;qu3PH$QNRMJ_#lJ8H#oIgeEV z(#Pv%Z%H07Ut_JYw)pVhYG$riJC1GB+HYfEbJOn2YTdO;62g04UbOq@uq3K2m}81d z@DHx*A;x+gvHjWKxvQmPk2c3ozI>ETbLy&WQQr0~l0STcmT>a3tT~cgzGP95ytvz| z%Rzn>;xQKmJXQY2G*wGpdfPEkdbf4Kv17J5$4=gSQ*ZZRLZ`8_P;F>*{UzVz-qzdx z0itqji=NMZVc&b}`t%<w3O2?bI>};pl(ju@-N)&=N<AL3RmnGQoiVVBy0KV1tYgMD z0o9`?+9STcw~=~X{_1v<3*QOv_O%9P3pem;#2fFeDL39*_xaJ?+q2EzU-n$|{apL* z*SF25uV2rZ`Q_dI`rUhW|9<=SEw>Zj-rwcd!{ze=vlHf@J@)i<{Jn>_?f36JZM5e0 zzq_}iuhdycb?=+KaA!qtb8+GO|5e4m{~dm+ZDk=j#qaCk-QoHA@hR;bZt9ahukV+Y zpWTv>wA^EhjEBCu)c%h@JRU~~gzae0cwx4krIh`-!xQ<+cjYIvf&v!RFDic#!TYOz zLV4Umd(+K#A6|U0K4H(c&jM|)O$6R|q)dCU?>J-LMfY`6cmpaYot5=ok`nCjai!a1 zdk5X<4|o1PjbhE#Des;!Gp%a&Rkv4J0(A~y6K3hm?Pp8>GkI@R*+1cvEA20>5&eI& zU4F%Vaq|`7^-0}4wO#3tH)<R!JN2pFe&Zj1o&UnSxt`YVs{Z}(+pAxF+xOpSKmFfC zK*f{qX}wgp(dz#}r=QNczfM1X&%S5jS;?ChKa;#?7IvEL(bC(z`)``AD5>1}>v`J; z-}`5;|KIX<?`*mDuXj}>+rH=dFBed@om;j2+vW+TLKlkP&rE(h=lwE)UHPtNMVB1h z?Y8vRpB3@e-M2hK;MRQYeQR&Iz32;fUMUzJxiB%=S>}(ms^j@!ck7P2uHZB5^BX4F zxMtld+WuXr?ul4)Z<U0xRNb?e_JRKYKi=%y<;&l{NUpJHb9WU(s_OD$arHy}8|Mjq zRp66Ya4{rk=d0WMXZ$(-#9r;c;ZOabf89xouAZ*{U|g^9zq3h)cgCOa<43nyJ^tVG z-}vA8ANG&`|5f?>qu|^8g9#dKZ|xr|ZvKCzsP)g94arkqd%DIfU$I`~wl-Al?tRk# z<P93-C;aC>ruesBfrIJs|F3K9Z-0Qu@2}D8R(kyZzV?59xxeAN{tN%uufgLs<v;)N z<i;!Y^;6t>{$DS=ds(zT<3Ys@&gWHKR-#N7bbDmaw%IdC-0kJ*&N8#RWMFmW$-FB$ zYP^{#j>bx>*4?bgxWL8sF1o(pT+1Tu+EU9HP9CWv)Ba=?#Rs0BP(Ja?6RVvj(ti7v zb4qbo^DjPrMV#&4tlf%5|K5A_d@tUoU%g`fXSZz{_RH%_s~YBCHmc8&uM&RX>ijh0 ziD(l~=Lcq<1?)`^7QZ`mbfaRSy`RMfCCkUnhA02dzBOge{To}J*c}m5JJ59OZ_aA{ zt8yR9K0bTI+xB;2&f$nh4;`Xgzn_RU?pPYX+8}woWdD5M@C`02>e~<Ho?ZI&LcyV| zhf^2r`*zk%Va7I}aFhBu+iof-Zuz%v71yF%vy6FXxPIpu*`0dwU+9zl>i=~%KkZlj z+pV$b*Zcd6|M=I`KJPyM>U;g7zy5NqulAo_bmZLsR}=qlJ$_tMqFP>R+b4^on}mdT zKk&yGCm!OJ>Jc*dcm7TNnSaOEy>I*<E@v_E*njs)5}W>?WZL}yYVqF>`G42er+!QS zwQK!a`->^oLcPbX=~idI_U@kVJT+UYqIcpF$47^5spNB*N?LW=taw`B^tSC>?);wx zm-g^1@%|(lT5_s@rKe41yXo}FkHn;wizlVbVe#pD=bR<vQhfNB;fX5os=_YczR4`j zi!aYhQ{JTZz43`!q6h1{Pp90<Kl$2~WYsS^=<`4Q*Z+C{^ye7<{a^j$ze?XHh2Qly zGHO5m^Emz5Zz99{kKH`w&-|D5J^!vR``>$Hp+?u4|DUaw|2I6z!ME&xr_jg$Q;sb% zn9})wxr(>X|J9EddC4yEZ740@l%2bM*R6ujLGL~+X8YLnz%F9ePsXkF{dwEwidX(9 zWzGDpCsiNO{!_TJJN`wygM79!@2ML~Z@W^&mWqnb+4VGd`P8Ml;sL?u&az)D4dyS~ zW3}Z%*LTxLYX8EU&fYoGDy%;<FQI9|^cn3YB`I8CyI;Ou{nA%7pL^2c3wOnWv#sZ6 z&&qn(`8&;{HhhBBEw+G9ANudSE2)TbSaA1^>F$`#H+Ra%)N?TFKmWsDF{5q09goQF z1F^09X7c;Cu6Xpp|GKV1)}t%aKmIhGtao?Tq(3_<j_&$!+bQa!*T?(sSQ34;B4Yj* zuhzN!ME5y!#rEAX>sj1<Dn9pa{I>t)W|NXnI%ZLV>~kKr-p{*o;868R{p3Q0<59=H z%bm!Oa>$4a-@PxiYNgKidT$fa$gdBy9_I-w__v+ieopXxi<s2Jd93EDC!?0%NjYS? z{Ickw7caMXK6pJzcE{~^292#J|NHz)|75>gYuf+JwzZ#r+dKZ7&i3Tb@yY)$URLb> zZ)N#YUY<+wW)Y+P+{X$Ne*a(fwyt%_f6GbN<ejIVSm(%+YOk@BLHeMU!mFQ`>Uqj5 zwl6MQF1RxMz~LK?b8P;)e&JHDuit9r`QUNQ{0n8?Tsl$p7WMVHrF)H-UhMn){d$<) z%Bgb>{<Qu#`I~EG?ldWT^RTrnJ1<urRIjqyZlzk{q_szVU2I+KfmUUXfB$@sU7j6# zVr{R(ouG>;f;n?<1;1#!c4|F`PRZp>A7^c?kCS41cmC(!$w`aupGs;p5qk5;F7u*B z57T3bXEL<`r!7w%wcmK$azm5chUR%XT#M(K2>hS6WyZPm-QFv2Y%!a0H2HwaUhjPu zYQF~lU$S;N3;&~drG$d`C=Kn}o!+&lTSK1s)H?XSQjy-WsQtiugQ;DyKL3+{#V`7& z-pMp)kI~=y{+IO+mt1PU@}I?1s_CD)m$PR}%!d~*Gb~bm-5307ufC*sm(er%xw8{0 zk7Y@V96Yt|)z4@x>AB$xMRYRTQf_(oB=35<s^v-Bv~{Apm@Hf`zjV(PU%x1E`Jn^p zAJ<>B571}RT6eIwyTM>}?M;P`cgzo)eX;+!c&T&gX2<E-JC*8<L#&<m%XUis71>|L zH1}@<JI9?v!46W1yIU_Gy4?Tbu!6^=-|ekOa!gq3Qdit6T9p?*yKH&)>Q}jIHJ?nK z-@$BX<Kb}9Bk0;6h3v<NGNg~z&DtXN_+WtK`6~x&)K)qBN@*Jxs&yZ3G_ew9>*`2L zYn-<BMw?}3Dffy0GAoru>g^&rrc_`2UTvf+oH+B(@hAJ8Pt||gY2&sa{geFU|KGIR zW&Hk0dn<kWKgA_b>cl0EKlMi#H~*J+{pT&rV5%Tyxnw`f!}q5SuG1^ud}!(0yVmwI zcWqm+{pI}$W^AiE-+z5^_3}sFe$_T@71h|;%Np4#l5bp(FZp8oW4m_tF6L(w>dg*z zywBswUbWVY(@tUM(sLz_7iDix6nZbu)!le7KXZ9U@^^m5OdU=wQSXP}tCP|MLT}Cd zGsk1YrR){%zaFXkNqlHJ;C=SW^)Fj_+gVO)R@Sq=U%h#y!3?fLCWn7NE8BbRdjFAr ztx^MrpPlVL-X_V#G_ExJy#IY`L2B!zdtdqMOU>HXSo^oVI=f`@t@D$UY<*{S8-I)m zJwIi$aioRt)y7qGPA+aWky<rNtz*%KfYyr~nkr2;_QGlD-Je@4>|%6UCR$88zxCk) zNtdVXm#TRlKK%PYaY66Zt#1!S1zg$}t?_G9o|4EF&!-_PZajOJ#Lvyu*frr_ZPot! zwlZdFj_c~Pnx0Nt&hvXaN2HeP<W0g$)DLe`zoEWqx|dtSS3j1(N$n@!>cn1s)@8~2 z<?iD0**EVUP4>K`kg6eXc-GV1_pP8O_cK0Uf%RtVr7b-Y0$)Wpy;9l|w$1x?>luyX zt9Na>@+?D_=kvt+OX&>}+m2nnZR2`I{7L#e&6!re7HCe};!*!xM9F3Y+dtO2Ig9su zv`tF3;<&ubY?8uZ$DNtel`FS}-o2$WRrYRi`StDQ)7Q_6eJ4EUl>e-b1Z4qJ?p+U7 z{SzsQTe6vX<Iat*>gRmjT3Wx%e$m<c`c{2;zW;Zc=JZNE*uZY+_lwC?tke0dGuLE0 zj`nj0i+(xHsof&gc871*`TFo<bC+iyo~PIIHfrsB-{cuTCAZcDxXtBoI<>V~D^lik znp%9?|I}~ytKRHScAV6*nQ`-fqu=l23SS(a`=9?Hv&`}T-G>i)NId@h|6|A8|MMsO z6~7%bSL9r9z1f2JsY}--Eu0z^wt54PwdSGSf@Vq2Z>nCpuDkm5#Q)7T?`#*VCD#|| zEwBG~|Jc5Z7xLrR9a?qgX`IwEpYYIJ-}?Pw)3^`EWNQ7b-MMxCf+fH1@Ec@xl|7qx zS?F!fMP-Raa~EoFa^4*ubdBe2qY9rypxaZ1cU!(MxtQ^p@ne76L}eSrp7SC9a_9Cg z+5FRCT9vb-+_xt)%dQ2h_`22io<D!SRr1m#w|YU@1Vt&$XL@qmH<v$i<MxSaDcY*E zW!D1Hm9BF#(~3=#^cEI8aLam-VPNxPVVKLBI}x)b&4afdymF$jX*Qo3n_!_lXR@Gx z^8G}E=L<4AU%Z>VsqTt~mr$3?uWdaUvs*Szd?EF5<^dhGg`1bfY&mH7`d?&Z*0MvK zO!^;!v`e+?gLeCe9R9!<xMZ!`y<0P{?Z3}=)9JEkrQx!v_vN0K?YM5VBXLjsTq})3 zjXth<=U5jS6$R|i)nHiMJiG0YgIFd{lm8_n`9~F!Lf7UyNX=F9G?MD7-pnU>+fUhK z?Sv9(!}VJ%bFZtEF8QbbR%Ke)irUQYPoBM5J=^p)*W;Nz=|=VQB0Dm2c(na`{65?H ztE=YaZ0%3mapF$MtbGj+p6Qe*JXvhv$=UzHj&HdQmtqmu?6SjZo+Sn^H&1rzX-L*S zz4YVH2Q9W$y-g*tT5i`*ues0|-nprNf$vg__ba>)oZ1xeY5T4S*P}a5>}`Fr?D_66 z%}61w=86_iL+cydDyGSc>cxb&&R7xJb1Ucj*UkI3KV8NBYjxP0Q!{24^v5h+*5e~~ z^v=0mGL^c<FINQ`c*kB87QMu`YuUu6=*YT|**~Tgdp>=i>A87l&eL1VO*txW|I(dT zs^ix3Bs|0QgyB=cC$_1}&WWWze>~63#{T`uTLF$rH<K<%^B?`Z**G$ot?9s%dRw*k zwZWg{?s1%Vy>fUmZ?KNKdhE8pvqSG3c78bJTIBDuPFMSMxU9PW`YbuH;;G)8fIU33 zLew3rG73x{8cK1dw6WZruz+diWq0mA<-D+N=E&A_Ug4WIebv$0#^!fb^v#!<ff=fD zi*h;s&Z@Y#=+E1)PrF4b4m_VG8pZo%XWHue;K{qa=Q})G5T`Hl%kj*q6VLzt+f~78 zB{^-Uo!&g2OI|lBTm|&bDLq!G(Rshv;>W4w4ELGtsMkz8ni2ZNbN22}e|tXngw0G^ zP!!{;CB=SdR!C#09ZRyG!Zn868L8^hee35`wEsGvTbvv`;RcW27ngq@W&}@+5a%h? zbk^uE|5jfwBXjD7W&ppMOzatdr><nn=&47zJ1<VV_11wiqeMl0!z2$CV->4ivYnov z3csyA%=u|~slunVCqEiW#Lbr3|2ORDk6VJzE0St>_+)>+cspC*#yoAY(1L>S?FY8~ z4STxvd6)Cmli9~@epoYcrP|8<v3tYmao|ePi4Ri~nI*S-)wft|`QrW3)#Sh_E5_8Q zDZ7rGdv*O;#-!(t;%lXT8l-J}J(-_><DE<2H{M}o+1c`Ro%00iBT1+4A5RjWuw&A< zZx2=12}iwdSQ2`>>#0Cjgj{Ud#y|UY{_C&)e{9{EpY^4pJ5D?4M>6PH{$Fn}iT!ZF zUvtm2pY{7Yf8~2-*5${1sekqBpW4cr^&bp3zh9T9ublVz{)DLMDnEKFHe7Elf83gO z)>k(D=iT>~A3jXoRCnjmm%}C#3s!veUUK6=_=MlvZwdWf{mVZ8z1Y6S-}kCr7&hFu zl(bq@96vAl{Wn>$Y~!#q-0E6<>PCF(R(@V7Z+sQqwUS?jJ`kL<irc7i^P#B?^=z7E zXKez7=a;-S-ZJ^fls1)Z!p%9mE{h2JR25AtU!#6@&X#-i59*w&wbni}+V+5LW9R>` z&wg#6a($Y}QFXp`pRea%YIu?>m|Y~lvy|aR()Ig7wd_%r=dLfiTIumy<;KoN#Sd|9 z=a%YKdhI^?{jzn;16|P<6W<uU2zqzvxyg^O^-uHlFIStK-1a50V40C~^=?TugR(5H ztqb@>LasRNdb8)MYBK{*@=4D6($D{Xt($OA?y;|m;iLFHdp%hX%n`W5;G-$nmM>K{ zS4?D8c$ej6pW^FJ|0PB2`ZR?vY0(P*MIJK$-yQZ}pZBhApP%*W6(;Wk)Yt3JXqQo+ zmwbNqi#nV7@+*yjYDJIVJo319@7%WT#U~QOcFpQNv~!8#tmE5_C$@QH2`I~KQT|@F zQ+a!{P?wR4@(ClMlyzlw(;fyckkH&X*Q@c=lHz;fiG2cmf(s0m%RZXmlI(SH=Bf-| z&zj!1n|XMxIp$60d-&nupFo+f!W-g}BF=BH<9yxCF5a?X-?RE1O)PV!OP({H<A3Pq zqrkI?Uh&y;;&e{&ujV_J-cdcPBU-Jp!(V*$tsNEfFYlUhde4lz`yx&ph&XU4;>wYT zBgZ1<Puy6g{h0Ag&&6r2F=0nGo!hfDN<z!X?^?p#HAieBH`abqOb-&14^q8$>2;C+ zjo2fz{#;~<|JZwX&x2W~(jM0b%(vTf`%jeC-3j?dm8;(wrmH{ufA~-RjQ{+*PyPJ= z=key)O<&5{{?Bi;5o%fRzu!QvL+JDWl^6fB|1kY1Tv)OAvDBj-IVF|}(+vMPP7V3{ z?{vEKm)$?QZgjS85L_oz>{3+4X5}%vYSryy?!1}Yx88kPzB=OKjVodvJ1Ra&DQDJO zZQHo6>g~U8-~S8W=JR!T-|P4FtBVvbTV`_mtmE6*SD*2TmRM$XSHeT)wr|qxsN!Vi zl}cu}qQaA(XDS*r+HpS8&C=~>+oX2BWu8XQl6gAkmfpLWu;km4nQB>^EM|G1GT5fi zGedLQ(`b9ezal{)GFOsVj0)xZqhe3JRhfC!vEJ!?-mNGJx6|pCxo1T-hi=PS)BXCS z>3zv##y#tKAD@1(W%HAsHsc;X_RH5hFC^OJxF`7@J=XE~mcopG=fBi1`)~Z$KIp&u z9{1))I;y!0n!om2NSsRiTd%@D<6phiNBgTse(zCw@kHy#m-0RX!3h?cK~b8g&NCKW zyFBsk;)(4+^^-P#md%{>`LFXPqZfx3zjDsqWt00}A??zxU9bLr`9ALq|APZ3uc)t8 zzkM%4{I8vz-DUgC;^eJP#!q=aUu>Lyb^hC5FQ(Rgx|#gp$2U*qgCQ%t-*tUC8GCEn z%JbVUG%c<zzH$A6)BNkfrKhXctO?!G-r{~PqM++?%+m=%|0X)~=G2=UxD&D3DlgAo z(`=`!AEV{nM^nxoGcjA%=kn|+pWK3NvrcU<n!l>`qS`B$wPHR=$5Y-UeX^hYf99X_ z>wG@h3rU`d{V9AP<!Ag9w@D(?{x98l;K~18=l}D~<z_Iyqj1rukoo+>p7%>vw(REA z<lE06H_4*>(|Tpjtgz`eDu$oe-LDU6obbJ%quN04z}3hP89$kCuYG&QX;RcxrC;*8 zvAMAYPSdk3)%{+m+ZF%1_4Gxs;TI9V1^d`qBT5~OuRq@UfUzoA_~a+GSy~I&_>)$# zGNt@m5FVeT$HeL`z*qXFSS$PQ+Hz*&e9jM&>Vksa%jC{I+{&1Kjd9kZfBL%3xAQ;L zAG%U{Y0vJu(%P@{nlEiXr?YeV_WbzbX^kw!CU=4!*Zuza>zkD6!@PS1xeK++W^G(# z7t)tk?Y*+`N&eQWt^S|ht2}y9*ln>>qv+Pb?ujoZ+`hWiiAO{BuzUFb8?9wY*W#02 z&(HkZ?Y{Zvwf_~OMM+`tX{+aFe(gS9`m?<L5~EGMaEKR|*2+m-liinb%TAQHnZ4?x zfcJZW5W_BZ-=&HY9}j25Z?W7K>M!HlRsJvI@dn)+IxqG&zPI15`scHL^4gEl)$<J4 zA}@w56wO^)(w))T>Ehawk@ZzM;k!`y#>e$P3u=FV|8Y(7rNPW^px#Z?Dx(-3x5Ycx z@El)k>?y%rKWj(CnXNlHJj+dUOVs+$v8`Ibd%(&t{OIP3k7^90Rxd6N47;+qWvB9% zdwRK6eKwPhe@a-^!xAXc{=CU#Hs|4)7Ev6NOyv669~b|uF_Xz!ys)Bd+1$D3mma<8 zGq3;2#hr`JrWYNu*kyWGL8;}0ZB*m*jNr_b#Zmd;#x1w=1)kNbe0klkQ~g3@{c9hO zkouAdNlz|*>EC2{<3Z}5i#04SPHa71YhY9H_2V1`UA@A#1Jzp&J>Ga=sz2)vRu(G> zo`lum^-g}NJKHx2tbf5Uw=OkE^!uN6SNUG7Vv7<xxmfSwVzHZ#0}i?KUUK*Qs9bDP z@5|(oE99Xks4|^LNX%)Ka=qnL>v)+dTytB#1h&7K$k=uCX|I7xzfQpw#k`dEbVtEv zFTD%@zbET>O;P%|S7LG?>nc9BQo-8g4UB4g4tzMzbT7@j_L8^nj_zF#)~d6fJ3M#c ziTBP)CpuT0Ja_2I67A>FD~fn@c-}6~K6`P)G=HvZq6XV7n~I<INhhg&IBe)#Z}sPI zyI{CMq>Ga7?ITBCWQShqXn5%GY+J^J>+V6D4({}uHA(o+)j%=+FQpfzD>Y2_7x-}V z#2L?Bw~hbjKl^|7&;FVJ%a{D$-f8R7ao>?s|HGCa{*jXof4$!(bozhD^8ectm!{P_ z@!USiIMr6lq+DycWzwZ>JLfuUowHbIedP1x`n?(7TrR4$=)`^b?VY?%XFkvAyfWGQ zQn_pMcxrRq_thphp55g;+v-~fkFTuy<`c^zzAxj|&E01z`|^kX5nIoV&;9qTI{$WC z;=-S`mwZ=9es8}W`uW?hcYnWpx_kD^ue06d`T6(uw1>>`3sDnOf8liV_W2BvxS5~1 zem&{nv%3>pe}CW3np$329UkRPXLq08z5D*Xd;1#9jF0x@6qnb2eK_q4lRMKE-cVO1 z@mY1(y}U1M(Tezz(vjlCbW56ze>$t^3vP+97_pWE$2BHO2y$9=c(*PV`VuC2q4Icp z*RD&~8+$@OguiBx?%MN~Z-(rw6t|lPBzNxjvsUHq{-5&ce_;KS{~HxN58Z444XUYs zvM-x!QS#?G$HTcCpZ4qUu`4I)JpSQ7?f=)1$M&1u3&ng{e#>1rr@5@BJ|?k~y)gB* z)A!z|WvrsJzvo}Nb@7<gnW8N!MlWQ(_y;*ke&;y(v-I?}=kIpEo2&Qz+?DXIoxfLa zdp+se`md(DHkZuWW&hDRy?&0+!R47>r`=b$!In2czf+_A($jVOZzwqGzAu}$c5Tze z)fM}GE)F-8_3%8^v0H7bh0@(Bj|;p#SA5ncJKvh9_c&_#?-<1+2O1PqWoMQBxnTO^ z?#pXmgm-u;?hTmR5FmAzCwI$Qx9oreEy;V<`y^+-etrC<d#-t=<t-ka8QfwT^*;`( ze_>b`y(7x!P)dMw_P-@-r+isfoUiX!^IIr@C*$~&e~R%sTq^so$Y1}^_%q(EcwYUP z|F_!SRPkJ??q}K3?ON{oOO(mU|BuK~%~;`r%PZ41SgM6iW;mL4*(tQb{`mW=Uh5v1 zm&#cc&S3uB>=pQ<U*-baQ<LNl;e`z5`m^e9&JyQ%bDH~-K;fmUJ;A}-wi(u55!l-l zWWi}|ly&&GfX(mj8<N+O*Z=W1uisbs+3xp(%q7Z?=6)^PS#qwfGk)cL&(8-8S5&C_ z@3AwVm;7VOrSpl8cdp$2@YBTO7lNK8@@-@}b=+7fb#FmWz`~|8Dd}?Ns~ekxm(RWO zfFq(*;!M3(TkO(c4FMnPGPc%?n8_0B<|?cc^}k+lYKNL}jJ0Eo_B@%UM^ZA=Gj_BK zAKF^nmc6_q+p#*-@laTH;Y;VlD6h_!$`4N~Zd|7FZpzA!Jqs&a&O9i5m3)#T{gdTR zC+^FeT7x-4Wsg6WJajuUM`7i$2aD1kHkr+Nv+U8LZM{y>_1teRy(-yuI;+=jzVijP zShaO#9nYt>Ulnusdqtx8Nperqiuq;lCQ9D@tX$<La=!hJ=~IOh6>^gv`z)DNJkiG5 z`uV(dzj~H@vGnZRJgMrOcb2`eWoz+dnRNB1=lr>3dY;&nX;w@NdSbKm%uKJ9#Z!aQ zwNuZBcg@?FVwJ9`CiJELX!DcbT~{XCsH}7|u_zMR%)Y<*xYRSI^qn7^__w%oNA@wV zsgY{XO_=Gt+snK3%u1dWDO=VQoDFK+-1E0r>dqep52gyH2d{3w|EW;1<?*aJ3)Z%I zywUZPEm3;kbmDxe<0+x(vvf9^u61vSo8FL>sjPZ>&XYB7e;(ZQvZA3c_HO;>Rg)k6 z5SyOrTg7%dCjXR*eA$(!6Q-nkY885~D(mdBe%$h)M#$=JWv*umm-DVEW#xa?gme~6 z^XE9n8XWZVK~0h5N~aC>F;1%t1h_8$?)j!5zvH#i^_dDW%6reTzc{txTjml5Yq^hl zEZ;t<{1LfSFWWy!oT)1Gi%dh`|3mzK^?V<mtF!;;usHswF70a8L)JTI7Co$eabOb1 z{G%d~YL9mc?dg};zqt6{(G4X}IcsCQD_f*L3H<%JV(r8lhJL}|t5NaRE4fRU4oZs} z-|u?UbX7m$qpOXBhU;0bn8v5mZ+tqgUw`vOTh!K5Ox^mMkM_s^VJf;<w)on)ncFKR zi*?iMXID?K;7UGs!}ObNvCpOIJ2mxz*^i&yetz`K>i93r`ZZS`g)6hZQTFW(I`FNM zlkvXREs+Uwi!Lq`dztuqPfL5gXy&OZ>nqBoD%RFA!5?lgyD`NrG5A-z<J!?5+zsDv z?Ab4JXZ6FKzh9SXy(?5dS++WE<EFmit($f`y;iLcejQrRx!jWT`L3+#+Sd-R<60nI zx|_ShS|w@OE9Ngb56v?V8b>}V+~js8g=^tSQRB(_9%}I+KJN@>oGH{Vp2S!3$e;U` zp3sr4re8ZJzloWfYZ4k>ak%D9{)CF#`<)ajO)m6Rq-Pb^F1{_{I=k|?!fAmw%9=ZP ziz@u?h%C3XKmFfve!YGDGyB)_r3du>gl;(hCicI$qlLY6-?^uS#`zI9gWPwd7D}Ie zvZg*yH285?ht!PD`@*tQL&DgFf2Umj-nypq?vYu-yIliTR7_7%|9W^!#tPMQ?`k$& zy??vvvXb@k9&Ue;z4nZolNosZuYIYocw=pGE3`dd^roKX62C)LGaG8_g%0lvKC*9< zT7djF*_#}*8mf&B?+re(chmFjb*I9emDXDY>MwY7O>4(hmF=N@KCWNOSTmPdpYAO# z%Qlly>Ff^|TC~?+H)C3_y@T^Uj_{t0dMB<gUh)1cbr+s~;D37hiA>!ZlT=&DN&5wU zytqGYtzYQd%PvojU)twi6Lo{R)vI1)Rkgw@zl)upANEAgIh>N&;oAC(dF7@<`-M37 z2Tc&4o|SwszW$28;fK@9ng0cAiJyJ7f8($GHUErP{}+q=&--QNr>FOWUhVJv<T0t~ z>-^S@771VPzgqKuTF~dUlQ!i|2%a>>bN80q;Q6^18-AWzb~ote2d<4JQE%TchyG-~ zdU0L7pmz)#_lj1oRTF<@KHQW&@#~dCTW`HH`8?U~dQ<Dw1S!i`t7_|vrc9O7HMDf= z*|5X+UZ%R}oNrmr-T1_pn~0h&{gLLiR@1Y1$<d~TyLVJNg-F<}Hg7K8QDZ1(lqe;d zT6iq}ir&oal2$8i`cKSz!kH1H^*Fm{qhq1@AC(>dZ`WrWKKOc7Qe95#r3Rajoh?(< zHO_5WwX*P8MPzosoJkBNA2@Dn1O$d=^e;NRJ+soOm2ab=)~UKV%I`Ep8&8WXd26~b zG;jU>ee-(0J*sDYPr7Y4Nm%bU**-IV%GI=$AB&T(u38Wy9(R7`BEOfLLbq7YdwD2q z-ra`<t7S8zwYJTjTi+QPo%ihKq60sTof|o3?vapNF#A+lroV5qno`P-0tT_#f*KC3 z_@e=g{F#O<3@if2&hs!H`c+XTVlm}av4-pN1*?L6%HB_3$rW7NRVH4)Y$juL=dY00 zoFO-lu5iDtwUtM8rCDi*#o5@?wy)-IG1XQ39A%hW8+$qIn4H$sr2cvf0qwiD*$XrD z8hT^%7S5hFH@8=5-O^2UceMInr1I`PadB(ZtA78o)lAEira!y#;zFe1Rm+)42aY>W zs_77V*&o@Xa_v=5rPQ5QyN}P-ipZSXH$`;u*$q*O&v$gk3$QBniFH40+#EV1Csgy& zH`yI_;fkkSd-hZ>;`rC@%XFwJp?+OTw8R?i$s1y9>cbX=$w<vzb|zH&+>=AIxmedR zDA@|1)RyZ_X><E|aRt}U)2@sAZ{_VXnbLn=yt}POLiC{0{>S00OI}oRe4hF^fNkZz zG9QgL{z}(hRWiS3{<r`9U;o8_dodMH3u%M@_0RtM|M_qK^49a_-~YXtJtrOfA1~Hk z-#pP^TIc`&4L|>1fAOFHSdZJb2!=Py_6J^D_2}2r-K`&1af<v9T^K9=-}_O{?m5%N zwc-yr+&E}2!zS?UZGXbcb=~%7)Z4r5WsV;DxAOXQTNx&P;}s`*W$S$mE^S=mZ_Vg` z)$i>FKh4Y=9s8IfW(&y5pXynrw*B0UDZcI%ix=C~KfLUjxBADn7pKqOT~b;8_tmdg zw#!%FIemNk`khz#+@}}q|65aA-TUC@iN7a!XKC*BnJ@A>^ODW<=gE~i^Ukt3O3vlq zvT#A)lZ^#+W>(h1PdA#x@SJP;sH2(Q@Ue33%*kCf%Y+_GG2AJgm40^qmZ_80?i4;& z|6Xu<*}o8;OwT__^@e(uyW_0*p4HiBuR5T?Tx)PWo@<?3>`vofQx7!1S@}dxXo4r} zt^F4kRwyb>;}@NH_ms2$r{G5+?pjV={ZR)eAA0lPU*O6M+Tk~Ncdehb+)~H4>*!q_ z8C`Mb({7eU8#a6Gh}?Q+ja=Nci7ihbr%W~2^rvQxgtXSlqg}=uFWIcAXA%o*YJQrU zp!hGePvrYji~KK__Gr)1W1Z}ykRMvGF<JaiSz`Hztc{iqk?S1ZbaJo@22Ay5T*b=5 zDphzeg;h=4AhPi!*Ljz)4!wylua};7Um5;d{`um~iL>4<Z%RDy+*Et&;sjBav&n8n zceGAUeD(W5$RD4+Hjbwg7M939omH>;NM?ni;guCkrd`(~$^su(T%Eew<fq}06(QOy zPdmSTP%%;HOWmBSYcqPgmt9?!x^q>i{bw=x?vR(Sb&F>%dVQSv?Tx8A$!8SR0}mVR zXIxsb@zkuNv*(;vv_1b+Bd&9k-?_l^Q7)mzXZSBJD!H{aGdz0Q469QXOT~7_oOaaL zI9<QTpy%3kJ@@%t3zr)o&8g*`*Rz`Y%Iw!n$C|^hUAgj%!+xz<1Z%|x)`$hi%e>}$ zS}fJtxK(lK=R41ZCW>lZ=2v%M$f&-*zVTWfXgDDIy^5aBpUn;BH-*Bt9udEnqaBx< zG$l}c&(|dl){Bgle%4y3tlWRb_01_66UW4fPc`-G-B}WAgxuSwitV;znBpy?B&2b^ z_wB89v#!iN`6)swz3K1dWh*id)E|0i5YYD{f3NloeWjjhxk*Z2wo17<KPb4dxj=r; z>qYXB3!>#2PqIE)b%KMjqV|Z2{fVW*%#~)diY76Zyh#$@edDNJ*~aczB&K@ZU-RA) LHtuT-3s@NdgDHfU diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java index 7947e87a49..99df658030 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java @@ -1,9 +1,9 @@ package at.tuwien.endpoints; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.UpdateDatabaseAccessDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; import at.tuwien.api.error.ApiErrorDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.api.user.UserDto; import at.tuwien.exception.*; import at.tuwien.service.AccessService; import at.tuwien.service.CredentialService; @@ -29,7 +29,7 @@ import java.util.UUID; @RestController @CrossOrigin(origins = "*") @RequestMapping(path = "/api/database/{databaseId}/access") -public class AccessEndpoint extends AbstractEndpoint { +public class AccessEndpoint extends RestEndpoint { private final AccessService accessService; private final CredentialService credentialService; @@ -80,8 +80,8 @@ public class AccessEndpoint extends AbstractEndpoint { throws NotAllowedException, DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseMalformedException, MetadataServiceException { log.debug("endpoint give access to database, databaseId={}, userId={}", databaseId, userId); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); - final PrivilegedUserDto user = credentialService.getUser(userId); + final DatabaseDto database = credentialService.getDatabase(databaseId); + final UserDto user = credentialService.getUser(userId); if (database.getAccesses().stream().anyMatch(a -> a.getUser().getId().equals(userId))) { log.error("Failed to create access to user with id {}: already has access", userId); throw new NotAllowedException("Failed to create access to user with id " + userId + ": already has access"); @@ -137,8 +137,8 @@ public class AccessEndpoint extends AbstractEndpoint { DatabaseMalformedException, MetadataServiceException { log.debug("endpoint modify access to database, databaseId={}, userId={}, access.type={}", databaseId, userId, access.getType()); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); - final PrivilegedUserDto user = credentialService.getUser(userId); + final DatabaseDto database = credentialService.getDatabase(databaseId); + final UserDto user = credentialService.getUser(userId); if (database.getAccesses().stream().noneMatch(a -> a.getHuserid().equals(userId))) { log.error("Failed to update access to user with id {}: no access", userId); throw new NotAllowedException("Failed to update access to user with id " + userId + ": no access"); @@ -208,8 +208,8 @@ public class AccessEndpoint extends AbstractEndpoint { DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseMalformedException, MetadataServiceException { log.debug("endpoint revoke access to database, databaseId={}, userId={}", databaseId, userId); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); - final PrivilegedUserDto user = credentialService.getUser(userId); + final DatabaseDto database = credentialService.getDatabase(databaseId); + final UserDto user = credentialService.getUser(userId); if (database.getAccesses().stream().noneMatch(a -> a.getUser().getId().equals(userId))) { log.error("Failed to delete access to user with id {}: no access", userId); throw new NotAllowedException("Failed to delete access to user with id " + userId + ": no access"); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java index d101a8c973..27848cf517 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java @@ -1,19 +1,17 @@ package at.tuwien.endpoints; -import at.tuwien.api.container.internal.PrivilegedContainerDto; +import at.tuwien.api.container.ContainerDto; import at.tuwien.api.database.AccessTypeDto; import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.internal.CreateDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; import at.tuwien.api.error.ApiErrorDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.api.user.UserDto; import at.tuwien.api.user.internal.UpdateUserPasswordDto; import at.tuwien.exception.*; -import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.AccessService; +import at.tuwien.service.ContainerService; import at.tuwien.service.CredentialService; import at.tuwien.service.DatabaseService; -import at.tuwien.service.SubsetService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -35,21 +33,19 @@ import java.sql.SQLException; @RestController @CrossOrigin(origins = "*") @RequestMapping(path = "/api/database") -public class DatabaseEndpoint extends AbstractEndpoint { +public class DatabaseEndpoint extends RestEndpoint { - private final SubsetService queryService; private final AccessService accessService; - private final MetadataMapper metadataMapper; private final DatabaseService databaseService; + private final ContainerService containerService; private final CredentialService credentialService; @Autowired - public DatabaseEndpoint(SubsetService queryService, AccessService accessService, MetadataMapper metadataMapper, - DatabaseService databaseService, CredentialService credentialService) { - this.queryService = queryService; + public DatabaseEndpoint(AccessService accessService, DatabaseService databaseService, + ContainerService containerService, CredentialService credentialService) { this.accessService = accessService; - this.metadataMapper = metadataMapper; this.databaseService = databaseService; + this.containerService = containerService; this.credentialService = credentialService; } @@ -90,18 +86,18 @@ public class DatabaseEndpoint extends AbstractEndpoint { DatabaseMalformedException, QueryStoreCreateException, MetadataServiceException { log.debug("endpoint create database, data.containerId={}, data.internalName={}, data.username={}", data.getContainerId(), data.getInternalName(), data.getUsername()); - final PrivilegedContainerDto container = credentialService.getContainer(data.getContainerId()); + final ContainerDto container = credentialService.getContainer(data.getContainerId()); try { - final PrivilegedDatabaseDto database = databaseService.create(container, data); - queryService.createQueryStore(container, data.getInternalName()); - final PrivilegedUserDto user = PrivilegedUserDto.builder() + final DatabaseDto database = containerService.createDatabase(container, data); + containerService.createQueryStore(container, data.getInternalName()); + final UserDto user = UserDto.builder() .id(data.getUserId()) .username(data.getUsername()) .password(data.getPassword()) .build(); accessService.create(database, user, AccessTypeDto.WRITE_ALL); return ResponseEntity.status(HttpStatus.CREATED) - .body(metadataMapper.privilegedDatabaseDtoToDatabaseDto(database)); + .body(database); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e); @@ -138,7 +134,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { DatabaseMalformedException, MetadataServiceException { log.debug("endpoint update user password in database, databaseId={}, data.username={}", databaseId, data.getUsername()); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + final DatabaseDto database = credentialService.getDatabase(databaseId); try { databaseService.update(database, data); return ResponseEntity.status(HttpStatus.ACCEPTED) diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/RestEndpoint.java similarity index 98% rename from dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java rename to dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/RestEndpoint.java index 87a4d32532..333e0c8398 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/RestEndpoint.java @@ -11,7 +11,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; -public abstract class AbstractEndpoint { +public abstract class RestEndpoint { public boolean hasRole(Principal principal, String role) { if (principal == null || role == null) { diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java index f30251a5ff..ed867715e1 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java @@ -1,9 +1,9 @@ package at.tuwien.endpoints; import at.tuwien.ExportResourceDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.ViewColumnDto; import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; import at.tuwien.api.database.query.ExecuteStatementDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.query.QueryPersistDto; @@ -46,25 +46,25 @@ import java.util.UUID; @RestController @CrossOrigin(origins = "*") @RequestMapping(path = "/api/database/{databaseId}/subset") -public class SubsetEndpoint extends AbstractEndpoint { +public class SubsetEndpoint extends RestEndpoint { - private final SchemaService schemaService; private final SubsetService subsetService; private final MetadataMapper metadataMapper; private final MetricsService metricsService; private final StorageService storageService; + private final DatabaseService databaseService; private final CredentialService credentialService; private final EndpointValidator endpointValidator; @Autowired - public SubsetEndpoint(SchemaService schemaService, SubsetService subsetService, MetadataMapper metadataMapper, - MetricsService metricsService, StorageService storageService, + public SubsetEndpoint(SubsetService subsetService, MetadataMapper metadataMapper, MetricsService metricsService, + StorageService storageService, DatabaseService databaseService, CredentialService credentialService, EndpointValidator endpointValidator) { - this.schemaService = schemaService; this.subsetService = subsetService; this.metadataMapper = metadataMapper; this.metricsService = metricsService; this.storageService = storageService; + this.databaseService = databaseService; this.credentialService = credentialService; this.endpointValidator = endpointValidator; } @@ -102,7 +102,7 @@ public class SubsetEndpoint extends AbstractEndpoint { throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, QueryNotFoundException, NotAllowedException, MetadataServiceException { log.debug("endpoint find subsets in database, databaseId={}, filterPersisted={}", databaseId, filterPersisted); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + final DatabaseDto database = credentialService.getDatabase(databaseId); endpointValidator.validateOnlyPrivateSchemaAccess(database, principal); final List<QueryDto> queries; try { @@ -164,7 +164,7 @@ public class SubsetEndpoint extends AbstractEndpoint { NotAllowedException { log.debug("endpoint find subset in database, databaseId={}, subsetId={}, accept={}, timestamp={}", databaseId, subsetId, accept, timestamp); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + final DatabaseDto database = credentialService.getDatabase(databaseId); endpointValidator.validateOnlyPrivateSchemaAccess(database, principal); final QueryDto subset; try { @@ -286,7 +286,7 @@ public class SubsetEndpoint extends AbstractEndpoint { log.debug("timestamp not set: default to {}", timestamp); } /* create */ - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + final DatabaseDto database = credentialService.getDatabase(databaseId); endpointValidator.validateOnlyPrivateSchemaAccess(database, principal); try { final Long subsetId = subsetService.create(database, data.getStatement(), timestamp, userId); @@ -345,7 +345,7 @@ public class SubsetEndpoint extends AbstractEndpoint { log.debug("endpoint get subset data, databaseId={}, subsetId={}, principal.name={} page={}, size={}", databaseId, subsetId, principal != null ? principal.getName() : null, page, size); endpointValidator.validateDataParams(page, size); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + final DatabaseDto database = credentialService.getDatabase(databaseId); if (!database.getIsPublic()) { if (principal == null) { log.error("Failed to re-execute query: no authentication found"); @@ -377,7 +377,8 @@ public class SubsetEndpoint extends AbstractEndpoint { } final Dataset<Row> dataset = subsetService.getData(database, subset, page, size); metricsService.countSubsetGetData(databaseId, subsetId); - final ViewDto view = schemaService.inspectView(database, metadataMapper.queryDtoToViewName(subset)); + final String viewName = metadataMapper.queryDtoToViewName(subset); + final ViewDto view = databaseService.inspectView(database, viewName); headers.set("Access-Control-Expose-Headers", "X-Id X-Headers"); headers.set("X-Headers", String.join(",", view.getColumns().stream().map(ViewColumnDto::getInternalName).toList())); return ResponseEntity.status(request.getMethod().equals("POST") ? HttpStatus.CREATED : HttpStatus.OK) @@ -435,7 +436,7 @@ public class SubsetEndpoint extends AbstractEndpoint { DatabaseUnavailableException, QueryNotFoundException, UserNotFoundException, MetadataServiceException { log.debug("endpoint persist query, databaseId={}, queryId={}, data.persist={}, principal.name={}", databaseId, queryId, data.getPersist(), principal.getName()); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + final DatabaseDto database = credentialService.getDatabase(databaseId); credentialService.getAccess(databaseId, getId(principal)); try { subsetService.persist(database, queryId, data.getPersist()); 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 f0ec00a035..077ec5b819 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 @@ -3,11 +3,9 @@ package at.tuwien.endpoints; import at.tuwien.ExportResourceDto; import at.tuwien.api.database.DatabaseAccessDto; import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.table.*; import at.tuwien.api.database.table.columns.ColumnDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.api.database.table.internal.TableCreateDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.exception.*; @@ -47,24 +45,24 @@ import java.util.Map; @RestController @CrossOrigin(origins = "*") @RequestMapping(path = "/api/database/{databaseId}/table") -public class TableEndpoint extends AbstractEndpoint { +public class TableEndpoint extends RestEndpoint { private final TableService tableService; - private final SchemaService schemaService; private final MetricsService metricsService; private final StorageService storageService; + private final DatabaseService databaseService; private final CredentialService credentialService; private final EndpointValidator endpointValidator; private final MetadataServiceGateway metadataServiceGateway; @Autowired - public TableEndpoint(TableService tableService, SchemaService schemaService, MetricsService metricsService, - StorageService storageService, CredentialService credentialService, + public TableEndpoint(TableService tableService, MetricsService metricsService, StorageService storageService, + DatabaseService databaseService, CredentialService credentialService, EndpointValidator endpointValidator, MetadataServiceGateway metadataServiceGateway) { this.tableService = tableService; - this.schemaService = schemaService; this.metricsService = metricsService; this.storageService = storageService; + this.databaseService = databaseService; this.credentialService = credentialService; this.endpointValidator = endpointValidator; this.metadataServiceGateway = metadataServiceGateway; @@ -113,11 +111,11 @@ public class TableEndpoint extends AbstractEndpoint { throw new TableMalformedException("Table must have a primary key"); } /* create */ - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + final DatabaseDto database = credentialService.getDatabase(databaseId); try { - final TableDto table = tableService.createTable(database, data); + final TableDto table = databaseService.createTable(database, data); return ResponseEntity.status(HttpStatus.CREATED) - .body(schemaService.inspectTable(database, table.getInternalName())); + .body(databaseService.inspectTable(database, table.getInternalName())); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e); @@ -157,7 +155,7 @@ public class TableEndpoint extends AbstractEndpoint { TableMalformedException, DatabaseUnavailableException, TableNotFoundException, MetadataServiceException { log.debug("endpoint update table, databaseId={}, data.description={}", databaseId, data.getDescription()); /* create */ - final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); try { tableService.updateTable(table, data); return ResponseEntity.status(HttpStatus.ACCEPTED) @@ -200,7 +198,7 @@ public class TableEndpoint extends AbstractEndpoint { throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, QueryMalformedException, MetadataServiceException { log.debug("endpoint delete table, databaseId={}, tableId={}", databaseId, tableId); - final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); try { tableService.delete(table); return ResponseEntity.status(HttpStatus.ACCEPTED) @@ -253,7 +251,7 @@ public class TableEndpoint extends AbstractEndpoint { @NotNull HttpServletRequest request, Principal principal) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, - PaginationException, MetadataServiceException, NotAllowedException { + PaginationException, MetadataServiceException, NotAllowedException, DatabaseNotFoundException { log.debug("endpoint get table data, databaseId={}, tableId={}, timestamp={}, page={}, size={}", databaseId, tableId, timestamp, page, size); endpointValidator.validateDataParams(page, size); @@ -270,7 +268,7 @@ public class TableEndpoint extends AbstractEndpoint { timestamp = Instant.now(); log.debug("timestamp not set: default to {}", timestamp); } - final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); if (!table.getIsPublic()) { if (principal == null) { log.error("Failed find table data: authentication required"); @@ -289,8 +287,8 @@ public class TableEndpoint extends AbstractEndpoint { } headers.set("Access-Control-Expose-Headers", "X-Headers"); headers.set("X-Headers", String.join(",", table.getColumns().stream().map(ColumnDto::getInternalName).toList())); - final Dataset<Row> dataset = tableService.getData(table.getDatabase(), table.getInternalName(), timestamp, - null, null, null, null); + final Dataset<Row> dataset = tableService.getData(credentialService.getDatabase(table.getTdbid()), + table.getInternalName(), timestamp, null, null, null, null); metricsService.countTableGetData(databaseId, tableId); return ResponseEntity.ok() .headers(headers) @@ -340,7 +338,7 @@ public class TableEndpoint extends AbstractEndpoint { TableMalformedException, QueryMalformedException, NotAllowedException, StorageUnavailableException, StorageNotFoundException, MetadataServiceException { log.debug("endpoint insert raw table data, databaseId={}, tableId={}", databaseId, tableId); - final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal)); endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal)); try { @@ -393,7 +391,7 @@ public class TableEndpoint extends AbstractEndpoint { TableMalformedException, QueryMalformedException, NotAllowedException, MetadataServiceException { log.debug("endpoint update raw table data, databaseId={}, tableId={}, data.keys={}", databaseId, tableId, data.getKeys()); - final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal)); endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal)); try { @@ -446,7 +444,7 @@ public class TableEndpoint extends AbstractEndpoint { TableMalformedException, QueryMalformedException, NotAllowedException, MetadataServiceException { log.debug("endpoint delete raw table data, databaseId={}, tableId={}, data.keys={}", databaseId, tableId, data.getKeys()); - final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal)); endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal)); try { @@ -506,7 +504,7 @@ public class TableEndpoint extends AbstractEndpoint { log.debug("size not set: default to 100L"); size = 100L; } - final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); if (!table.getIsPublic()) { if (principal == null) { log.error("Failed to find table history: no authentication found"); @@ -565,9 +563,9 @@ public class TableEndpoint extends AbstractEndpoint { throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, DatabaseMalformedException, TableNotFoundException, MetadataServiceException { log.debug("endpoint inspect table schemas, databaseId={}", databaseId); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + final DatabaseDto database = credentialService.getDatabase(databaseId); try { - return ResponseEntity.ok(tableService.getSchemas(database)); + return ResponseEntity.ok(databaseService.exploreTables(database)); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e); @@ -611,14 +609,14 @@ public class TableEndpoint extends AbstractEndpoint { @RequestParam(required = false) Instant timestamp, Principal principal) throws RemoteUnavailableException, TableNotFoundException, NotAllowedException, StorageUnavailableException, - QueryMalformedException, MetadataServiceException { + QueryMalformedException, MetadataServiceException, DatabaseNotFoundException { log.debug("endpoint export table data, databaseId={}, tableId={}, timestamp={}", databaseId, tableId, timestamp); /* parameters */ if (timestamp == null) { timestamp = Instant.now(); log.debug("timestamp not set: default to {}", timestamp); } - final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); if (!table.getIsPublic()) { if (principal == null) { log.error("Failed to export private table: principal is null"); @@ -626,8 +624,8 @@ public class TableEndpoint extends AbstractEndpoint { } credentialService.getAccess(databaseId, getId(principal)); } - final Dataset<Row> dataset = tableService.getData(table.getDatabase(), table.getInternalName(), timestamp, null, - null, null, null); + final Dataset<Row> dataset = tableService.getData(credentialService.getDatabase(table.getTdbid()), + table.getInternalName(), timestamp, null, null, null, null); metricsService.countTableGetData(databaseId, tableId); final ExportResourceDto resource = storageService.transformDataset(dataset); final HttpHeaders headers = new HttpHeaders(); @@ -677,7 +675,7 @@ public class TableEndpoint extends AbstractEndpoint { StorageNotFoundException, MalformedException, StorageUnavailableException, QueryMalformedException, DatabaseUnavailableException { log.debug("endpoint insert table data, databaseId={}, tableId={}, data.location={}", databaseId, tableId, data.getLocation()); - final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal)); endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal)); if (data.getLineTermination() == null) { @@ -725,11 +723,11 @@ public class TableEndpoint extends AbstractEndpoint { public ResponseEntity<TableStatisticDto> statistic(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, - MetadataServiceException, TableMalformedException { + MetadataServiceException, TableMalformedException, DatabaseNotFoundException { log.debug("endpoint generate table statistic, databaseId={}, tableId={}", databaseId, tableId); - final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); try { - return ResponseEntity.ok(tableService.getStatistics(table)); + return ResponseEntity.ok(tableService.getStatistics(credentialService.getDatabase(table.getTdbid()), table)); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database", e); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java index 2b0463483d..c662bf49c1 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java @@ -1,11 +1,10 @@ package at.tuwien.endpoints; import at.tuwien.ExportResourceDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.ViewColumnDto; import at.tuwien.api.database.ViewCreateDto; import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.exception.*; import at.tuwien.service.*; @@ -42,23 +41,25 @@ import java.util.Map; @RestController @CrossOrigin(origins = "*") @RequestMapping(path = "/api/database/{databaseId}/view") -public class ViewEndpoint extends AbstractEndpoint { +public class ViewEndpoint extends RestEndpoint { private final ViewService viewService; private final TableService tableService; private final MetricsService metricsService; private final StorageService storageService; + private final DatabaseService databaseService; private final CredentialService credentialService; private final EndpointValidator endpointValidator; @Autowired public ViewEndpoint(ViewService viewService, TableService tableService, MetricsService metricsService, - StorageService storageService, CredentialService credentialService, - EndpointValidator endpointValidator) { + StorageService storageService, DatabaseService databaseService, + CredentialService credentialService, EndpointValidator endpointValidator) { this.viewService = viewService; this.tableService = tableService; this.metricsService = metricsService; this.storageService = storageService; + this.databaseService = databaseService; this.credentialService = credentialService; this.endpointValidator = endpointValidator; } @@ -102,11 +103,11 @@ public class ViewEndpoint extends AbstractEndpoint { }) public ResponseEntity<List<ViewDto>> getSchema(@NotNull @PathVariable("databaseId") Long databaseId) throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, - ViewNotFoundException, DatabaseMalformedException, MetadataServiceException { + DatabaseMalformedException, MetadataServiceException, ViewNotFoundException { log.debug("endpoint inspect view schemas, databaseId={}", databaseId); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + final DatabaseDto database = credentialService.getDatabase(databaseId); try { - return ResponseEntity.ok(viewService.getSchemas(database)); + return ResponseEntity.ok(databaseService.exploreViews(database)); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e); @@ -149,10 +150,10 @@ public class ViewEndpoint extends AbstractEndpoint { @Valid @RequestBody ViewCreateDto data) throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, ViewMalformedException, MetadataServiceException { log.debug("endpoint create view, databaseId={}, data.name={}", databaseId, data.getName()); - final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + final DatabaseDto database = credentialService.getDatabase(databaseId); try { return ResponseEntity.status(HttpStatus.CREATED) - .body(viewService.create(database, data)); + .body(databaseService.createView(database, data)); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e); @@ -193,9 +194,9 @@ public class ViewEndpoint extends AbstractEndpoint { throws DatabaseUnavailableException, RemoteUnavailableException, ViewNotFoundException, ViewMalformedException, MetadataServiceException { log.debug("endpoint delete view, databaseId={}, viewId={}", databaseId, viewId); - final PrivilegedViewDto view = credentialService.getView(databaseId, viewId); + final ViewDto view = credentialService.getView(databaseId, viewId); try { - viewService.delete(view.getDatabase(), view.getInternalName()); + viewService.delete(view); return ResponseEntity.status(HttpStatus.ACCEPTED) .build(); } catch (SQLException e) { @@ -251,7 +252,7 @@ public class ViewEndpoint extends AbstractEndpoint { @NotNull HttpServletRequest request, Principal principal) throws DatabaseUnavailableException, RemoteUnavailableException, ViewNotFoundException, PaginationException, - QueryMalformedException, NotAllowedException, MetadataServiceException, TableNotFoundException { + QueryMalformedException, NotAllowedException, MetadataServiceException, TableNotFoundException, DatabaseNotFoundException { log.debug("endpoint get view data, databaseId={}, viewId={}, page={}, size={}, timestamp={}", databaseId, viewId, page, size, timestamp); endpointValidator.validateDataParams(page, size); @@ -268,7 +269,7 @@ public class ViewEndpoint extends AbstractEndpoint { timestamp = Instant.now(); log.debug("timestamp not set: default to {}", timestamp); } - final PrivilegedViewDto view = credentialService.getView(databaseId, viewId); + final ViewDto view = credentialService.getView(databaseId, viewId); if (!view.getIsPublic()) { if (principal == null) { log.error("Failed to get data from view: unauthorized"); @@ -287,8 +288,8 @@ public class ViewEndpoint extends AbstractEndpoint { } headers.set("Access-Control-Expose-Headers", "X-Headers"); headers.set("X-Headers", String.join(",", view.getColumns().stream().map(ViewColumnDto::getInternalName).toList())); - final Dataset<Row> dataset = tableService.getData(view.getDatabase(), view.getInternalName(), timestamp, - page, size, null, null); + final Dataset<Row> dataset = tableService.getData(credentialService.getDatabase(databaseId), + view.getInternalName(), timestamp, page, size, null, null); metricsService.countViewGetData(databaseId, viewId); return ResponseEntity.ok() .headers(headers) @@ -336,7 +337,7 @@ public class ViewEndpoint extends AbstractEndpoint { @RequestParam(required = false) Instant timestamp, Principal principal) throws RemoteUnavailableException, ViewNotFoundException, NotAllowedException, MetadataServiceException, - StorageUnavailableException, QueryMalformedException, TableNotFoundException { + StorageUnavailableException, QueryMalformedException, TableNotFoundException, DatabaseNotFoundException { log.debug("endpoint export view data, databaseId={}, viewId={}", databaseId, viewId); /* parameters */ if (timestamp == null) { @@ -344,7 +345,7 @@ public class ViewEndpoint extends AbstractEndpoint { log.debug("timestamp not set: default to {}", timestamp); } /* parameters */ - final PrivilegedViewDto view = credentialService.getView(databaseId, viewId); + final ViewDto view = credentialService.getView(databaseId, viewId); if (!view.getIsPublic()) { if (principal == null) { log.error("Failed to export private view: principal is null"); @@ -352,8 +353,8 @@ public class ViewEndpoint extends AbstractEndpoint { } credentialService.getAccess(databaseId, getId(principal)); } - final Dataset<Row> dataset = tableService.getData(view.getDatabase(), view.getInternalName(), timestamp, null, - null, null, null); + final Dataset<Row> dataset = tableService.getData(credentialService.getDatabase(databaseId), + view.getInternalName(), timestamp, null, null, null, null); metricsService.countViewGetData(databaseId, viewId); final ExportResourceDto resource = storageService.transformDataset(dataset); final HttpHeaders headers = new HttpHeaders(); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java index 25b858f51b..18307b5ac2 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java @@ -2,9 +2,9 @@ package at.tuwien.validation; import at.tuwien.api.database.AccessTypeDto; import at.tuwien.api.database.DatabaseAccessDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.config.QueryConfig; -import at.tuwien.endpoints.AbstractEndpoint; +import at.tuwien.endpoints.RestEndpoint; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import lombok.extern.log4j.Log4j2; @@ -21,7 +21,7 @@ import java.util.regex.Pattern; @Log4j2 @Component -public class EndpointValidator extends AbstractEndpoint { +public class EndpointValidator extends RestEndpoint { private final QueryConfig queryConfig; private final MetadataServiceGateway metadataServiceGateway; @@ -48,12 +48,12 @@ public class EndpointValidator extends AbstractEndpoint { } } - public void validateOnlyPrivateSchemaAccess(PrivilegedDatabaseDto database, Principal principal) + public void validateOnlyPrivateSchemaAccess(DatabaseDto database, Principal principal) throws NotAllowedException, RemoteUnavailableException, MetadataServiceException { validateOnlyPrivateSchemaAccess(database, principal, false); } - public void validateOnlyPrivateSchemaAccess(PrivilegedDatabaseDto database, Principal principal, + public void validateOnlyPrivateSchemaAccess(DatabaseDto database, Principal principal, boolean writeAccessOnly) throws NotAllowedException, RemoteUnavailableException, MetadataServiceException { if (database.getIsSchemaPublic()) { @@ -63,7 +63,7 @@ public class EndpointValidator extends AbstractEndpoint { validateOnlyAccess(database, principal, writeAccessOnly); } - public void validateOnlyPrivateSchemaHasRole(PrivilegedDatabaseDto database, Principal principal, String role) + public void validateOnlyPrivateSchemaHasRole(DatabaseDto database, Principal principal, String role) throws NotAllowedException { if (database.getIsSchemaPublic()) { log.trace("database with id {} has public schema: no access needed", database.getId()); @@ -82,7 +82,7 @@ public class EndpointValidator extends AbstractEndpoint { log.trace("principal has role '{}': access granted", role); } - public void validateOnlyAccess(PrivilegedDatabaseDto database, Principal principal, boolean writeAccessOnly) + public void validateOnlyAccess(DatabaseDto database, Principal principal, boolean writeAccessOnly) throws NotAllowedException, RemoteUnavailableException, MetadataServiceException { if (principal == null) { throw new NotAllowedException("No principal provided"); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java index a30ffb7b81..d4daa90741 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java @@ -1,18 +1,14 @@ package at.tuwien.config; -import at.tuwien.api.container.internal.PrivilegedContainerDto; +import at.tuwien.api.container.ContainerDto; import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; import at.tuwien.api.database.query.QueryDto; -import at.tuwien.api.database.table.columns.ColumnTypeDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; import lombok.extern.log4j.Log4j2; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import java.sql.*; -import java.time.Instant; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -21,38 +17,7 @@ import java.util.regex.Pattern; @Configuration public class MariaDbConfig { - /** - * Inserts a query into a created database with given hostname and database name. The method uses the JDBC in-out - * notation <a href="#{@link}">{@link https://learn.microsoft.com/en-us/sql/connect/jdbc/using-sql-escape-sequences?view=sql-server-ver16#stored-procedure-calls}</a> - * - * @param database The database. - * @param query The query. - * @param username The connection username. - * @param password The connection password. - * @return The generated or retrieved query id. - * @throws SQLException The procedure did not succeed. - */ - public static Long mockSystemQueryInsert(PrivilegedDatabaseDto database, String query, String username, String password) - throws SQLException { - final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); - log.trace("connect to database {}", jdbc); - try (Connection connection = DriverManager.getConnection(jdbc, username, password)) { - final String call = "{call _store_query(?,?,?,?)}"; - log.trace("prepare procedure '{}'", call); - final CallableStatement statement = connection.prepareCall(call); - statement.setString(1, username); - statement.setString(2, query); - statement.setTimestamp(3, Timestamp.from(Instant.now())); - statement.registerOutParameter(4, Types.BIGINT); - statement.executeUpdate(); - final Long queryId = statement.getLong(4); - statement.close(); - log.debug("received queryId={}", queryId); - return queryId; - } - } - - public static void createDatabase(PrivilegedContainerDto container, String database) throws SQLException { + public static void createDatabase(ContainerDto container, String database) throws SQLException { final String jdbc = "jdbc:mariadb://" + container.getHost() + ":" + container.getPort(); log.trace("connect to database {}", jdbc); try (Connection connection = DriverManager.getConnection(jdbc, container.getUsername(), container.getPassword())) { @@ -65,7 +30,7 @@ public class MariaDbConfig { log.debug("created database {}", database); } - public static void createInitDatabase(PrivilegedContainerDto container, DatabaseDto database) throws SQLException { + public static void createInitDatabase(ContainerDto container, DatabaseDto database) throws SQLException { final String jdbc = "jdbc:mariadb://" + container.getHost() + ":" + container.getPort(); log.trace("connect to database {}", jdbc); try (Connection connection = DriverManager.getConnection(jdbc, container.getUsername(), container.getPassword())) { @@ -76,21 +41,7 @@ public class MariaDbConfig { log.debug("created init database {}", database.getInternalName()); } - public static void grantReadAccess(PrivilegedDatabaseDto database, String username) { - final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); - log.trace("connect to database {}", jdbc); - try (Connection connection = DriverManager.getConnection(jdbc, database.getContainer().getUsername(), database.getContainer().getPassword())) { - connection.prepareStatement("GRANT SELECT ON *.* TO `" + username + "`@`%`;") - .executeUpdate(); - connection.prepareStatement("FLUSH PRIVILEGES;") - .executeUpdate(); - } catch (SQLException e) { - log.error("could not grant read access", e); - } - log.debug("granted read access to user {} in database {}", username, database.getInternalName()); - } - - public static void grantWriteAccess(PrivilegedDatabaseDto database, String username) { + public static void grantWriteAccess(DatabaseDto database, String username) { final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); log.trace("connect to database {}", jdbc); try (Connection connection = DriverManager.getConnection(jdbc, database.getContainer().getUsername(), database.getContainer().getPassword())) { @@ -104,7 +55,7 @@ public class MariaDbConfig { log.debug("granted read access to user {} in database {}", username, database.getInternalName()); } - public static void dropAllDatabases(PrivilegedContainerDto container) { + public static void dropAllDatabases(ContainerDto container) { final String jdbc = "jdbc:mariadb://" + container.getHost() + ":" + container.getPort(); log.trace("connect to database {}", jdbc); try (Connection connection = DriverManager.getConnection(jdbc, container.getUsername(), container.getPassword())) { @@ -130,7 +81,7 @@ public class MariaDbConfig { log.debug("dropped all databases"); } - public static void dropDatabase(PrivilegedContainerDto container, String database) + public static void dropDatabase(ContainerDto container, String database) throws SQLException { final String jdbc = "jdbc:mariadb://" + container.getHost() + ":" + container.getPort(); log.trace("connect to database {}", jdbc); @@ -144,26 +95,7 @@ public class MariaDbConfig { log.debug("dropped database {}", database); } - public static List<String> getUsernames(String hostname, String database, String username, String password) - throws SQLException { - final String jdbc = "jdbc:mariadb://" + hostname + "/" + database; - log.trace("connect to database {}", jdbc); - final List<String> usernames = new LinkedList<>(); - try (Connection connection = DriverManager.getConnection(jdbc, username, password)) { - final String query = "SELECT User FROM mysql.user;"; - log.trace("prepare statement '{}'", query); - final PreparedStatement statement = connection.prepareStatement(query); - final ResultSet set = statement.executeQuery(); - statement.close(); - while (set.next()) { - usernames.add(set.getString("User")); - } - log.debug("received usernames={}", usernames); - return usernames; - } - } - - public static List<String> getPrivileges(PrivilegedDatabaseDto database, String username) throws SQLException { + public static List<String> getPrivileges(DatabaseDto database, String username) throws SQLException { final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); log.trace("connect to database {}", jdbc); try (Connection connection = DriverManager.getConnection(jdbc, database.getContainer().getUsername(), database.getContainer().getPassword())) { @@ -185,7 +117,7 @@ public class MariaDbConfig { throw new SQLException("Failed to get privileges"); } - public static void dropTable(PrivilegedDatabaseDto database, String table) throws SQLException { + public static void dropTable(DatabaseDto database, String table) throws SQLException { final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); log.trace("connect to database {}", jdbc); try (Connection connection = DriverManager.getConnection(jdbc, database.getContainer().getUsername(), database.getContainer().getPassword())) { @@ -209,50 +141,7 @@ public class MariaDbConfig { } } - /** - * Inserts a query into a created database with given hostname and database name. The method uses the JDBC in-out - * notation <a href="#{@link}">{@link https://learn.microsoft.com/en-us/sql/connect/jdbc/using-sql-escape-sequences?view=sql-server-ver16#stored-procedure-calls}</a> - * - * @param database The database. - * @param query The query. - * @param username The connection username. - * @param password The connection password. - * @return The generated or retrieved query id. - * @throws SQLException The procedure did not succeed. - */ - public static Long mockUserQueryInsert(PrivilegedDatabaseDto database, String query, String username, String password) - throws SQLException { - final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); - log.trace("connect to database: {}", jdbc); - try (Connection connection = DriverManager.getConnection(jdbc, username, password)) { - final String call = "{call store_query(?,?,?)}"; - log.trace("prepare procedure '{}'", call); - final CallableStatement statement = connection.prepareCall(call); - statement.setString(1, query); - statement.setTimestamp(2, Timestamp.from(Instant.now())); - statement.registerOutParameter(3, Types.BIGINT); - statement.executeUpdate(); - final Long queryId = statement.getLong(3); - statement.close(); - log.debug("received queryId={}", queryId); - return queryId; - } - } - - /** - * Inserts a query into a created database with given hostname and database name. The method uses the JDBC in-out - * notation <a href="#{@link}">{@link https://learn.microsoft.com/en-us/sql/connect/jdbc/using-sql-escape-sequences?view=sql-server-ver16#stored-procedure-calls}</a> - * - * @param database The database. - * @param query The query. - * @return The generated or retrieved query id. - * @throws SQLException The procedure did not succeed. - */ - public static Long mockSystemQueryInsert(PrivilegedDatabaseDto database, String query) throws SQLException { - return mockSystemQueryInsert(database, query, database.getContainer().getUsername(), database.getContainer().getPassword()); - } - - public static void insertQueryStore(PrivilegedDatabaseDto database, QueryDto query, UUID userId) throws SQLException { + public static void insertQueryStore(DatabaseDto database, QueryDto query, UUID userId) throws SQLException { final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); log.trace("connect to database: {}", jdbc); try (Connection connection = DriverManager.getConnection(jdbc, database.getContainer().getUsername(), database.getContainer().getPassword())) { @@ -272,7 +161,7 @@ public class MariaDbConfig { } } - public static List<Map<String, Object>> listQueryStore(PrivilegedDatabaseDto database) throws SQLException { + public static List<Map<String, Object>> listQueryStore(DatabaseDto database) throws SQLException { final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); log.trace("connect to database {}", jdbc); try (Connection connection = DriverManager.getConnection(jdbc, database.getContainer().getUsername(), database.getContainer().getPassword())) { @@ -297,7 +186,7 @@ public class MariaDbConfig { } } - public static List<Map<String, String>> selectQuery(PrivilegedDatabaseDto database, String query, Set<String> columns) + public static List<Map<String, String>> selectQuery(DatabaseDto database, String query, Set<String> columns) throws SQLException { final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); log.trace("connect to database {}", jdbc); @@ -318,7 +207,7 @@ public class MariaDbConfig { return rows; } - public static List<Map<String, byte[]>> selectQueryByteArr(PrivilegedDatabaseDto database, String query, Set<String> columns) + public static List<Map<String, byte[]>> selectQueryByteArr(DatabaseDto database, String query, Set<String> columns) throws SQLException { final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); log.trace("connect to database {}", jdbc); @@ -339,7 +228,7 @@ public class MariaDbConfig { return rows; } - public static void execute(PrivilegedDatabaseDto database, String query) + public static void execute(DatabaseDto database, String query) throws SQLException { final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); log.trace("connect to database {}", jdbc); @@ -349,17 +238,7 @@ public class MariaDbConfig { } } - public static void execute(PrivilegedContainerDto container, String query) - throws SQLException { - final String jdbc = "jdbc:mariadb://" + container.getHost() + ":" + container.getPort(); - log.trace("connect to database: {}", jdbc); - try (Connection connection = DriverManager.getConnection(jdbc, container.getUsername(), container.getPassword())) { - final Statement statement = connection.createStatement(); - statement.executeUpdate(query); - } - } - - public static void dropQueryStore(PrivilegedDatabaseDto database) + public static void dropQueryStore(DatabaseDto database) throws SQLException { final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); log.trace("connect to database: {}", jdbc); @@ -373,78 +252,4 @@ public class MariaDbConfig { } } - public static Map<String, List<Object>> describeTableSchema(PrivilegedTableDto table, String username, String password) - throws SQLException { - final String jdbc = "jdbc:mariadb://" + table.getDatabase().getContainer().getHost() + ":" + table.getDatabase().getContainer().getPort() + "/" + table.getDatabase().getInternalName(); - log.trace("connect to database {}", jdbc); - final Map<String, List<Object>> out = new HashMap<>(); - try (Connection connection = DriverManager.getConnection(jdbc, username, password)) { - final String query = "SHOW COLUMNS FROM `" + table.getInternalName() + "`;"; - log.trace("prepare statement '{}'", query); - final PreparedStatement statement = connection.prepareStatement(query); - final ResultSet resultSet = statement.executeQuery(); - statement.close(); - while (resultSet.next()) { - if (resultSet.getString("Field").equals("id")) { - continue; - } - out.put(resultSet.getString("Field"), List.of(resultSet.getString("Type"), resultSet.getString("Null"), resultSet.getString("Key"))); - } - return out; - } - } - - public static ColumnTypeDto typetoColumnTypeDto(String data) throws Exception { - if (data.toUpperCase().startsWith("TINYINT(1)")) { - /* boolean in MySQL */ - return ColumnTypeDto.BOOL; - } - final Matcher matcher = Pattern.compile("([A-Z]+)") - .matcher(data.toUpperCase()); - if (!matcher.find()) { - log.error("Failed to map type: does not match expected format"); - throw new Exception("Failed to map type: does not match expected format"); - } - final String type = matcher.group(1); - try { - return ColumnTypeDto.valueOf(type); - } catch (IllegalArgumentException e) { - if (type.startsWith("TINYINT")) { - /* boolean in MySQL */ - return ColumnTypeDto.BOOL; - } else if (type.startsWith("BOOL")) { - /* boolean */ - return ColumnTypeDto.BOOL; - } else if (type.startsWith("DOUBLE")) { - /* double precision */ - return ColumnTypeDto.DOUBLE; - } else if (type.startsWith("INT")) { - /* integer synonym */ - return ColumnTypeDto.INT; - } else if (type.startsWith("DEC")) { - /* decimal synonym */ - return ColumnTypeDto.DECIMAL; - } else if (type.startsWith("ENUM")) { - return ColumnTypeDto.ENUM; - } else if (type.startsWith("SET")) { - return ColumnTypeDto.SET; - } - } - log.error("Failed to map data {} and type {}", data, type); - throw new Exception("Failed to map data " + data + " and type " + type); - } - - public static boolean tableExists(PrivilegedDatabaseDto database, String tableName) - throws SQLException { - final String jdbc = "jdbc:mariadb://" + database.getContainer().getHost() + ":" + database.getContainer().getPort() + "/" + database.getInternalName(); - log.trace("connect to database {}", jdbc); - try (Connection connection = DriverManager.getConnection(jdbc, database.getContainer().getUsername(), database.getContainer().getPassword())) { - final Statement statement = connection.createStatement(); - final String query = "SHOW TABLES LIKE '" + tableName + "';"; - log.trace("execute query {}", query); - final ResultSet result = statement.executeQuery(query); - return result.next(); - } - } - } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java index 9fb4003dba..00553dce06 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java @@ -1,8 +1,8 @@ package at.tuwien.endpoint; import at.tuwien.api.database.AccessTypeDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.user.UserDto; import at.tuwien.endpoints.AccessEndpoint; import at.tuwien.exception.*; import at.tuwien.service.AccessService; @@ -51,7 +51,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_4_ID)) .thenReturn(USER_4_PRIVILEGED_DTO); @@ -68,7 +68,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_1_ID)) .thenReturn(USER_1_PRIVILEGED_DTO); @@ -85,12 +85,12 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_4_ID)) .thenReturn(USER_4_PRIVILEGED_DTO); doThrow(SQLException.class) .when(accessService) - .create(DATABASE_1_PRIVILEGED_DTO, USER_4_PRIVILEGED_DTO, AccessTypeDto.READ); + .create(DATABASE_1_DTO, USER_4_PRIVILEGED_DTO, AccessTypeDto.READ); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -121,7 +121,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); doThrow(UserNotFoundException.class) .when(credentialService) .getUser(USER_4_ID); @@ -149,7 +149,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_1_ID)) .thenReturn(USER_1_PRIVILEGED_DTO); @@ -166,12 +166,12 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_1_ID)) .thenReturn(USER_1_PRIVILEGED_DTO); doThrow(SQLException.class) .when(accessService) - .update(DATABASE_1_PRIVILEGED_DTO, USER_1_PRIVILEGED_DTO, AccessTypeDto.READ); + .update(DATABASE_1_DTO, USER_1_PRIVILEGED_DTO, AccessTypeDto.READ); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -186,7 +186,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_4_ID)) .thenReturn(USER_4_PRIVILEGED_DTO); @@ -229,7 +229,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); doThrow(UserNotFoundException.class) .when(credentialService) .getUser(USER_1_ID); @@ -248,12 +248,12 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_1_ID)) .thenReturn(USER_1_PRIVILEGED_DTO); doNothing() .when(accessService) - .delete(any(PrivilegedDatabaseDto.class), any(PrivilegedUserDto.class)); + .delete(any(DatabaseDto.class), any(UserDto.class)); /* test */ final ResponseEntity<Void> response = accessEndpoint.revoke(DATABASE_1_ID, USER_1_ID); @@ -268,7 +268,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_4_ID)) .thenReturn(USER_4_PRIVILEGED_DTO); @@ -311,7 +311,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); doThrow(UserNotFoundException.class) .when(credentialService) .getUser(USER_1_ID); @@ -329,12 +329,12 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_1_ID)) .thenReturn(USER_1_PRIVILEGED_DTO); doThrow(SQLException.class) .when(accessService) - .delete(DATABASE_1_PRIVILEGED_DTO, USER_1_PRIVILEGED_DTO); + .delete(DATABASE_1_DTO, USER_1_PRIVILEGED_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java index c55442290e..592063e034 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java @@ -2,13 +2,10 @@ package at.tuwien.endpoint; import at.tuwien.api.database.AccessTypeDto; import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.api.user.UserDto; import at.tuwien.endpoints.DatabaseEndpoint; import at.tuwien.exception.*; -import at.tuwien.service.AccessService; -import at.tuwien.service.CredentialService; -import at.tuwien.service.DatabaseService; -import at.tuwien.service.SubsetService; +import at.tuwien.service.*; import at.tuwien.test.AbstractUnitTest; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; @@ -41,6 +38,9 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { @MockBean private SubsetService queryService; + @MockBean + private ContainerService containerService; + @MockBean private AccessService accessService; @@ -63,15 +63,15 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getContainer(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); - when(databaseService.create(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_CREATE_INTERNAL)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); + when(containerService.createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL)) + .thenReturn(DATABASE_1_DTO); doNothing() - .when(queryService) - .createQueryStore(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + .when(containerService) + .createQueryStore(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); doNothing() .when(accessService) - .create(eq(DATABASE_1_PRIVILEGED_DTO), any(PrivilegedUserDto.class), any(AccessTypeDto.class)); + .create(eq(DATABASE_1_DTO), any(UserDto.class), any(AccessTypeDto.class)); /* test */ final ResponseEntity<DatabaseDto> response = databaseEndpoint.create(DATABASE_1_CREATE_INTERNAL); @@ -85,15 +85,15 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getContainer(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); - when(databaseService.create(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_CREATE_INTERNAL)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); + when(containerService.createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL)) + .thenReturn(DATABASE_1_DTO); doNothing() - .when(queryService) - .createQueryStore(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + .when(containerService) + .createQueryStore(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); doNothing() .when(accessService) - .create(eq(DATABASE_1_PRIVILEGED_DTO), any(PrivilegedUserDto.class), any(AccessTypeDto.class)); + .create(eq(DATABASE_1_DTO), any(UserDto.class), any(AccessTypeDto.class)); /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { @@ -108,10 +108,10 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getContainer(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); doThrow(SQLException.class) - .when(databaseService) - .create(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_CREATE_INTERNAL); + .when(containerService) + .createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -144,11 +144,11 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { doThrow(ContainerNotFoundException.class) .when(credentialService) .getContainer(CONTAINER_1_ID); - when(databaseService.create(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_CREATE_INTERNAL)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(containerService.createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL)) + .thenReturn(DATABASE_1_DTO); doThrow(QueryStoreCreateException.class) - .when(queryService) - .createQueryStore(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + .when(containerService) + .createQueryStore(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); /* test */ assertThrows(ContainerNotFoundException.class, () -> { @@ -163,7 +163,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); /* test */ databaseEndpoint.update(DATABASE_1_ID, USER_1_UPDATE_PASSWORD_DTO); @@ -176,10 +176,10 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); doThrow(SQLException.class) .when(databaseService) - .update(DATABASE_1_PRIVILEGED_DTO, USER_1_UPDATE_PASSWORD_DTO); + .update(DATABASE_1_DTO, USER_1_UPDATE_PASSWORD_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -193,7 +193,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { @@ -224,10 +224,10 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); doThrow(DatabaseMalformedException.class) .when(databaseService) - .update(DATABASE_1_PRIVILEGED_DTO, USER_1_UPDATE_PASSWORD_DTO); + .update(DATABASE_1_DTO, USER_1_UPDATE_PASSWORD_DTO); /* test */ assertThrows(DatabaseMalformedException.class, () -> { diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java index 8a08f8231f..c76aa91ebf 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java @@ -1,6 +1,6 @@ package at.tuwien.endpoint; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.ExecuteStatementDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.query.QueryPersistDto; @@ -8,7 +8,6 @@ import at.tuwien.endpoints.SubsetEndpoint; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.service.CredentialService; -import at.tuwien.service.SchemaService; import at.tuwien.service.StorageService; import at.tuwien.service.SubsetService; import at.tuwien.test.AbstractUnitTest; @@ -25,7 +24,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.test.context.support.WithAnonymousUser; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -53,9 +51,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @MockBean private SubsetService subsetService; - @MockBean - private SchemaService schemaService; - @MockBean private MetadataServiceGateway metadataServiceGateway; @@ -68,9 +63,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @MockBean private CredentialService credentialService; - @MockBean - private MockHttpServletRequest mockHttpServletRequest; - @BeforeEach public void beforeEach() { genesis(); @@ -82,12 +74,12 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { RemoteUnavailableException, SQLException, MetadataServiceException { /* mock */ - when(subsetService.findAll(DATABASE_3_PRIVILEGED_DTO, null)) + when(subsetService.findAll(DATABASE_3_DTO, null)) .thenReturn(List.of(QUERY_1_DTO, QUERY_2_DTO, QUERY_3_DTO, QUERY_4_DTO, QUERY_5_DTO, QUERY_6_DTO)); /* test */ assertThrows(NotAllowedException.class, () -> { - generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, null); + generic_list(DATABASE_3_ID, DATABASE_3_DTO, null); }); } @@ -98,11 +90,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { MetadataServiceException { /* mock */ - when(subsetService.findAll(DATABASE_3_PRIVILEGED_DTO, null)) + when(subsetService.findAll(DATABASE_3_DTO, null)) .thenReturn(List.of(QUERY_1_DTO, QUERY_2_DTO, QUERY_3_DTO, QUERY_4_DTO, QUERY_5_DTO, QUERY_6_DTO)); /* test */ - final List<QueryDto> response = generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, USER_3_PRINCIPAL); + final List<QueryDto> response = generic_list(DATABASE_3_ID, DATABASE_3_DTO, USER_3_PRINCIPAL); assertEquals(6, response.size()); } @@ -123,14 +115,14 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); + .thenReturn(DATABASE_3_DTO); doThrow(SQLException.class) .when(subsetService) - .findAll(DATABASE_3_PRIVILEGED_DTO, null); + .findAll(DATABASE_3_DTO, null); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { - generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, USER_3_PRINCIPAL); + generic_list(DATABASE_3_ID, DATABASE_3_DTO, USER_3_PRINCIPAL); }); } @@ -141,8 +133,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) + .thenReturn(DATABASE_1_DTO); + when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) .thenReturn(QUERY_1_DTO); /* test */ @@ -158,8 +150,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) + .thenReturn(DATABASE_1_DTO); + when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) .thenReturn(QUERY_1_DTO); /* test */ @@ -176,8 +168,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_DTO); + when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); /* test */ @@ -191,8 +183,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_4_ID)) - .thenReturn(DATABASE_4_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_4_PRIVILEGED_DTO, QUERY_7_ID)) + .thenReturn(DATABASE_4_DTO); + when(subsetService.findById(DATABASE_4_DTO, QUERY_7_ID)) .thenReturn(QUERY_7_DTO); /* test */ @@ -211,12 +203,12 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) + .thenReturn(DATABASE_1_DTO); + when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) .thenReturn(QUERY_5_DTO); when(storageService.transformDataset(any(Dataset.class))) .thenReturn(EXPORT_RESOURCE_DTO); - when(subsetService.getData(any(PrivilegedDatabaseDto.class), any(QueryDto.class), eq(null), eq(null))) + when(subsetService.getData(any(DatabaseDto.class), any(QueryDto.class), eq(null), eq(null))) .thenReturn(mock); /* test */ @@ -233,10 +225,10 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_DTO); + when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(any(PrivilegedDatabaseDto.class), any(QueryDto.class), eq(null), eq(null))) + when(subsetService.getData(any(DatabaseDto.class), any(QueryDto.class), eq(null), eq(null))) .thenReturn(mock); when(storageService.transformDataset(any(Dataset.class))) .thenReturn(EXPORT_RESOURCE_DTO); @@ -256,10 +248,10 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_4_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_DTO); + when(subsetService.findById(DATABASE_4_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(any(PrivilegedDatabaseDto.class), any(QueryDto.class), eq(null), eq(null))) + when(subsetService.getData(any(DatabaseDto.class), any(QueryDto.class), eq(null), eq(null))) .thenReturn(mock); when(storageService.transformDataset(any(Dataset.class))) .thenReturn(EXPORT_RESOURCE_DTO); @@ -293,10 +285,10 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); + .thenReturn(DATABASE_3_DTO); doThrow(SQLException.class) .when(subsetService) - .findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID); + .findById(DATABASE_3_DTO, QUERY_5_ID); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -312,14 +304,14 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_DTO); + when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); when(storageService.transformDataset(any(Dataset.class))) .thenReturn(EXPORT_RESOURCE_DTO); doThrow(SQLException.class) .when(subsetService) - .getData(eq(DATABASE_3_PRIVILEGED_DTO), eq(QUERY_5_DTO), eq(null), eq(null)); + .getData(eq(DATABASE_3_DTO), eq(QUERY_5_DTO), eq(null), eq(null)); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -341,13 +333,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(subsetService.getData(eq(DATABASE_3_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L))) + .thenReturn(DATABASE_3_DTO); + when(subsetService.getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(0L), eq(10L))) .thenReturn(mock); - when(subsetService.findById(eq(DATABASE_3_PRIVILEGED_DTO), anyLong())) + when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) .thenReturn(QUERY_5_DTO); - when(schemaService.inspectView(eq(DATABASE_3_PRIVILEGED_DTO), anyString())) - .thenReturn(VIEW_5_DTO); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -387,12 +377,10 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(schemaService.inspectView(eq(DATABASE_3_PRIVILEGED_DTO), anyString())) - .thenReturn(VIEW_5_DTO); - when(subsetService.findById(eq(DATABASE_3_PRIVILEGED_DTO), anyLong())) + .thenReturn(DATABASE_3_DTO); + when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(eq(DATABASE_3_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(0L), eq(10L))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -413,17 +401,17 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); + .thenReturn(DATABASE_3_DTO); doThrow(SQLException.class) .when(subsetService) - .getData(eq(DATABASE_3_PRIVILEGED_DTO), any(QueryDto.class), eq(null), eq(null)); - when(subsetService.findById(eq(DATABASE_3_PRIVILEGED_DTO), anyLong())) + .getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(null), eq(null)); + when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) .thenReturn(QUERY_5_DTO); when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_READ_ACCESS_DTO); doThrow(SQLException.class) .when(subsetService) - .create(eq(DATABASE_3_PRIVILEGED_DTO), eq(QUERY_5_STATEMENT), any(Instant.class), eq(USER_1_ID)); + .create(eq(DATABASE_3_DTO), eq(QUERY_5_STATEMENT), any(Instant.class), eq(USER_1_ID)); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -468,13 +456,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(subsetService.getData(eq(DATABASE_3_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L))) + .thenReturn(DATABASE_3_DTO); + when(subsetService.getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(0L), eq(10L))) .thenReturn(mock); - when(subsetService.findById(eq(DATABASE_3_PRIVILEGED_DTO), anyLong())) + when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) .thenReturn(QUERY_5_DTO); - when(schemaService.inspectView(eq(DATABASE_3_PRIVILEGED_DTO), anyString())) - .thenReturn(VIEW_5_DTO); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -496,13 +482,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_4_ID)) - .thenReturn(DATABASE_4_PRIVILEGED_DTO); - when(subsetService.findById(eq(DATABASE_4_PRIVILEGED_DTO), anyLong())) + .thenReturn(DATABASE_4_DTO); + when(subsetService.findById(eq(DATABASE_4_DTO), anyLong())) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(eq(DATABASE_4_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(eq(DATABASE_4_DTO), any(QueryDto.class), eq(0L), eq(10L))) .thenReturn(mock); - when(schemaService.inspectView(eq(DATABASE_4_PRIVILEGED_DTO), anyString())) - .thenReturn(VIEW_5_DTO); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -524,13 +508,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(subsetService.findById(eq(DATABASE_1_PRIVILEGED_DTO), anyLong())) + .thenReturn(DATABASE_1_DTO); + when(subsetService.findById(eq(DATABASE_1_DTO), anyLong())) .thenReturn(QUERY_1_DTO); - when(subsetService.getData(eq(DATABASE_1_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(eq(DATABASE_1_DTO), any(QueryDto.class), eq(0L), eq(10L))) .thenReturn(mock); - when(schemaService.inspectView(eq(DATABASE_1_PRIVILEGED_DTO), anyString())) - .thenReturn(VIEW_1_DTO); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -550,13 +532,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_2_ID)) - .thenReturn(DATABASE_2_PRIVILEGED_DTO); - when(subsetService.findById(eq(DATABASE_2_PRIVILEGED_DTO), anyLong())) + .thenReturn(DATABASE_2_DTO); + when(subsetService.findById(eq(DATABASE_2_DTO), anyLong())) .thenReturn(QUERY_2_DTO); - when(subsetService.getData(eq(DATABASE_2_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(eq(DATABASE_2_DTO), any(QueryDto.class), eq(0L), eq(10L))) .thenReturn(mock); - when(schemaService.inspectView(eq(DATABASE_2_PRIVILEGED_DTO), anyString())) - .thenReturn(VIEW_4_DTO); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -575,15 +555,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_DTO); + when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(subsetService.reExecuteCount(DATABASE_3_PRIVILEGED_DTO, QUERY_5_DTO)) + when(subsetService.reExecuteCount(DATABASE_3_DTO, QUERY_5_DTO)) .thenReturn(QUERY_5_RESULT_NUMBER); - when(subsetService.getData(eq(DATABASE_3_PRIVILEGED_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(0L), eq(10L))) .thenReturn(mock); - when(schemaService.inspectView(eq(DATABASE_3_PRIVILEGED_DTO), anyString())) - .thenReturn(VIEW_5_DTO); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -601,10 +579,10 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_DTO); + when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(subsetService.reExecuteCount(DATABASE_3_PRIVILEGED_DTO, QUERY_5_DTO)) + when(subsetService.reExecuteCount(DATABASE_3_DTO, QUERY_5_DTO)) .thenReturn(QUERY_5_RESULT_NUMBER); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); @@ -627,15 +605,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) + .thenReturn(DATABASE_1_DTO); + when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) .thenReturn(QUERY_1_DTO); - when(subsetService.reExecuteCount(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO)) + when(subsetService.reExecuteCount(DATABASE_1_DTO, QUERY_1_DTO)) .thenReturn(QUERY_1_RESULT_NUMBER); - when(subsetService.getData(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, 0L, 10L)) + when(subsetService.getData(DATABASE_1_DTO, QUERY_1_DTO, 0L, 10L)) .thenReturn(mock); - when(schemaService.inspectView(eq(DATABASE_1_PRIVILEGED_DTO), anyString())) - .thenReturn(VIEW_1_DTO); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -652,7 +628,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -667,7 +643,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_1_ID); @@ -687,10 +663,10 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) + .thenReturn(DATABASE_1_DTO); + when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) .thenReturn(QUERY_1_DTO); - when(subsetService.reExecuteCount(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO)) + when(subsetService.reExecuteCount(DATABASE_1_DTO, QUERY_1_DTO)) .thenReturn(QUERY_1_RESULT_NUMBER); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); @@ -711,14 +687,14 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) + .thenReturn(DATABASE_1_DTO); + when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) .thenReturn(QUERY_1_DTO); when(httpServletRequest.getMethod()) .thenReturn("GET"); doThrow(SQLException.class) .when(subsetService) - .getData(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, 0L, 10L); + .getData(DATABASE_1_DTO, QUERY_1_DTO, 0L, 10L); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -739,11 +715,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); + .thenReturn(DATABASE_3_DTO); doNothing() .when(subsetService) - .persist(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID, true); - when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .persist(DATABASE_3_DTO, QUERY_5_ID, true); + when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); /* test */ @@ -812,12 +788,12 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); + .thenReturn(DATABASE_3_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); doThrow(SQLException.class) .when(subsetService) - .persist(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID, true); + .persist(DATABASE_3_DTO, QUERY_5_ID, true); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -825,7 +801,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { }); } - protected List<QueryDto> generic_list(Long databaseId, PrivilegedDatabaseDto database, Principal principal) + protected List<QueryDto> generic_list(Long databaseId, DatabaseDto database, Principal principal) throws NotAllowedException, DatabaseUnavailableException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException { 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 e3171892a0..82be231e56 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 @@ -1,14 +1,14 @@ package at.tuwien.endpoint; import at.tuwien.api.database.DatabaseAccessDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.table.*; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.endpoints.TableEndpoint; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.service.CredentialService; -import at.tuwien.service.SchemaService; +import at.tuwien.service.DatabaseService; import at.tuwien.service.TableService; import at.tuwien.test.AbstractUnitTest; import jakarta.servlet.http.HttpServletRequest; @@ -61,7 +61,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { private TableService tableService; @MockBean - private SchemaService schemaService; + private DatabaseService databaseService; @MockBean private CredentialService credentialService; @@ -92,16 +92,16 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"}) - public void create_succeeds() throws DatabaseUnavailableException, TableMalformedException, + public void create_succeeds() throws DatabaseUnavailableException, TableMalformedException, ViewNotFoundException, DatabaseNotFoundException, TableExistsException, RemoteUnavailableException, SQLException, TableNotFoundException, QueryMalformedException, MetadataServiceException, ContainerNotFoundException { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(tableService.createTable(DATABASE_1_PRIVILEGED_DTO, TABLE_4_CREATE_INTERNAL_DTO)) + .thenReturn(DATABASE_1_DTO); + when(databaseService.createTable(DATABASE_1_DTO, TABLE_4_CREATE_INTERNAL_DTO)) .thenReturn(TABLE_4_DTO); - when(schemaService.inspectTable(DATABASE_1_PRIVILEGED_DTO, TABLE_4_INTERNALNAME)) + when(databaseService.inspectTable(DATABASE_1_DTO, TABLE_4_INTERNALNAME)) .thenReturn(TABLE_4_DTO); /* test */ @@ -142,10 +142,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); doThrow(SQLException.class) - .when(tableService) - .createTable(DATABASE_1_PRIVILEGED_DTO, TABLE_4_CREATE_INTERNAL_DTO); + .when(databaseService) + .createTable(DATABASE_1_DTO, TABLE_4_CREATE_INTERNAL_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -166,12 +166,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser public void statistic_succeeds() throws DatabaseUnavailableException, TableNotFoundException, SQLException, - TableMalformedException, RemoteUnavailableException, MetadataServiceException { + TableMalformedException, RemoteUnavailableException, MetadataServiceException, DatabaseNotFoundException { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); - when(tableService.getStatistics(any(PrivilegedTableDto.class))) + .thenReturn(TABLE_8_DTO); + when(tableService.getStatistics(any(DatabaseDto.class), any(TableDto.class))) .thenReturn(TABLE_8_STATISTIC_DTO); /* test */ @@ -186,10 +186,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); doThrow(SQLException.class) .when(tableService) - .getStatistics(any(PrivilegedTableDto.class)); + .getStatistics(any(DatabaseDto.class), any(TableDto.class)); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -220,10 +220,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); doNothing() .when(tableService) - .delete(TABLE_1_PRIVILEGED_DTO); + .delete(TABLE_1_DTO); /* test */ final ResponseEntity<Void> response = tableEndpoint.delete(DATABASE_1_ID, TABLE_1_ID); @@ -263,10 +263,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); doThrow(SQLException.class) .when(tableService) - .delete(TABLE_1_PRIVILEGED_DTO); + .delete(TABLE_1_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -277,13 +277,14 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser public void getData_succeeds() throws DatabaseUnavailableException, TableNotFoundException, QueryMalformedException, - RemoteUnavailableException, PaginationException, MetadataServiceException, NotAllowedException { + RemoteUnavailableException, PaginationException, MetadataServiceException, NotAllowedException, + DatabaseNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); - when(tableService.getData(eq(DATABASE_3_PRIVILEGED_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + .thenReturn(TABLE_8_DTO); + when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -298,15 +299,15 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void getData_head_succeeds() throws DatabaseUnavailableException, TableNotFoundException, SQLException, QueryMalformedException, RemoteUnavailableException, PaginationException, - MetadataServiceException, NotAllowedException { + MetadataServiceException, NotAllowedException, DatabaseNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); - when(tableService.getCount(eq(TABLE_8_PRIVILEGED_DTO), any(Instant.class))) + .thenReturn(TABLE_8_DTO); + when(tableService.getCount(eq(TABLE_8_DTO), any(Instant.class))) .thenReturn(3L); - when(tableService.getData(eq(DATABASE_3_PRIVILEGED_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); @@ -328,7 +329,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -343,7 +344,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_2_ID); @@ -361,10 +362,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); doThrow(QueryMalformedException.class) .when(tableService) - .getData(eq(DATABASE_3_PRIVILEGED_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null)); + .getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null)); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -381,7 +382,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); doThrow(RemoteUnavailableException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_2_ID); @@ -397,15 +398,15 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @MethodSource("anyAccess_parameters") public void getData_private_succeeds(String name, DatabaseAccessDto access) throws DatabaseUnavailableException, TableNotFoundException, QueryMalformedException, RemoteUnavailableException, PaginationException, - MetadataServiceException, NotAllowedException { + MetadataServiceException, NotAllowedException, DatabaseNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(access); - when(tableService.getData(eq(DATABASE_1_PRIVILEGED_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + when(tableService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -445,12 +446,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .createTuple(TABLE_8_PRIVILEGED_DTO, request); + .createTuple(TABLE_8_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -511,7 +512,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); @@ -535,12 +536,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doThrow(SQLException.class) .when(tableService) - .createTuple(TABLE_8_PRIVILEGED_DTO, request); + .createTuple(TABLE_8_DTO, request); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -562,7 +563,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); @@ -583,7 +584,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); @@ -607,7 +608,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); @@ -632,12 +633,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .updateTuple(TABLE_8_PRIVILEGED_DTO, request); + .updateTuple(TABLE_8_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -707,7 +708,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); @@ -733,12 +734,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); doThrow(SQLException.class) .when(tableService) - .updateTuple(TABLE_8_PRIVILEGED_DTO, request); + .updateTuple(TABLE_8_DTO, request); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -763,12 +764,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .updateTuple(TABLE_8_PRIVILEGED_DTO, request); + .updateTuple(TABLE_8_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -794,7 +795,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); @@ -821,12 +822,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); doNothing() .when(tableService) - .updateTuple(TABLE_8_PRIVILEGED_DTO, request); + .updateTuple(TABLE_8_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -849,12 +850,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); + .deleteTuple(TABLE_8_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -912,7 +913,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); @@ -934,12 +935,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); doThrow(SQLException.class) .when(tableService) - .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); + .deleteTuple(TABLE_8_DTO, request); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -960,12 +961,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); + .deleteTuple(TABLE_8_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -987,7 +988,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); @@ -1010,12 +1011,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); doNothing() .when(tableService) - .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); + .deleteTuple(TABLE_8_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -1032,8 +1033,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); - when(tableService.history(TABLE_8_PRIVILEGED_DTO, null)) + .thenReturn(TABLE_8_DTO); + when(tableService.history(TABLE_8_DTO, null)) .thenReturn(List.of()); /* test */ @@ -1048,7 +1049,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -1074,7 +1075,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_4_ID); @@ -1092,10 +1093,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(DATABASE_1_USER_2_READ_ACCESS_DTO); - when(tableService.history(TABLE_1_PRIVILEGED_DTO, 10L)) + when(tableService.history(TABLE_1_DTO, 10L)) .thenReturn(List.of()); /* test */ @@ -1110,10 +1111,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); doThrow(SQLException.class) .when(tableService) - .history(TABLE_8_PRIVILEGED_DTO, 100L); + .history(TABLE_8_DTO, 100L); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -1139,14 +1140,14 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void exportData_succeeds() throws TableNotFoundException, NotAllowedException, - StorageUnavailableException, QueryMalformedException, RemoteUnavailableException, MetadataServiceException { + public void exportData_succeeds() throws TableNotFoundException, NotAllowedException, StorageUnavailableException, + QueryMalformedException, RemoteUnavailableException, MetadataServiceException, DatabaseNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); - when(tableService.getData(eq(DATABASE_3_PRIVILEGED_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + .thenReturn(TABLE_8_DTO); + when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) .thenReturn(mock); /* test */ @@ -1159,15 +1160,15 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @MethodSource("anyAccess_parameters") public void exportData_private_succeeds(String name, DatabaseAccessDto access) throws TableNotFoundException, NotAllowedException, StorageUnavailableException, QueryMalformedException, RemoteUnavailableException, - MetadataServiceException { + MetadataServiceException, DatabaseNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(access); - when(tableService.getData(eq(DATABASE_1_PRIVILEGED_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + when(tableService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) .thenReturn(mock); /* test */ @@ -1182,7 +1183,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_4_ID); @@ -1201,8 +1202,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(tableService.getSchemas(DATABASE_3_PRIVILEGED_DTO)) + .thenReturn(DATABASE_3_DTO); + when(databaseService.exploreTables(DATABASE_3_DTO)) .thenReturn(List.of(TABLE_8_DTO)); /* test */ @@ -1237,10 +1238,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); + .thenReturn(DATABASE_3_DTO); doThrow(SQLException.class) - .when(tableService) - .getSchemas(DATABASE_3_PRIVILEGED_DTO); + .when(databaseService) + .exploreTables(DATABASE_3_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -1260,12 +1261,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .importDataset(TABLE_8_PRIVILEGED_DTO, request); + .importDataset(TABLE_8_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -1324,12 +1325,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); doThrow(SQLException.class) .when(tableService) - .importDataset(any(PrivilegedTableDto.class), eq(request)); + .importDataset(any(TableDto.class), eq(request)); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -1350,12 +1351,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); doThrow(SQLException.class) .when(tableService) - .importDataset(any(PrivilegedTableDto.class), eq(request)); + .importDataset(any(TableDto.class), eq(request)); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -1375,7 +1376,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); @@ -1398,7 +1399,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); @@ -1418,7 +1419,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); @@ -1441,7 +1442,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); @@ -1462,7 +1463,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(DATABASE_1_USER_2_WRITE_ALL_ACCESS_DTO); @@ -1483,7 +1484,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_2_ID)) - .thenReturn(TABLE_2_PRIVILEGED_DTO); + .thenReturn(TABLE_2_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO); @@ -1503,7 +1504,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO); @@ -1525,7 +1526,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_2_ID)) - .thenReturn(TABLE_2_PRIVILEGED_DTO); + .thenReturn(TABLE_2_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(DATABASE_1_USER_2_READ_ACCESS_DTO); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java index 5f580a2fc1..8586f8f92d 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java @@ -4,6 +4,7 @@ import at.tuwien.api.database.ViewDto; import at.tuwien.endpoints.ViewEndpoint; import at.tuwien.exception.*; import at.tuwien.service.CredentialService; +import at.tuwien.service.DatabaseService; import at.tuwien.service.TableService; import at.tuwien.service.ViewService; import at.tuwien.test.AbstractUnitTest; @@ -40,6 +41,9 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { @MockBean private ViewService viewService; + @MockBean + private DatabaseService databaseService; + @MockBean private CredentialService credentialService; @@ -67,8 +71,8 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(viewService.create(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO)) + .thenReturn(DATABASE_1_DTO); + when(databaseService.createView(DATABASE_1_DTO, VIEW_1_CREATE_DTO)) .thenReturn(VIEW_1_DTO); /* test */ @@ -83,10 +87,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); doThrow(SQLException.class) - .when(viewService) - .create(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO); + .when(databaseService) + .createView(DATABASE_1_DTO, VIEW_1_CREATE_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -101,8 +105,8 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(viewService.create(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO)) + .thenReturn(DATABASE_1_DTO); + when(databaseService.createView(DATABASE_1_DTO, VIEW_1_CREATE_DTO)) .thenReturn(VIEW_1_DTO); /* test */ @@ -134,8 +138,8 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); - when(viewService.getSchemas(DATABASE_1_PRIVILEGED_DTO)) + .thenReturn(DATABASE_1_DTO); + when(databaseService.exploreViews(DATABASE_1_DTO)) .thenReturn(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO)); /* test */ @@ -176,10 +180,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); doThrow(SQLException.class) - .when(viewService) - .getSchemas(DATABASE_1_PRIVILEGED_DTO); + .when(databaseService) + .exploreViews(DATABASE_1_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -204,10 +208,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_PRIVILEGED_DTO); + .thenReturn(VIEW_1_DTO); doNothing() .when(viewService) - .delete(DATABASE_1_PRIVILEGED_DTO, VIEW_1_INTERNAL_NAME); + .delete(VIEW_1_DTO); /* test */ final ResponseEntity<Void> response = viewEndpoint.delete(DATABASE_1_ID, VIEW_1_ID); @@ -221,10 +225,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_PRIVILEGED_DTO); + .thenReturn(VIEW_1_DTO); doThrow(SQLException.class) .when(viewService) - .delete(DATABASE_1_PRIVILEGED_DTO, VIEW_1_INTERNAL_NAME); + .delete(VIEW_1_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -239,10 +243,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); doNothing() .when(viewService) - .delete(DATABASE_1_PRIVILEGED_DTO, VIEW_1_INTERNAL_NAME); + .delete(VIEW_1_DTO); /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { @@ -268,17 +272,17 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"}) - public void getData_private_succeeds() throws RemoteUnavailableException, ViewNotFoundException, ViewMalformedException, + public void getData_private_succeeds() throws RemoteUnavailableException, ViewNotFoundException, SQLException, DatabaseUnavailableException, QueryMalformedException, PaginationException, - NotAllowedException, MetadataServiceException, TableNotFoundException { + NotAllowedException, MetadataServiceException, TableNotFoundException, DatabaseNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_PRIVILEGED_DTO); + .thenReturn(VIEW_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_1_ID)) .thenReturn(DATABASE_1_USER_1_READ_ACCESS_DTO); - when(tableService.getData(eq(DATABASE_1_PRIVILEGED_DTO), eq(VIEW_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(tableService.getData(eq(DATABASE_1_DTO), eq(VIEW_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -293,16 +297,16 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"}) public void getData_privateHead_succeeds() throws RemoteUnavailableException, ViewNotFoundException, SQLException, DatabaseUnavailableException, QueryMalformedException, PaginationException, - NotAllowedException, MetadataServiceException, TableNotFoundException { + NotAllowedException, MetadataServiceException, TableNotFoundException, DatabaseNotFoundException { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID)) - .thenReturn(VIEW_3_PRIVILEGED_DTO); + .thenReturn(VIEW_3_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_1_ID)) .thenReturn(DATABASE_1_USER_1_READ_ACCESS_DTO); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); - when(viewService.count(eq(VIEW_3_PRIVILEGED_DTO), any(Instant.class))) + when(viewService.count(eq(VIEW_3_DTO), any(Instant.class))) .thenReturn(VIEW_3_DATA_COUNT); /* test */ @@ -324,7 +328,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID)) - .thenReturn(VIEW_3_PRIVILEGED_DTO); + .thenReturn(VIEW_3_DTO); when(httpServletRequest.getMethod()) .thenReturn("GET"); doThrow(NotAllowedException.class) @@ -360,7 +364,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID)) - .thenReturn(VIEW_3_PRIVILEGED_DTO); + .thenReturn(VIEW_3_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_3_ID); @@ -378,7 +382,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID)) - .thenReturn(VIEW_3_PRIVILEGED_DTO); + .thenReturn(VIEW_3_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_3_ID); @@ -412,7 +416,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID)) - .thenReturn(VIEW_3_PRIVILEGED_DTO); + .thenReturn(VIEW_3_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_1_ID); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java index f65de0707e..a8633836b8 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java @@ -1,16 +1,12 @@ package at.tuwien.gateway; import at.tuwien.api.container.ContainerDto; -import at.tuwien.api.container.internal.PrivilegedContainerDto; import at.tuwien.api.database.DatabaseAccessDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; import at.tuwien.api.database.table.TableDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.api.identifier.IdentifierBriefDto; import at.tuwien.api.user.UserDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; import at.tuwien.exception.*; import at.tuwien.test.AbstractUnitTest; import lombok.extern.log4j.Log4j2; @@ -75,13 +71,13 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { .body(TABLE_1_DTO)); /* test */ - final PrivilegedTableDto response = metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID); - assertEquals(IMAGE_1_JDBC, response.getDatabase().getContainer().getImage().getJdbcMethod()); - assertEquals(CONTAINER_1_HOST, response.getDatabase().getContainer().getHost()); - assertEquals(CONTAINER_1_PORT, response.getDatabase().getContainer().getPort()); - assertEquals(CONTAINER_1_PRIVILEGED_USERNAME, response.getDatabase().getContainer().getUsername()); - assertEquals(CONTAINER_1_PRIVILEGED_PASSWORD, response.getDatabase().getContainer().getPassword()); - assertEquals(DATABASE_1_INTERNALNAME, response.getDatabase().getInternalName()); + final TableDto response = metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID); + assertEquals(IMAGE_1_JDBC, response.getJdbcMethod()); + assertEquals(CONTAINER_1_HOST, response.getHost()); + assertEquals(CONTAINER_1_PORT, response.getPort()); + assertEquals(CONTAINER_1_PRIVILEGED_USERNAME, response.getUsername()); + assertEquals(CONTAINER_1_PRIVILEGED_PASSWORD, response.getPassword()); + assertEquals(DATABASE_1_INTERNALNAME, response.getDatabase()); assertEquals(TABLE_1_INTERNAL_NAME, response.getInternalName()); } @@ -181,13 +177,13 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { headers.set("X-Port", "" + CONTAINER_1_PORT); /* mock */ - when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class))) + when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseDto.class))) .thenReturn(ResponseEntity.ok() .headers(headers) - .body(DATABASE_1_PRIVILEGED_DTO)); + .body(DATABASE_1_DTO)); /* test */ - final PrivilegedDatabaseDto response = metadataServiceGateway.getDatabaseById(DATABASE_1_ID); + final DatabaseDto response = metadataServiceGateway.getDatabaseById(DATABASE_1_ID); assertEquals(DATABASE_1_ID, response.getId()); } @@ -197,7 +193,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ doThrow(HttpServerErrorException.class) .when(internalRestTemplate) - .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseDto.class)); /* test */ assertThrows(RemoteUnavailableException.class, () -> { @@ -211,7 +207,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ doThrow(HttpClientErrorException.NotFound.class) .when(internalRestTemplate) - .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseDto.class)); /* test */ assertThrows(DatabaseNotFoundException.class, () -> { @@ -223,7 +219,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { public void getDatabaseById_statusCode_fails() { /* mock */ - when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class))) + when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseDto.class))) .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT) .build()); @@ -240,7 +236,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD); /* mock */ - when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class))) + when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseDto.class))) .thenReturn(ResponseEntity.status(HttpStatus.OK) .headers(headers) .build()); @@ -261,7 +257,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { headers.add(customHeaders.get(j), ""); } /* mock */ - when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(PrivilegedDatabaseDto.class))) + when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseDto.class))) .thenReturn(ResponseEntity.status(HttpStatus.OK) .headers(headers) .build()); @@ -285,7 +281,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { .body(CONTAINER_1_DTO)); /* test */ - final PrivilegedContainerDto response = metadataServiceGateway.getContainerById(CONTAINER_1_ID); + final ContainerDto response = metadataServiceGateway.getContainerById(CONTAINER_1_ID); assertEquals(CONTAINER_1_ID, response.getId()); } @@ -388,7 +384,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { .body(VIEW_1_DTO)); /* test */ - final PrivilegedViewDto response = metadataServiceGateway.getViewById(CONTAINER_1_ID, VIEW_1_ID); + final ViewDto response = metadataServiceGateway.getViewById(CONTAINER_1_ID, VIEW_1_ID); assertEquals(VIEW_1_ID, response.getId()); } @@ -477,19 +473,6 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { }); } - @Test - public void getUserById_succeeds() throws RemoteUnavailableException, UserNotFoundException, MetadataServiceException { - - /* mock */ - when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class))) - .thenReturn(ResponseEntity.ok() - .body(USER_1_DTO)); - - /* test */ - final UserDto response = metadataServiceGateway.getUserById(USER_1_ID); - assertEquals(USER_1_ID, response.getId()); - } - @Test public void getUserById_unavailable_fails() { @@ -505,49 +488,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { } @Test - public void getUserById_notFound_fails() { - - /* mock */ - doThrow(HttpClientErrorException.NotFound.class) - .when(internalRestTemplate) - .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)); - - /* test */ - assertThrows(UserNotFoundException.class, () -> { - metadataServiceGateway.getUserById(USER_1_ID); - }); - } - - @Test - public void getUserById_statusCode_fails() { - - /* mock */ - when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class))) - .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT) - .build()); - - /* test */ - assertThrows(MetadataServiceException.class, () -> { - metadataServiceGateway.getUserById(USER_1_ID); - }); - } - - @Test - public void getUserById_emptyBody_fails() { - - /* mock */ - when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class))) - .thenReturn(ResponseEntity.ok() - .build()); - - /* test */ - assertThrows(MetadataServiceException.class, () -> { - metadataServiceGateway.getUserById(USER_1_ID); - }); - } - - @Test - public void getPrivilegedUserById_succeeds() throws RemoteUnavailableException, UserNotFoundException, + public void getUserById_succeeds() throws RemoteUnavailableException, UserNotFoundException, MetadataServiceException { final HttpHeaders headers = new HttpHeaders(); headers.set("X-Username", CONTAINER_1_PRIVILEGED_USERNAME); @@ -560,28 +501,14 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { .body(USER_1_DTO)); /* test */ - final PrivilegedUserDto response = metadataServiceGateway.getPrivilegedUserById(USER_1_ID); + final UserDto response = metadataServiceGateway.getUserById(USER_1_ID); assertEquals(USER_1_ID, response.getId()); assertEquals(CONTAINER_1_PRIVILEGED_USERNAME, response.getUsername()); assertEquals(CONTAINER_1_PRIVILEGED_PASSWORD, response.getPassword()); } @Test - public void getPrivilegedUserById_unavailable_fails() { - - /* mock */ - doThrow(HttpServerErrorException.class) - .when(internalRestTemplate) - .exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class)); - - /* test */ - assertThrows(RemoteUnavailableException.class, () -> { - metadataServiceGateway.getPrivilegedUserById(USER_1_ID); - }); - } - - @Test - public void getPrivilegedUserById_notFound_fails() { + public void getUserById_notFound_fails() { /* mock */ doThrow(HttpClientErrorException.NotFound.class) @@ -590,12 +517,12 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* test */ assertThrows(UserNotFoundException.class, () -> { - metadataServiceGateway.getPrivilegedUserById(USER_1_ID); + metadataServiceGateway.getUserById(USER_1_ID); }); } @Test - public void getPrivilegedUserById_statusCode_fails() { + public void getUserById_statusCode_fails() { /* mock */ when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(UserDto.class))) @@ -604,12 +531,12 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* test */ assertThrows(MetadataServiceException.class, () -> { - metadataServiceGateway.getPrivilegedUserById(USER_1_ID); + metadataServiceGateway.getUserById(USER_1_ID); }); } @Test - public void getPrivilegedUserById_headerMissing_fails() { + public void getUserById_headerMissing_fails() { final List<String> customHeaders = List.of("X-Username", "X-Password"); for (int i = 0; i < customHeaders.size(); i++) { @@ -624,13 +551,13 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* test */ assertThrows(MetadataServiceException.class, () -> { - metadataServiceGateway.getPrivilegedUserById(USER_1_ID); + metadataServiceGateway.getUserById(USER_1_ID); }); } } @Test - public void getPrivilegedUserById_emptyBody_fails() { + public void getUserById_emptyBody_fails() { final HttpHeaders headers = new HttpHeaders(); headers.set("X-Username", CONTAINER_1_PRIVILEGED_USERNAME); headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD); @@ -643,7 +570,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* test */ assertThrows(MetadataServiceException.class, () -> { - metadataServiceGateway.getPrivilegedUserById(USER_1_ID); + metadataServiceGateway.getUserById(USER_1_ID); }); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java index 88bad7b061..44e5f88912 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java @@ -57,8 +57,8 @@ public class DefaultListenerIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* database */ - MariaDbConfig.dropAllDatabases(CONTAINER_1_PRIVILEGED_DTO); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.dropAllDatabases(CONTAINER_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); } @Test @@ -68,7 +68,7 @@ public class DefaultListenerIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ defaultListener.onMessage(request); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java index 648e36caa9..b3a3bf6397 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java @@ -52,8 +52,8 @@ public class DefaultListenerUnitTest extends AbstractUnitTest { @BeforeEach public void beforeEach() throws SQLException { /* metadata database */ - MariaDbConfig.dropAllDatabases(CONTAINER_1_PRIVILEGED_DTO); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.dropAllDatabases(CONTAINER_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); } @Test @@ -81,7 +81,7 @@ public class DefaultListenerUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ defaultListener.onMessage(request); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java index 158c6743a4..abbe28d55a 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java @@ -41,8 +41,8 @@ public class SubsetEndpointMvcTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_DTO); + when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); /* test */ @@ -56,8 +56,8 @@ public class SubsetEndpointMvcTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) - .thenReturn(DATABASE_3_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_DTO); + when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); /* test */ @@ -72,8 +72,8 @@ public class SubsetEndpointMvcTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_4_ID)) - .thenReturn(DATABASE_4_PRIVILEGED_DTO); - when(subsetService.findById(DATABASE_4_PRIVILEGED_DTO, QUERY_7_ID)) + .thenReturn(DATABASE_4_DTO); + when(subsetService.findById(DATABASE_4_DTO, QUERY_7_ID)) .thenReturn(QUERY_5_DTO); /* test */ diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/AccessServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/AccessServiceIntegrationTest.java index 5eccf50ed2..bc121c3b26 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/AccessServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/AccessServiceIntegrationTest.java @@ -44,16 +44,16 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); } @Test public void create_read_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.create(DATABASE_1_PRIVILEGED_DTO, USER_1_PRIVILEGED_DTO, AccessTypeDto.READ); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); + accessService.create(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.READ); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); for (String privilege : grantDefaultRead.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -63,8 +63,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void create_writeOwn_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.create(DATABASE_1_PRIVILEGED_DTO, USER_1_PRIVILEGED_DTO, AccessTypeDto.WRITE_OWN); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); + accessService.create(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.WRITE_OWN); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); for (String privilege : grantDefaultWrite.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -74,8 +74,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void create_writeAll_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.create(DATABASE_1_PRIVILEGED_DTO, USER_1_PRIVILEGED_DTO, AccessTypeDto.WRITE_ALL); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); + accessService.create(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.WRITE_ALL); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); for (String privilege : grantDefaultWrite.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -85,8 +85,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void update_read_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.update(DATABASE_1_PRIVILEGED_DTO, USER_1_PRIVILEGED_DTO, AccessTypeDto.READ); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); + accessService.update(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.READ); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); for (String privilege : grantDefaultRead.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -96,8 +96,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void update_writeOwn_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.update(DATABASE_1_PRIVILEGED_DTO, USER_1_PRIVILEGED_DTO, AccessTypeDto.WRITE_OWN); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); + accessService.update(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.WRITE_OWN); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); for (String privilege : grantDefaultWrite.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -107,8 +107,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void update_writeAll_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.update(DATABASE_1_PRIVILEGED_DTO, USER_1_PRIVILEGED_DTO, AccessTypeDto.WRITE_ALL); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); + accessService.update(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.WRITE_ALL); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); for (String privilege : grantDefaultWrite.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -119,7 +119,7 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseMalformedException.class, () -> { - accessService.update(DATABASE_1_PRIVILEGED_DTO, USER_5_PRIVILEGED_DTO, AccessTypeDto.WRITE_ALL); + accessService.update(DATABASE_1_DTO, USER_5_DTO, AccessTypeDto.WRITE_ALL); }); } @@ -127,8 +127,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void delete_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.delete(DATABASE_1_PRIVILEGED_DTO, USER_1_PRIVILEGED_DTO); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); + accessService.delete(DATABASE_1_DTO, USER_1_DTO); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); assertEquals(1, privileges.size()); assertEquals("USAGE", privileges.get(0)); } @@ -138,7 +138,7 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseMalformedException.class, () -> { - accessService.delete(DATABASE_1_PRIVILEGED_DTO, USER_5_PRIVILEGED_DTO); + accessService.delete(DATABASE_1_DTO, USER_5_DTO); }); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java new file mode 100644 index 0000000000..e793a8362c --- /dev/null +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java @@ -0,0 +1,109 @@ +package at.tuwien.service; + +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.config.MariaDbConfig; +import at.tuwien.config.MariaDbContainerConfig; +import at.tuwien.exception.DatabaseMalformedException; +import at.tuwien.exception.QueryStoreCreateException; +import at.tuwien.test.AbstractUnitTest; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +@Log4j2 +@SpringBootTest +@ExtendWith(SpringExtension.class) +@Testcontainers +public class ContainerServiceIntegrationTest extends AbstractUnitTest { + + @Autowired + private ContainerService containerService; + + @Container + private static MariaDBContainer<?> mariaDBContainer = MariaDbContainerConfig.getContainer(); + + @BeforeAll + public static void beforeAll() throws InterruptedException { + Thread.sleep(1000) /* wait for test container some more */; + } + + @BeforeEach + public void beforeEach() throws SQLException { + genesis(); + /* metadata database */ + MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); + } + + @Test + public void create_succeeds() throws SQLException, DatabaseMalformedException { + + /* test */ + final DatabaseDto response = containerService.createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL); + assertNull(response.getName()); + assertEquals(DATABASE_1_INTERNALNAME, response.getInternalName()); + assertEquals(EXCHANGE_DBREPO_NAME, response.getExchangeName()); + assertNotNull(response.getOwner()); + assertEquals(USER_1_ID, response.getOwner().getId()); + assertNotNull(response.getContact()); + assertEquals(USER_1_ID, response.getContact().getId()); + assertNotNull(response.getContainer()); + assertEquals(CONTAINER_1_ID, response.getContainer().getId()); + } + + @Test + public void create_exists_fails() throws SQLException { + + /* mock */ + MariaDbConfig.createDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); + + /* test */ + assertThrows(DatabaseMalformedException.class, () -> { + containerService.createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL); + }); + } + + @Test + public void createQueryStore_succeeds() throws SQLException, QueryStoreCreateException, InterruptedException { + + /* mock */ + MariaDbConfig.dropQueryStore(DATABASE_1_DTO); + + /* test */ + createQueryStore_generic(DATABASE_1_INTERNALNAME); + } + + @Test + public void createQueryStore_fails() { + + /* test */ + assertThrows(QueryStoreCreateException.class, () -> { + createQueryStore_generic(DATABASE_1_INTERNALNAME); + }); + } + + protected void createQueryStore_generic(String databaseName) throws SQLException, QueryStoreCreateException, + InterruptedException { + + /* pre-condition */ + Thread.sleep(1000) /* wait for test container some more */; + + /* test */ + containerService.createQueryStore(CONTAINER_1_DTO, databaseName); + final List<Map<String, Object>> response = MariaDbConfig.listQueryStore(DATABASE_1_DTO); + assertEquals(0, response.size()); + } +} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/CredentialServiceUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/CredentialServiceUnitTest.java index 160918bfaa..8b5e2cc7c5 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/CredentialServiceUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/CredentialServiceUnitTest.java @@ -1,11 +1,11 @@ package at.tuwien.service; -import at.tuwien.api.container.internal.PrivilegedContainerDto; +import at.tuwien.api.container.ContainerDto; import at.tuwien.api.database.DatabaseAccessDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.ViewDto; +import at.tuwien.api.database.table.TableDto; +import at.tuwien.api.user.UserDto; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.service.impl.CredentialServiceImpl; @@ -48,10 +48,10 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_1_DTO); /* test */ - final PrivilegedDatabaseDto response = credentialService.getDatabase(DATABASE_1_ID); + final DatabaseDto response = credentialService.getDatabase(DATABASE_1_ID); assertNotNull(response); assertEquals(DATABASE_1_ID, response.getId()); } @@ -62,12 +62,12 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) - .thenReturn(DATABASE_1_PRIVILEGED_DTO) + .thenReturn(DATABASE_1_DTO) .thenThrow(RuntimeException.class) /* should never be thrown */; credentialService.getDatabase(DATABASE_1_ID); /* test */ - final PrivilegedDatabaseDto response = credentialService.getDatabase(DATABASE_1_ID); + final DatabaseDto response = credentialService.getDatabase(DATABASE_1_ID); assertNotNull(response); assertEquals(DATABASE_1_ID, response.getId()); } @@ -78,16 +78,16 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) - .thenReturn(DATABASE_2_PRIVILEGED_DTO) /* needs to be different id for test case */ - .thenReturn(DATABASE_1_PRIVILEGED_DTO); + .thenReturn(DATABASE_2_DTO) /* needs to be different id for test case */ + .thenReturn(DATABASE_1_DTO); /* pre-condition */ - final PrivilegedDatabaseDto tmp = credentialService.getDatabase(DATABASE_1_ID); + final DatabaseDto tmp = credentialService.getDatabase(DATABASE_1_ID); assertNotEquals(DATABASE_1_ID, tmp.getId()); Thread.sleep(5000); /* test */ - final PrivilegedDatabaseDto response = credentialService.getDatabase(DATABASE_1_ID); + final DatabaseDto response = credentialService.getDatabase(DATABASE_1_ID); assertNotNull(response); assertEquals(DATABASE_1_ID, response.getId()); } @@ -98,10 +98,10 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); /* test */ - final PrivilegedContainerDto response = credentialService.getContainer(CONTAINER_1_ID); + final ContainerDto response = credentialService.getContainer(CONTAINER_1_ID); assertNotNull(response); assertEquals(CONTAINER_1_ID, response.getId()); } @@ -112,12 +112,12 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO) + .thenReturn(CONTAINER_1_DTO) .thenThrow(RuntimeException.class) /* should never be thrown */; credentialService.getContainer(CONTAINER_1_ID); /* test */ - final PrivilegedContainerDto response = credentialService.getContainer(CONTAINER_1_ID); + final ContainerDto response = credentialService.getContainer(CONTAINER_1_ID); assertNotNull(response); assertEquals(CONTAINER_1_ID, response.getId()); } @@ -128,16 +128,16 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(DATABASE_1_ID)) - .thenReturn(CONTAINER_2_PRIVILEGED_DTO) /* needs to be different id for test case */ - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_2_DTO) /* needs to be different id for test case */ + .thenReturn(CONTAINER_1_DTO); /* pre-condition */ - final PrivilegedContainerDto tmp = credentialService.getContainer(CONTAINER_1_ID); + final ContainerDto tmp = credentialService.getContainer(CONTAINER_1_ID); assertNotEquals(CONTAINER_1_ID, tmp.getId()); Thread.sleep(5000); /* test */ - final PrivilegedContainerDto response = credentialService.getContainer(CONTAINER_1_ID); + final ContainerDto response = credentialService.getContainer(CONTAINER_1_ID); assertNotNull(response); assertEquals(CONTAINER_1_ID, response.getId()); } @@ -147,11 +147,11 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { UserNotFoundException { /* mock */ - when(metadataServiceGateway.getPrivilegedUserById(USER_1_ID)) - .thenReturn(USER_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getUserById(USER_1_ID)) + .thenReturn(USER_1_DTO); /* test */ - final PrivilegedUserDto response = credentialService.getUser(USER_1_ID); + final UserDto response = credentialService.getUser(USER_1_ID); assertNotNull(response); assertEquals(USER_1_ID, response.getId()); } @@ -161,13 +161,13 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { UserNotFoundException { /* mock */ - when(metadataServiceGateway.getPrivilegedUserById(USER_1_ID)) - .thenReturn(USER_1_PRIVILEGED_DTO) + when(metadataServiceGateway.getUserById(USER_1_ID)) + .thenReturn(USER_1_DTO) .thenThrow(RuntimeException.class) /* should never be thrown */; credentialService.getUser(USER_1_ID); /* test */ - final PrivilegedUserDto response = credentialService.getUser(USER_1_ID); + final UserDto response = credentialService.getUser(USER_1_ID); assertNotNull(response); assertEquals(USER_1_ID, response.getId()); } @@ -177,17 +177,17 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { InterruptedException, UserNotFoundException { /* mock */ - when(metadataServiceGateway.getPrivilegedUserById(USER_1_ID)) - .thenReturn(USER_2_PRIVILEGED_DTO) /* needs to be different id for test case */ - .thenReturn(USER_1_PRIVILEGED_DTO); + when(metadataServiceGateway.getUserById(USER_1_ID)) + .thenReturn(USER_2_DTO) /* needs to be different id for test case */ + .thenReturn(USER_1_DTO); /* pre-condition */ - final PrivilegedUserDto tmp = credentialService.getUser(USER_1_ID); + final UserDto tmp = credentialService.getUser(USER_1_ID); assertNotEquals(USER_1_ID, tmp.getId()); Thread.sleep(5000); /* test */ - final PrivilegedUserDto response = credentialService.getUser(USER_1_ID); + final UserDto response = credentialService.getUser(USER_1_ID); assertNotNull(response); assertEquals(USER_1_ID, response.getId()); } @@ -251,10 +251,10 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - final PrivilegedTableDto response = credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); + final TableDto response = credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); assertNotNull(response); assertEquals(TABLE_1_ID, response.getId()); } @@ -265,12 +265,12 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO) + .thenReturn(TABLE_1_DTO) .thenThrow(RuntimeException.class) /* should never be thrown */; credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); /* test */ - final PrivilegedTableDto response = credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); + final TableDto response = credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); assertNotNull(response); assertEquals(TABLE_1_ID, response.getId()); } @@ -281,16 +281,16 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_2_PRIVILEGED_DTO) /* needs to be different id for test case */ - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_2_DTO) /* needs to be different id for test case */ + .thenReturn(TABLE_1_DTO); /* pre-condition */ - final PrivilegedTableDto tmp = credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); + final TableDto tmp = credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); assertNotEquals(TABLE_1_ID, tmp.getId()); Thread.sleep(5000); /* test */ - final PrivilegedTableDto response = credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); + final TableDto response = credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); assertNotNull(response); assertEquals(TABLE_1_ID, response.getId()); } @@ -301,10 +301,10 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_PRIVILEGED_DTO); + .thenReturn(VIEW_1_DTO); /* test */ - final PrivilegedViewDto response = credentialService.getView(DATABASE_1_ID, VIEW_1_ID); + final ViewDto response = credentialService.getView(DATABASE_1_ID, VIEW_1_ID); assertNotNull(response); assertEquals(VIEW_1_ID, response.getId()); } @@ -314,12 +314,12 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_PRIVILEGED_DTO) + .thenReturn(VIEW_1_DTO) .thenThrow(RuntimeException.class) /* should never be thrown */; credentialService.getView(DATABASE_1_ID, VIEW_1_ID); /* test */ - final PrivilegedViewDto response = credentialService.getView(DATABASE_1_ID, VIEW_1_ID); + final ViewDto response = credentialService.getView(DATABASE_1_ID, VIEW_1_ID); assertNotNull(response); assertEquals(VIEW_1_ID, response.getId()); } @@ -330,16 +330,16 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_2_PRIVILEGED_DTO) /* needs to be different id for test case */ - .thenReturn(VIEW_1_PRIVILEGED_DTO); + .thenReturn(VIEW_2_DTO) /* needs to be different id for test case */ + .thenReturn(VIEW_1_DTO); /* pre-condition */ - final PrivilegedViewDto tmp = credentialService.getView(DATABASE_1_ID, VIEW_1_ID); + final ViewDto tmp = credentialService.getView(DATABASE_1_ID, VIEW_1_ID); assertNotEquals(VIEW_1_ID, tmp.getId()); Thread.sleep(5000); /* test */ - final PrivilegedViewDto response = credentialService.getView(DATABASE_1_ID, VIEW_1_ID); + final ViewDto response = credentialService.getView(DATABASE_1_ID, VIEW_1_ID); assertNotNull(response); assertEquals(VIEW_1_ID, response.getId()); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java index 83d81715bd..26ff951dda 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java @@ -1,12 +1,29 @@ package at.tuwien.service; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; +import at.tuwien.api.database.ViewColumnDto; +import at.tuwien.api.database.ViewDto; +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.ColumnTypeDto; +import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; +import at.tuwien.api.database.table.constraints.ConstraintsDto; +import at.tuwien.api.database.table.constraints.foreign.ForeignKeyCreateDto; +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.database.table.internal.TableCreateDto; +import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.internal.UpdateUserPasswordDto; import at.tuwien.config.MariaDbConfig; import at.tuwien.config.MariaDbContainerConfig; -import at.tuwien.exception.DatabaseMalformedException; +import at.tuwien.exception.*; import at.tuwien.test.AbstractUnitTest; import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -19,6 +36,9 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import java.sql.SQLException; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; import static org.junit.jupiter.api.Assertions.*; @@ -43,35 +63,39 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); } @Test - public void create_succeeds() throws SQLException, DatabaseMalformedException { + public void createView_succeeds() throws SQLException, ViewMalformedException { /* test */ - final PrivilegedDatabaseDto response = databaseService.create(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_CREATE_INTERNAL); - assertNull(response.getName()); - assertEquals(DATABASE_1_INTERNALNAME, response.getInternalName()); - assertEquals(EXCHANGE_DBREPO_NAME, response.getExchangeName()); - assertNotNull(response.getOwner()); - assertEquals(USER_1_ID, response.getOwner().getId()); - assertNotNull(response.getContact()); - assertEquals(USER_1_ID, response.getContact().getId()); - assertNotNull(response.getContainer()); - assertEquals(CONTAINER_1_ID, response.getContainer().getId()); + databaseService.createView(DATABASE_1_DTO, VIEW_1_CREATE_DTO); } @Test - public void create_exists_fails() throws SQLException { - - /* mock */ - MariaDbConfig.createDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + public void exploreViews_succeeds() throws SQLException, ViewNotFoundException, DatabaseMalformedException { /* test */ - assertThrows(DatabaseMalformedException.class, () -> { - databaseService.create(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_CREATE_INTERNAL); - }); + final List<ViewDto> response = databaseService.exploreViews(DATABASE_1_DTO); + final ViewDto view0 = response.get(0); + assertEquals("not_in_metadata_db2", view0.getName()); + assertEquals("not_in_metadata_db2", view0.getInternalName()); + assertEquals(DATABASE_1_ID, view0.getVdbid()); + assertEquals(DATABASE_1_OWNER, view0.getOwner().getId()); + assertFalse(view0.getIsInitialView()); + assertEquals(DATABASE_1_PUBLIC, view0.getIsPublic()); + assertTrue(view0.getQuery().length() >= 69); + assertNotNull(view0.getQueryHash()); + assertEquals(4, view0.getColumns().size()); + final ViewColumnDto column0a = view0.getColumns().get(0); + assertEquals("date", column0a.getInternalName()); + final ViewColumnDto column1a = view0.getColumns().get(1); + assertEquals("location", column1a.getInternalName()); + final ViewColumnDto column2a = view0.getColumns().get(2); + assertEquals("MinTemp", column2a.getInternalName()); + final ViewColumnDto column3a = view0.getColumns().get(3); + assertEquals("Rainfall", column3a.getInternalName()); } @Test @@ -82,8 +106,8 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { .build(); /* mock */ - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); - MariaDbConfig.grantWriteAccess(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); + MariaDbConfig.grantWriteAccess(DATABASE_1_DTO, USER_1_USERNAME); /* pre-condition */ MariaDbConfig.mockQuery(CONTAINER_1_HOST, CONTAINER_1_PORT, DATABASE_1_INTERNALNAME, "CREATE SEQUENCE debug NOCACHE", USER_1_USERNAME, USER_1_PASSWORD); @@ -95,7 +119,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { } /* test */ - databaseService.update(DATABASE_1_PRIVILEGED_DTO, request); + databaseService.update(DATABASE_1_DTO, request); MariaDbConfig.mockQuery(CONTAINER_1_HOST, CONTAINER_1_PORT, DATABASE_1_INTERNALNAME, "CREATE SEQUENCE debug2 NOCACHE", USER_1_USERNAME, USER_2_PASSWORD); } @@ -107,12 +131,612 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { .build(); /* mock */ - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); /* test */ assertThrows(DatabaseMalformedException.class, () -> { - databaseService.update(DATABASE_1_PRIVILEGED_DTO, request); + databaseService.update(DATABASE_1_DTO, request); }); } + + @Test + public void inspectTable_sameNameDifferentDb_succeeds() throws TableNotFoundException, SQLException { + + /* mock */ + MariaDbConfig.execute(DATABASE_2_DTO, "CREATE TABLE not_in_metadata_db (wrong_id BIGINT NOT NULL PRIMARY KEY, given_name VARCHAR(255) NOT NULL, middle_name VARCHAR(255), family_name VARCHAR(255) NOT NULL, age INT NOT NULL) WITH SYSTEM VERSIONING;"); + + /* test */ + final TableDto response = databaseService.inspectTable(DATABASE_1_DTO, "not_in_metadata_db"); + assertEquals("not_in_metadata_db", response.getInternalName()); + assertEquals("not_in_metadata_db", response.getName()); + assertEquals(DATABASE_1_ID, response.getTdbid()); + assertTrue(response.getIsVersioned()); + assertEquals(DATABASE_1_PUBLIC, response.getIsPublic()); + final List<ColumnDto> columns = response.getColumns(); + assertNotNull(columns); + assertEquals(5, columns.size()); + assertColumn(columns.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); + assertColumn(columns.get(1), null, null, DATABASE_1_ID, "given_name", "given_name", ColumnTypeDto.VARCHAR, 255L, null, false, null); + assertColumn(columns.get(2), null, null, DATABASE_1_ID, "middle_name", "middle_name", ColumnTypeDto.VARCHAR, 255L, null, true, null); + assertColumn(columns.get(3), null, null, DATABASE_1_ID, "family_name", "family_name", ColumnTypeDto.VARCHAR, 255L, null, false, null); + assertColumn(columns.get(4), null, null, DATABASE_1_ID, "age", "age", ColumnTypeDto.INT, 10L, 0L, false, null); + final ConstraintsDto constraints = response.getConstraints(); + assertNotNull(constraints); + final Set<PrimaryKeyDto> primaryKey = constraints.getPrimaryKey(); + assertEquals(1, primaryKey.size()); + final Set<String> checks = constraints.getChecks(); + assertEquals(1, checks.size()); + assertEquals(Set.of("`age` > 0 and `age` < 120"), checks); + final List<UniqueDto> uniques = constraints.getUniques(); + assertEquals(1, uniques.size()); + assertEquals(2, uniques.get(0).getColumns().size()); + assertEquals("not_in_metadata_db", uniques.get(0).getTable().getName()); + assertEquals("not_in_metadata_db", uniques.get(0).getTable().getInternalName()); + assertEquals("given_name", uniques.get(0).getColumns().get(0).getInternalName()); + assertEquals("family_name", uniques.get(0).getColumns().get(1).getInternalName()); + final List<ForeignKeyDto> foreignKeys = constraints.getForeignKeys(); + assertEquals(0, foreignKeys.size()); + } + + @Test + public void inspectTableEnum_succeeds() throws TableNotFoundException, SQLException { + + /* test */ + final TableDto response = databaseService.inspectTable(DATABASE_2_DTO, "experiments"); + assertEquals("experiments", response.getInternalName()); + assertEquals("experiments", response.getName()); + assertEquals(DATABASE_2_ID, response.getTdbid()); + assertTrue(response.getIsVersioned()); + assertEquals(DATABASE_2_PUBLIC, response.getIsPublic()); + assertNotNull(response.getOwner()); + assertEquals(DATABASE_2_OWNER, response.getOwner().getId()); + assertEquals(USER_2_NAME, response.getOwner().getName()); + assertEquals(USER_2_USERNAME, response.getOwner().getUsername()); + assertEquals(USER_2_FIRSTNAME, response.getOwner().getFirstname()); + assertEquals(USER_2_LASTNAME, response.getOwner().getLastname()); + assertEquals(USER_2_QUALIFIED_NAME, response.getOwner().getQualifiedName()); + final List<IdentifierDto> identifiers = response.getIdentifiers(); + assertNotNull(identifiers); + assertEquals(0, identifiers.size()); + final List<ColumnDto> columns = response.getColumns(); + assertNotNull(columns); + assertEquals(3, columns.size()); + assertColumn(columns.get(0), null, null, DATABASE_2_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); + assertColumn(columns.get(1), null, null, DATABASE_2_ID, "mode", "mode", ColumnTypeDto.ENUM, 3L, null, false, null); + assertEquals(2, columns.get(1).getEnums().size()); + assertEquals(List.of("ABC", "DEF"), columns.get(1).getEnums()); + assertColumn(columns.get(2), null, null, DATABASE_2_ID, "seq", "seq", ColumnTypeDto.SET, 5L, null, true, null); + assertEquals(3, columns.get(2).getSets().size()); + assertEquals(List.of("1", "2", "3"), columns.get(2).getSets()); + /* ignore rest (constraints) */ + } + + @Test + public void inspectTableFullConstraints_succeeds() throws TableNotFoundException, SQLException { + + /* test */ + final TableDto response = databaseService.inspectTable(DATABASE_1_DTO, "weather_aus"); + assertEquals("weather_aus", response.getInternalName()); + assertEquals("weather_aus", response.getName()); + assertEquals(DATABASE_1_ID, response.getTdbid()); + assertTrue(response.getIsVersioned()); + assertEquals(DATABASE_1_PUBLIC, response.getIsPublic()); + assertNotNull(response.getOwner()); + assertEquals(DATABASE_1_OWNER, response.getOwner().getId()); + assertEquals(USER_1_NAME, response.getOwner().getName()); + assertEquals(USER_1_USERNAME, response.getOwner().getUsername()); + assertEquals(USER_1_FIRSTNAME, response.getOwner().getFirstname()); + assertEquals(USER_1_LASTNAME, response.getOwner().getLastname()); + assertEquals(USER_1_QUALIFIED_NAME, response.getOwner().getQualifiedName()); + final List<IdentifierDto> identifiers = response.getIdentifiers(); + assertNotNull(identifiers); + assertEquals(0, identifiers.size()); + final List<ColumnDto> columns = response.getColumns(); + assertNotNull(columns); + assertEquals(5, columns.size()); + assertColumn(columns.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 20L, 0L, false, null); + assertColumn(columns.get(1), null, null, DATABASE_1_ID, "date", "date", ColumnTypeDto.DATE, null, null, false, null); + assertColumn(columns.get(2), null, null, DATABASE_1_ID, "location", "location", ColumnTypeDto.VARCHAR, 255L, null, true, "Closest city"); + assertColumn(columns.get(3), null, null, DATABASE_1_ID, "mintemp", "mintemp", ColumnTypeDto.DOUBLE, 22L, null, true, null); + assertColumn(columns.get(4), null, null, DATABASE_1_ID, "rainfall", "rainfall", ColumnTypeDto.DOUBLE, 22L, null, true, null); + final ConstraintsDto constraints = response.getConstraints(); + final List<PrimaryKeyDto> primaryKey = new LinkedList<>(constraints.getPrimaryKey()); + assertEquals(1, primaryKey.size()); + final PrimaryKeyDto pk0 = primaryKey.get(0); + assertNull(pk0.getId()); + assertNotNull(pk0.getTable()); + assertNull(pk0.getTable().getId()); + assertEquals("weather_aus", pk0.getTable().getName()); + assertEquals("weather_aus", pk0.getTable().getInternalName()); + assertEquals("Weather in Australia", pk0.getTable().getDescription()); + assertNotNull(pk0.getColumn()); + assertNull(pk0.getColumn().getId()); + assertNull(pk0.getColumn().getTableId()); + assertEquals(DATABASE_1_ID, pk0.getColumn().getDatabaseId()); + assertNull(pk0.getColumn().getAlias()); + assertEquals("id", pk0.getColumn().getName()); + assertEquals("id", pk0.getColumn().getInternalName()); + assertEquals(ColumnTypeDto.BIGINT, pk0.getColumn().getColumnType()); + final List<UniqueDto> uniques = constraints.getUniques(); + assertEquals(1, uniques.size()); + final UniqueDto unique0 = uniques.get(0); + assertNotNull(unique0.getTable()); + assertEquals("some_constraint", unique0.getName()); + assertNull(unique0.getTable().getId()); + assertEquals(TABLE_1_INTERNAL_NAME, unique0.getTable().getName()); + assertEquals(TABLE_1_INTERNAL_NAME, unique0.getTable().getInternalName()); + assertEquals(TABLE_1_DESCRIPTION, unique0.getTable().getDescription()); + assertTrue(unique0.getTable().getIsVersioned()); + assertNotNull(unique0.getColumns()); + assertEquals(1, unique0.getColumns().size()); + assertNull(unique0.getColumns().get(0).getId()); + assertNull(unique0.getColumns().get(0).getTableId()); + assertEquals("date", unique0.getColumns().get(0).getName()); + assertEquals("date", unique0.getColumns().get(0).getInternalName()); + final List<String> checks = new LinkedList<>(constraints.getChecks()); + assertEquals("`mintemp` > 0", checks.get(0)); + final List<ForeignKeyDto> foreignKeys = constraints.getForeignKeys(); + assertEquals(1, foreignKeys.size()); + final ForeignKeyDto fk0 = foreignKeys.get(0); + assertNotNull(fk0.getName()); + assertNotNull(fk0.getReferences()); + final ForeignKeyReferenceDto fk0ref0 = fk0.getReferences().get(0); + assertNull(fk0ref0.getId()); + assertNotNull(fk0ref0.getColumn()); + assertNotNull(fk0ref0.getReferencedColumn()); + assertNotNull(fk0ref0.getForeignKey()); + assertEquals(DATABASE_1_ID, fk0ref0.getColumn().getDatabaseId()); + assertNull(fk0ref0.getColumn().getId()); + assertNull(fk0ref0.getColumn().getTableId()); + assertEquals("location", fk0ref0.getColumn().getName()); + assertEquals("location", fk0ref0.getColumn().getInternalName()); + assertEquals(DATABASE_1_ID, fk0ref0.getReferencedColumn().getDatabaseId()); + assertNull(fk0ref0.getReferencedColumn().getId()); + assertNull(fk0ref0.getReferencedColumn().getTableId()); + assertEquals("location", fk0ref0.getReferencedColumn().getName()); + assertEquals("location", fk0ref0.getReferencedColumn().getInternalName()); + assertNotNull(fk0.getOnUpdate()); + assertEquals(ReferenceTypeDto.RESTRICT, fk0.getOnUpdate()); + assertNotNull(fk0.getOnDelete()); + assertEquals(ReferenceTypeDto.SET_NULL, fk0.getOnDelete()); + final TableBriefDto fk0table = fk0.getTable(); + assertNull(fk0table.getId()); + assertEquals(DATABASE_1_ID, fk0table.getDatabaseId()); + assertEquals(TABLE_1_INTERNAL_NAME, fk0table.getName()); + assertEquals(TABLE_1_INTERNAL_NAME, fk0table.getInternalName()); + assertNotNull(fk0.getOnDelete()); + assertNotNull(fk0.getOnUpdate()); + assertNotNull(fk0.getReferencedTable()); + assertEquals(TABLE_2_INTERNALNAME, fk0.getReferencedTable().getName()); + assertEquals(TABLE_2_INTERNALNAME, fk0.getReferencedTable().getInternalName()); + } + + @Test + public void inspectTable_multipleForeignKeyReferences_succeeds() throws TableNotFoundException, SQLException { + + /* test */ + final TableDto response = databaseService.inspectTable(DATABASE_1_DTO, "complex_foreign_keys"); + final ConstraintsDto constraints = response.getConstraints(); + final List<ForeignKeyDto> foreignKeys = constraints.getForeignKeys(); + assertEquals(1, foreignKeys.size()); + final ForeignKeyDto fk0 = foreignKeys.get(0); + assertNotNull(fk0.getName()); + assertNotNull(fk0.getReferences()); + final ForeignKeyReferenceDto fk0ref0 = fk0.getReferences().get(0); + assertNull(fk0ref0.getId()); + assertNotNull(fk0ref0.getColumn()); + assertNotNull(fk0ref0.getReferencedColumn()); + assertNotNull(fk0ref0.getForeignKey()); + assertEquals(DATABASE_1_ID, fk0ref0.getColumn().getDatabaseId()); + assertNull(fk0ref0.getColumn().getId()); + assertNull(fk0ref0.getColumn().getTableId()); + assertEquals("weather_id", fk0ref0.getColumn().getName()); + assertEquals("weather_id", fk0ref0.getColumn().getInternalName()); + assertEquals(DATABASE_1_ID, fk0ref0.getReferencedColumn().getDatabaseId()); + assertNull(fk0ref0.getReferencedColumn().getId()); + assertNull(fk0ref0.getReferencedColumn().getTableId()); + assertEquals("id", fk0ref0.getReferencedColumn().getName()); + assertEquals("id", fk0ref0.getReferencedColumn().getInternalName()); + final ForeignKeyReferenceDto fk0ref1 = fk0.getReferences().get(1); + assertNull(fk0ref1.getId()); + assertNotNull(fk0ref1.getColumn()); + assertNotNull(fk0ref1.getReferencedColumn()); + assertNotNull(fk0ref1.getForeignKey()); + assertEquals(DATABASE_1_ID, fk0ref1.getColumn().getDatabaseId()); + assertNull(fk0ref1.getColumn().getId()); + assertNull(fk0ref1.getColumn().getTableId()); + assertEquals("other_id", fk0ref1.getColumn().getName()); + assertEquals("other_id", fk0ref1.getColumn().getInternalName()); + assertEquals(DATABASE_1_ID, fk0ref1.getReferencedColumn().getDatabaseId()); + assertNull(fk0ref1.getReferencedColumn().getId()); + assertNull(fk0ref1.getReferencedColumn().getTableId()); + assertEquals("other_id", fk0ref1.getReferencedColumn().getName()); + assertEquals("other_id", fk0ref1.getReferencedColumn().getInternalName()); + final TableBriefDto fk0refT0 = fk0.getTable(); + assertNull(fk0refT0.getId()); + assertEquals(DATABASE_1_ID, fk0refT0.getDatabaseId()); + assertEquals("complex_foreign_keys", fk0refT0.getName()); + assertEquals("complex_foreign_keys", fk0refT0.getInternalName()); + assertNotNull(fk0.getReferencedTable()); + assertEquals("complex_primary_key", fk0.getReferencedTable().getName()); + assertEquals("complex_primary_key", fk0.getReferencedTable().getInternalName()); + assertNotNull(fk0.getOnDelete()); + assertNotNull(fk0.getOnUpdate()); + } + + @Test + public void inspectTable_multiplePrimaryKey_succeeds() throws TableNotFoundException, SQLException { + + /* test */ + final TableDto response = databaseService.inspectTable(DATABASE_1_DTO, "complex_primary_key"); + final ConstraintsDto constraints = response.getConstraints(); + final List<PrimaryKeyDto> primaryKey = new LinkedList<>(constraints.getPrimaryKey()); + assertEquals(2, primaryKey.size()); + final PrimaryKeyDto pk0 = primaryKey.get(0); + assertNull(pk0.getId()); + assertNotNull(pk0.getTable()); + assertNull(pk0.getTable().getId()); + assertEquals("complex_primary_key", pk0.getTable().getName()); + assertEquals("complex_primary_key", pk0.getTable().getInternalName()); + assertNotNull(pk0.getColumn()); + assertNull(pk0.getColumn().getId()); + assertNull(pk0.getColumn().getTableId()); + assertEquals(DATABASE_1_ID, pk0.getColumn().getDatabaseId()); + assertNull(pk0.getColumn().getAlias()); + assertEquals("id", pk0.getColumn().getName()); + assertEquals("id", pk0.getColumn().getInternalName()); + assertEquals(ColumnTypeDto.BIGINT, pk0.getColumn().getColumnType()); + final PrimaryKeyDto pk1 = primaryKey.get(1); + assertNull(pk1.getId()); + assertNotNull(pk1.getTable()); + assertNull(pk1.getTable().getId()); + assertEquals("complex_primary_key", pk1.getTable().getName()); + assertEquals("complex_primary_key", pk1.getTable().getInternalName()); + assertNotNull(pk1.getColumn()); + assertNull(pk1.getColumn().getId()); + assertNull(pk1.getColumn().getTableId()); + assertEquals(DATABASE_1_ID, pk1.getColumn().getDatabaseId()); + assertNull(pk1.getColumn().getAlias()); + assertEquals("other_id", pk1.getColumn().getName()); + assertEquals("other_id", pk1.getColumn().getInternalName()); + assertEquals(ColumnTypeDto.BIGINT, pk1.getColumn().getColumnType()); + } + + @Test + public void inspectTable_exoticBoolean_succeeds() throws TableNotFoundException, SQLException { + + /* test */ + final TableDto response = databaseService.inspectTable(DATABASE_1_DTO, "exotic_boolean"); + final ConstraintsDto constraints = response.getConstraints(); + final List<PrimaryKeyDto> primaryKey = new LinkedList<>(constraints.getPrimaryKey()); + assertEquals(1, primaryKey.size()); + final PrimaryKeyDto pk0 = primaryKey.get(0); + assertNull(pk0.getId()); + assertNotNull(pk0.getTable()); + assertNull(pk0.getTable().getId()); + assertEquals("exotic_boolean", pk0.getTable().getName()); + assertEquals("exotic_boolean", pk0.getTable().getInternalName()); + assertNotNull(pk0.getColumn()); + assertNull(pk0.getColumn().getId()); + assertNull(pk0.getColumn().getTableId()); + assertEquals(DATABASE_1_ID, pk0.getColumn().getDatabaseId()); + assertNull(pk0.getColumn().getAlias()); + assertEquals("bool_default", pk0.getColumn().getName()); + assertEquals("bool_default", pk0.getColumn().getInternalName()); + assertEquals(ColumnTypeDto.BOOL, pk0.getColumn().getColumnType()); + final List<ColumnDto> columns = response.getColumns(); + assertEquals(3, columns.size()); + assertColumn(columns.get(0), null, null, DATABASE_1_ID, "bool_default", "bool_default", ColumnTypeDto.BOOL, null, 0L, false, null); + assertColumn(columns.get(1), null, null, DATABASE_1_ID, "bool_tinyint", "bool_tinyint", ColumnTypeDto.BOOL, null, 0L, false, null); + assertColumn(columns.get(2), null, null, DATABASE_1_ID, "bool_tinyint_unsigned", "bool_tinyint_unsigned", ColumnTypeDto.BOOL, null, 0L, false, null); + } + + @Test + public void inspectView_succeeds() throws SQLException, ViewNotFoundException { + + /* test */ + final ViewDto response = databaseService.inspectView(DATABASE_1_DTO, "not_in_metadata_db2"); + assertEquals("not_in_metadata_db2", response.getInternalName()); + assertEquals("not_in_metadata_db2", response.getName()); + assertEquals(DATABASE_1_ID, response.getVdbid()); + assertEquals(DATABASE_1_OWNER, response.getOwner().getId()); + assertFalse(response.getIsInitialView()); + assertEquals(DATABASE_1_PUBLIC, response.getIsPublic()); + assertTrue(response.getQuery().length() >= 69); + assertNotNull(response.getQueryHash()); + assertEquals(4, response.getColumns().size()); + final ViewColumnDto column0 = response.getColumns().get(0); + assertNotNull(column0.getName()); + assertEquals("date", column0.getInternalName()); + assertEquals(DATABASE_1_ID, column0.getDatabaseId()); + final ViewColumnDto column1 = response.getColumns().get(1); + assertNotNull(column1.getName()); + assertEquals("location", column1.getInternalName()); + assertEquals(DATABASE_1_ID, column1.getDatabaseId()); + final ViewColumnDto column2 = response.getColumns().get(2); + assertNotNull(column2.getName()); + assertEquals("MinTemp", column2.getInternalName()); + assertEquals(DATABASE_1_ID, column2.getDatabaseId()); + final ViewColumnDto column3 = response.getColumns().get(3); + assertNotNull(column3.getName()); + assertEquals("Rainfall", column3.getInternalName()); + assertEquals(DATABASE_1_ID, column3.getDatabaseId()); + } + + @Test + public void getSchemas_succeeds() throws TableNotFoundException, SQLException, DatabaseMalformedException { + + /* test */ + final List<TableDto> response = databaseService.exploreTables(DATABASE_1_DTO); + assertEquals(4, response.size()); + final TableDto table0 = response.get(0); + Assertions.assertEquals("complex_foreign_keys", table0.getInternalName()); + Assertions.assertEquals("complex_foreign_keys", table0.getName()); + Assertions.assertEquals(DATABASE_1_ID, table0.getTdbid()); + assertTrue(table0.getIsVersioned()); + Assertions.assertEquals(DATABASE_1_PUBLIC, table0.getIsPublic()); + final List<ColumnDto> columns0 = table0.getColumns(); + assertNotNull(columns0); + Assertions.assertEquals(3, columns0.size()); + assertColumn(columns0.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); + assertColumn(columns0.get(1), null, null, DATABASE_1_ID, "weather_id", "weather_id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); + assertColumn(columns0.get(2), null, null, DATABASE_1_ID, "other_id", "other_id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); + final ConstraintsDto constraints0 = table0.getConstraints(); + assertNotNull(constraints0); + assertEquals(1, constraints0.getPrimaryKey().size()); + final PrimaryKeyDto pk0 = new LinkedList<>(constraints0.getPrimaryKey()).get(0); + assertNull(pk0.getId()); + assertNull(pk0.getColumn().getId()); + assertEquals("id", pk0.getColumn().getName()); + assertEquals("id", pk0.getColumn().getInternalName()); + assertEquals(1, constraints0.getForeignKeys().size()); + final ForeignKeyDto fk0 = constraints0.getForeignKeys().get(0); + assertNotNull(fk0.getName()); + assertNull(fk0.getTable().getId()); + assertEquals("complex_foreign_keys", fk0.getTable().getName()); + assertEquals("complex_foreign_keys", fk0.getTable().getInternalName()); + assertNull(fk0.getReferencedTable().getId()); + assertEquals("complex_primary_key", fk0.getReferencedTable().getName()); + assertEquals("complex_primary_key", fk0.getReferencedTable().getInternalName()); + assertEquals(2, fk0.getReferences().size()); + final ForeignKeyReferenceDto fk0r0 = fk0.getReferences().get(0); + assertEquals("weather_id", fk0r0.getColumn().getName()); + assertEquals("weather_id", fk0r0.getColumn().getInternalName()); + assertNotNull(fk0r0.getColumn().getName()); + assertNotNull(fk0r0.getForeignKey()); + assertEquals("id", fk0r0.getReferencedColumn().getName()); + assertEquals("id", fk0r0.getReferencedColumn().getInternalName()); + final ForeignKeyReferenceDto fk0r1 = fk0.getReferences().get(1); + assertEquals("other_id", fk0r1.getColumn().getName()); + assertEquals("other_id", fk0r1.getColumn().getInternalName()); + assertNotNull(fk0r1.getColumn().getName()); + assertNotNull(fk0r1.getForeignKey()); + assertEquals("other_id", fk0r1.getReferencedColumn().getName()); + assertEquals("other_id", fk0r1.getReferencedColumn().getInternalName()); + assertEquals(0, constraints0.getChecks().size()); + assertEquals(0, constraints0.getUniques().size()); + /* table 1 */ + final TableDto table1 = response.get(1); + Assertions.assertEquals("complex_primary_key", table1.getInternalName()); + Assertions.assertEquals("complex_primary_key", table1.getName()); + Assertions.assertEquals(DATABASE_1_ID, table1.getTdbid()); + assertTrue(table1.getIsVersioned()); + Assertions.assertEquals(DATABASE_1_PUBLIC, table1.getIsPublic()); + final List<ColumnDto> columns1 = table1.getColumns(); + assertNotNull(columns1); + Assertions.assertEquals(2, columns1.size()); + assertColumn(columns1.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); + assertColumn(columns1.get(1), null, null, DATABASE_1_ID, "other_id", "other_id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); + final ConstraintsDto constraints1 = table1.getConstraints(); + assertNotNull(constraints1); + assertEquals(2, constraints1.getPrimaryKey().size()); + final PrimaryKeyDto pk10 = new LinkedList<>(constraints1.getPrimaryKey()).get(0); + assertNull(pk10.getId()); + assertNull(pk10.getColumn().getId()); + assertEquals("id", pk10.getColumn().getName()); + assertEquals("id", pk10.getColumn().getInternalName()); + final PrimaryKeyDto pk11 = new LinkedList<>(constraints1.getPrimaryKey()).get(1); + assertNull(pk11.getId()); + assertNull(pk11.getColumn().getId()); + assertEquals("other_id", pk11.getColumn().getName()); + assertEquals("other_id", pk11.getColumn().getInternalName()); + assertEquals(0, constraints1.getForeignKeys().size()); + assertEquals(0, constraints1.getChecks().size()); + assertEquals(0, constraints1.getUniques().size()); + /* table 2 */ + final TableDto table2 = response.get(2); + Assertions.assertEquals("exotic_boolean", table2.getInternalName()); + Assertions.assertEquals("exotic_boolean", table2.getName()); + Assertions.assertEquals(DATABASE_1_ID, table2.getTdbid()); + assertTrue(table2.getIsVersioned()); + Assertions.assertEquals(DATABASE_1_PUBLIC, table2.getIsPublic()); + final List<ColumnDto> columns2 = table2.getColumns(); + assertNotNull(columns2); + Assertions.assertEquals(3, columns2.size()); + assertColumn(columns2.get(0), null, null, DATABASE_1_ID, "bool_default", "bool_default", ColumnTypeDto.BOOL, null, 0L, false, null); + assertColumn(columns2.get(1), null, null, DATABASE_1_ID, "bool_tinyint", "bool_tinyint", ColumnTypeDto.BOOL, null, 0L, false, null); + assertColumn(columns2.get(2), null, null, DATABASE_1_ID, "bool_tinyint_unsigned", "bool_tinyint_unsigned", ColumnTypeDto.BOOL, null, 0L, false, null); + final ConstraintsDto constraints2 = table2.getConstraints(); + assertNotNull(constraints2); + final Set<PrimaryKeyDto> primaryKey2 = constraints2.getPrimaryKey(); + Assertions.assertEquals(1, primaryKey2.size()); + final Set<String> checks2 = constraints2.getChecks(); + Assertions.assertEquals(0, checks2.size()); + final List<UniqueDto> uniques2 = constraints2.getUniques(); + Assertions.assertEquals(0, uniques2.size()); + /* table 3 */ + final TableDto table3 = response.get(3); + Assertions.assertEquals("not_in_metadata_db", table3.getInternalName()); + Assertions.assertEquals("not_in_metadata_db", table3.getName()); + Assertions.assertEquals(DATABASE_1_ID, table3.getTdbid()); + assertTrue(table3.getIsVersioned()); + Assertions.assertEquals(DATABASE_1_PUBLIC, table3.getIsPublic()); + final List<ColumnDto> columns3 = table3.getColumns(); + assertNotNull(columns3); + Assertions.assertEquals(5, columns3.size()); + assertColumn(columns3.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); + assertColumn(columns3.get(1), null, null, DATABASE_1_ID, "given_name", "given_name", ColumnTypeDto.VARCHAR, 255L, null, false, null); + assertColumn(columns3.get(2), null, null, DATABASE_1_ID, "middle_name", "middle_name", ColumnTypeDto.VARCHAR, 255L, null, true, null); + assertColumn(columns3.get(3), null, null, DATABASE_1_ID, "family_name", "family_name", ColumnTypeDto.VARCHAR, 255L, null, false, null); + assertColumn(columns3.get(4), null, null, DATABASE_1_ID, "age", "age", ColumnTypeDto.INT, 10L, 0L, false, null); + final ConstraintsDto constraints3 = table3.getConstraints(); + assertNotNull(constraints3); + final Set<PrimaryKeyDto> primaryKey3 = constraints3.getPrimaryKey(); + Assertions.assertEquals(1, primaryKey3.size()); + final Set<String> checks3 = constraints3.getChecks(); + Assertions.assertEquals(1, checks3.size()); + Assertions.assertEquals(Set.of("`age` > 0 and `age` < 120"), checks3); + final List<UniqueDto> uniques3 = constraints3.getUniques(); + Assertions.assertEquals(1, uniques3.size()); + Assertions.assertEquals(2, uniques3.get(0).getColumns().size()); + Assertions.assertEquals("not_in_metadata_db", uniques3.get(0).getTable().getInternalName()); + Assertions.assertEquals("given_name", uniques3.get(0).getColumns().get(0).getInternalName()); + Assertions.assertEquals("family_name", uniques3.get(0).getColumns().get(1).getInternalName()); + } + + @Test + public void createTable_succeeds() throws TableNotFoundException, TableMalformedException, SQLException, + TableExistsException { + + /* test */ + final TableDto response = databaseService.createTable(DATABASE_1_DTO, TABLE_4_CREATE_INTERNAL_DTO); + assertEquals(TABLE_4_NAME, response.getName()); + assertEquals(TABLE_4_INTERNALNAME, response.getInternalName()); + final List<ColumnDto> columns = response.getColumns(); + assertEquals(TABLE_4_COLUMNS.size(), columns.size()); + assertColumn(columns.get(0), null, null, DATABASE_1_ID, "timestamp", "timestamp", ColumnTypeDto.TIMESTAMP, null, null, false, null); + assertColumn(columns.get(1), null, null, DATABASE_1_ID, "value", "value", ColumnTypeDto.DECIMAL, 10L, 10L, true, null); + final ConstraintsDto constraints = response.getConstraints(); + assertNotNull(constraints); + final Set<PrimaryKeyDto> primaryKey = constraints.getPrimaryKey(); + Assertions.assertEquals(1, primaryKey.size()); + final Set<String> checks = constraints.getChecks(); + Assertions.assertEquals(0, checks.size()); + } + + @Test + public void createTable_malformed_fails() { + final at.tuwien.api.database.table.internal.TableCreateDto request = TableCreateDto.builder() + .name("missing_foreign_key") + .columns(List.of()) + .constraints(ConstraintsCreateDto.builder() + .foreignKeys(List.of(ForeignKeyCreateDto.builder() + .columns(List.of("i_do_not_exist")) + .referencedTable("neither_do_i") + .referencedColumns(List.of("behold")) + .build())) + .build()) + .build(); + + /* test */ + assertThrows(TableMalformedException.class, () -> { + databaseService.createTable(DATABASE_1_DTO, request); + }); + } + + @Test + public void createTable_compositePrimaryKey_fails() throws TableNotFoundException, TableMalformedException, SQLException, + TableExistsException { + final at.tuwien.api.database.table.internal.TableCreateDto request = TableCreateDto.builder() + .name("composite_primary_key") + .columns(List.of(ColumnCreateDto.builder() + .name("name") + .type(ColumnTypeDto.VARCHAR) + .size(255L) + .nullAllowed(false) + .build(), + ColumnCreateDto.builder() + .name("lat") + .type(ColumnTypeDto.DECIMAL) + .size(10L) + .d(10L) + .nullAllowed(false) + .build(), + ColumnCreateDto.builder() + .name("lng") + .type(ColumnTypeDto.DECIMAL) + .size(10L) + .d(10L) + .nullAllowed(false) + .build())) + .constraints(ConstraintsCreateDto.builder() + .primaryKey(Set.of("lat", "lng")) + .foreignKeys(List.of()) + .checks(Set.of()) + .uniques(List.of()) + .build()) + .build(); + + /* test */ + final TableDto response = databaseService.createTable(DATABASE_1_DTO, request); + assertEquals("composite_primary_key", response.getName()); + assertEquals("composite_primary_key", response.getInternalName()); + final List<ColumnDto> columns = response.getColumns(); + assertEquals(3, columns.size()); + assertColumn(columns.get(0), null, null, DATABASE_1_ID, "name", "name", ColumnTypeDto.VARCHAR, 255L, null, false, null); + assertColumn(columns.get(1), null, null, DATABASE_1_ID, "lat", "lat", ColumnTypeDto.DECIMAL, 10L, 10L, false, null); + assertColumn(columns.get(2), null, null, DATABASE_1_ID, "lng", "lng", ColumnTypeDto.DECIMAL, 10L, 10L, false, null); + final ConstraintsDto constraints = response.getConstraints(); + assertNotNull(constraints); + final Set<String> checks = constraints.getChecks(); + assertNotNull(checks); + assertEquals(0, checks.size()); + final List<PrimaryKeyDto> primaryKeys = new LinkedList<>(constraints.getPrimaryKey()); + assertNotNull(primaryKeys); + assertEquals(2, primaryKeys.size()); + assertEquals("lat", primaryKeys.get(0).getColumn().getInternalName()); + assertEquals("lng", primaryKeys.get(1).getColumn().getInternalName()); + final List<ForeignKeyDto> foreignKeys = constraints.getForeignKeys(); + assertNotNull(foreignKeys); + assertEquals(0, foreignKeys.size()); + final List<UniqueDto> uniques = constraints.getUniques(); + assertNotNull(uniques); + assertEquals(0, uniques.size()); + } + + @Test + public void createTable_needSequence_succeeds() throws TableNotFoundException, TableMalformedException, SQLException, + TableExistsException { + + /* mock */ + MariaDbConfig.dropTable(DATABASE_1_DTO, TABLE_1_INTERNAL_NAME); + + /* test */ + final TableDto response = databaseService.createTable(DATABASE_1_DTO, TABLE_1_CREATE_INTERNAL_DTO); + assertEquals(TABLE_1_NAME, response.getName()); + assertEquals(TABLE_1_INTERNAL_NAME, response.getInternalName()); + assertEquals(TABLE_1_COLUMNS.size(), response.getColumns().size()); + } + + protected static void assertViewColumn(ViewColumnDto column, ViewColumnDto other) { + assertNotNull(column); + assertNotNull(other); + assertEquals(column.getId(), other.getId()); + assertEquals(column.getDatabaseId(), other.getDatabaseId()); + assertEquals(column.getName(), other.getName()); + assertEquals(column.getInternalName(), other.getInternalName()); + assertEquals(column.getColumnType(), other.getColumnType()); + assertEquals(column.getSize(), other.getSize()); + assertEquals(column.getD(), other.getD()); + assertEquals(column.getIsNullAllowed(), other.getIsNullAllowed()); + assertEquals(column.getDescription(), other.getDescription()); + } + + protected static void assertColumn(ColumnDto column, Long id, Long tableId, Long databaseId, String name, + String internalName, ColumnTypeDto type, Long size, Long d, Boolean nullAllowed, + String description) { + log.trace("assert column: {}", internalName); + assertNotNull(column); + assertEquals(id, column.getId()); + assertEquals(tableId, column.getTableId()); + assertEquals(databaseId, column.getDatabaseId()); + assertEquals(name, column.getName()); + assertEquals(internalName, column.getInternalName()); + assertEquals(type, column.getColumnType()); + assertEquals(size, column.getSize()); + assertEquals(d, column.getD()); + assertEquals(nullAllowed, column.getIsNullAllowed()); + assertEquals(description, column.getDescription()); + } } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java index bd57eafacc..a2ff1716e5 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java @@ -52,8 +52,8 @@ public class QueueServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); } @Test @@ -69,12 +69,12 @@ public class QueueServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - queueService.insert(TABLE_1_PRIVILEGED_DTO, request); + queueService.insert(TABLE_1_DTO, request); } @Test @@ -87,10 +87,10 @@ public class QueueServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - queueService.insert(TABLE_1_PRIVILEGED_DTO, request); + queueService.insert(TABLE_1_DTO, request); } } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java deleted file mode 100644 index 2a0ad624db..0000000000 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SchemaServiceIntegrationTest.java +++ /dev/null @@ -1,416 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.api.database.ViewColumnDto; -import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.table.TableBriefDto; -import at.tuwien.api.database.table.TableDto; -import at.tuwien.api.database.table.columns.ColumnDto; -import at.tuwien.api.database.table.columns.ColumnTypeDto; -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.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.config.MariaDbConfig; -import at.tuwien.config.MariaDbContainerConfig; -import at.tuwien.exception.TableNotFoundException; -import at.tuwien.exception.ViewNotFoundException; -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.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.testcontainers.containers.MariaDBContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -import java.sql.SQLException; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.*; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -@Testcontainers -public class SchemaServiceIntegrationTest extends AbstractUnitTest { - - @Autowired - private SchemaService schemaService; - - @Container - private static MariaDBContainer<?> mariaDBContainer = MariaDbContainerConfig.getContainer(); - - @BeforeEach - public void beforeEach() throws SQLException { - genesis(); - /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_DTO); - } - - @Test - public void inspectTable_sameNameDifferentDb_succeeds() throws TableNotFoundException, SQLException { - - /* mock */ - MariaDbConfig.execute(DATABASE_2_PRIVILEGED_DTO, "CREATE TABLE not_in_metadata_db (wrong_id BIGINT NOT NULL PRIMARY KEY, given_name VARCHAR(255) NOT NULL, middle_name VARCHAR(255), family_name VARCHAR(255) NOT NULL, age INT NOT NULL) WITH SYSTEM VERSIONING;"); - - /* test */ - final TableDto response = schemaService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "not_in_metadata_db"); - assertEquals("not_in_metadata_db", response.getInternalName()); - assertEquals("not_in_metadata_db", response.getName()); - assertEquals(DATABASE_1_ID, response.getTdbid()); - assertTrue(response.getIsVersioned()); - assertEquals(DATABASE_1_PUBLIC, response.getIsPublic()); - final List<ColumnDto> columns = response.getColumns(); - assertNotNull(columns); - assertEquals(5, columns.size()); - assertColumn(columns.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); - assertColumn(columns.get(1), null, null, DATABASE_1_ID, "given_name", "given_name", ColumnTypeDto.VARCHAR, 255L, null, false, null); - assertColumn(columns.get(2), null, null, DATABASE_1_ID, "middle_name", "middle_name", ColumnTypeDto.VARCHAR, 255L, null, true, null); - assertColumn(columns.get(3), null, null, DATABASE_1_ID, "family_name", "family_name", ColumnTypeDto.VARCHAR, 255L, null, false, null); - assertColumn(columns.get(4), null, null, DATABASE_1_ID, "age", "age", ColumnTypeDto.INT, 10L, 0L, false, null); - final ConstraintsDto constraints = response.getConstraints(); - assertNotNull(constraints); - final Set<PrimaryKeyDto> primaryKey = constraints.getPrimaryKey(); - assertEquals(1, primaryKey.size()); - final Set<String> checks = constraints.getChecks(); - assertEquals(1, checks.size()); - assertEquals(Set.of("`age` > 0 and `age` < 120"), checks); - final List<UniqueDto> uniques = constraints.getUniques(); - assertEquals(1, uniques.size()); - assertEquals(2, uniques.get(0).getColumns().size()); - assertEquals("not_in_metadata_db", uniques.get(0).getTable().getName()); - assertEquals("not_in_metadata_db", uniques.get(0).getTable().getInternalName()); - assertEquals("given_name", uniques.get(0).getColumns().get(0).getInternalName()); - assertEquals("family_name", uniques.get(0).getColumns().get(1).getInternalName()); - final List<ForeignKeyDto> foreignKeys = constraints.getForeignKeys(); - assertEquals(0, foreignKeys.size()); - } - - @Test - public void inspectTableEnum_succeeds() throws TableNotFoundException, SQLException { - - /* test */ - final TableDto response = schemaService.inspectTable(DATABASE_2_PRIVILEGED_DTO, "experiments"); - assertEquals("experiments", response.getInternalName()); - assertEquals("experiments", response.getName()); - assertEquals(DATABASE_2_ID, response.getTdbid()); - assertTrue(response.getIsVersioned()); - assertEquals(DATABASE_2_PUBLIC, response.getIsPublic()); - assertNotNull(response.getOwner()); - assertEquals(DATABASE_2_OWNER, response.getOwner().getId()); - assertEquals(USER_2_NAME, response.getOwner().getName()); - assertEquals(USER_2_USERNAME, response.getOwner().getUsername()); - assertEquals(USER_2_FIRSTNAME, response.getOwner().getFirstname()); - assertEquals(USER_2_LASTNAME, response.getOwner().getLastname()); - assertEquals(USER_2_QUALIFIED_NAME, response.getOwner().getQualifiedName()); - final List<IdentifierDto> identifiers = response.getIdentifiers(); - assertNotNull(identifiers); - assertEquals(0, identifiers.size()); - final List<ColumnDto> columns = response.getColumns(); - assertNotNull(columns); - assertEquals(3, columns.size()); - assertColumn(columns.get(0), null, null, DATABASE_2_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); - assertColumn(columns.get(1), null, null, DATABASE_2_ID, "mode", "mode", ColumnTypeDto.ENUM, 3L, null, false, null); - assertEquals(2, columns.get(1).getEnums().size()); - assertEquals(List.of("ABC", "DEF"), columns.get(1).getEnums()); - assertColumn(columns.get(2), null, null, DATABASE_2_ID, "seq", "seq", ColumnTypeDto.SET, 5L, null, true, null); - assertEquals(3, columns.get(2).getSets().size()); - assertEquals(List.of("1", "2", "3"), columns.get(2).getSets()); - /* ignore rest (constraints) */ - } - - @Test - public void inspectTableFullConstraints_succeeds() throws TableNotFoundException, SQLException { - - /* test */ - final TableDto response = schemaService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "weather_aus"); - assertEquals("weather_aus", response.getInternalName()); - assertEquals("weather_aus", response.getName()); - assertEquals(DATABASE_1_ID, response.getTdbid()); - assertTrue(response.getIsVersioned()); - assertEquals(DATABASE_1_PUBLIC, response.getIsPublic()); - assertNotNull(response.getOwner()); - assertEquals(DATABASE_1_OWNER, response.getOwner().getId()); - assertEquals(USER_1_NAME, response.getOwner().getName()); - assertEquals(USER_1_USERNAME, response.getOwner().getUsername()); - assertEquals(USER_1_FIRSTNAME, response.getOwner().getFirstname()); - assertEquals(USER_1_LASTNAME, response.getOwner().getLastname()); - assertEquals(USER_1_QUALIFIED_NAME, response.getOwner().getQualifiedName()); - final List<IdentifierDto> identifiers = response.getIdentifiers(); - assertNotNull(identifiers); - assertEquals(0, identifiers.size()); - final List<ColumnDto> columns = response.getColumns(); - assertNotNull(columns); - assertEquals(5, columns.size()); - assertColumn(columns.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 20L, 0L, false, null); - assertColumn(columns.get(1), null, null, DATABASE_1_ID, "date", "date", ColumnTypeDto.DATE, null, null, false, null); - assertColumn(columns.get(2), null, null, DATABASE_1_ID, "location", "location", ColumnTypeDto.VARCHAR, 255L, null, true, "Closest city"); - assertColumn(columns.get(3), null, null, DATABASE_1_ID, "mintemp", "mintemp", ColumnTypeDto.DOUBLE, 22L, null, true, null); - assertColumn(columns.get(4), null, null, DATABASE_1_ID, "rainfall", "rainfall", ColumnTypeDto.DOUBLE, 22L, null, true, null); - final ConstraintsDto constraints = response.getConstraints(); - final List<PrimaryKeyDto> primaryKey = new LinkedList<>(constraints.getPrimaryKey()); - assertEquals(1, primaryKey.size()); - final PrimaryKeyDto pk0 = primaryKey.get(0); - assertNull(pk0.getId()); - assertNotNull(pk0.getTable()); - assertNull(pk0.getTable().getId()); - assertEquals("weather_aus", pk0.getTable().getName()); - assertEquals("weather_aus", pk0.getTable().getInternalName()); - assertEquals("Weather in Australia", pk0.getTable().getDescription()); - assertNotNull(pk0.getColumn()); - assertNull(pk0.getColumn().getId()); - assertNull(pk0.getColumn().getTableId()); - assertEquals(DATABASE_1_ID, pk0.getColumn().getDatabaseId()); - assertNull(pk0.getColumn().getAlias()); - assertEquals("id", pk0.getColumn().getName()); - assertEquals("id", pk0.getColumn().getInternalName()); - assertEquals(ColumnTypeDto.BIGINT, pk0.getColumn().getColumnType()); - final List<UniqueDto> uniques = constraints.getUniques(); - assertEquals(1, uniques.size()); - final UniqueDto unique0 = uniques.get(0); - assertNotNull(unique0.getTable()); - assertEquals("some_constraint", unique0.getName()); - assertNull(unique0.getTable().getId()); - assertEquals(TABLE_1_INTERNAL_NAME, unique0.getTable().getName()); - assertEquals(TABLE_1_INTERNAL_NAME, unique0.getTable().getInternalName()); - assertEquals(TABLE_1_DESCRIPTION, unique0.getTable().getDescription()); - assertTrue(unique0.getTable().getIsVersioned()); - assertNotNull(unique0.getColumns()); - assertEquals(1, unique0.getColumns().size()); - assertNull(unique0.getColumns().get(0).getId()); - assertNull(unique0.getColumns().get(0).getTableId()); - assertEquals("date", unique0.getColumns().get(0).getName()); - assertEquals("date", unique0.getColumns().get(0).getInternalName()); - final List<String> checks = new LinkedList<>(constraints.getChecks()); - assertEquals("`mintemp` > 0", checks.get(0)); - final List<ForeignKeyDto> foreignKeys = constraints.getForeignKeys(); - assertEquals(1, foreignKeys.size()); - final ForeignKeyDto fk0 = foreignKeys.get(0); - assertNotNull(fk0.getName()); - assertNotNull(fk0.getReferences()); - final ForeignKeyReferenceDto fk0ref0 = fk0.getReferences().get(0); - assertNull(fk0ref0.getId()); - assertNotNull(fk0ref0.getColumn()); - assertNotNull(fk0ref0.getReferencedColumn()); - assertNotNull(fk0ref0.getForeignKey()); - assertEquals(DATABASE_1_ID, fk0ref0.getColumn().getDatabaseId()); - assertNull(fk0ref0.getColumn().getId()); - assertNull(fk0ref0.getColumn().getTableId()); - assertEquals("location", fk0ref0.getColumn().getName()); - assertEquals("location", fk0ref0.getColumn().getInternalName()); - assertEquals(DATABASE_1_ID, fk0ref0.getReferencedColumn().getDatabaseId()); - assertNull(fk0ref0.getReferencedColumn().getId()); - assertNull(fk0ref0.getReferencedColumn().getTableId()); - assertEquals("location", fk0ref0.getReferencedColumn().getName()); - assertEquals("location", fk0ref0.getReferencedColumn().getInternalName()); - assertNotNull(fk0.getOnUpdate()); - assertEquals(ReferenceTypeDto.RESTRICT, fk0.getOnUpdate()); - assertNotNull(fk0.getOnDelete()); - assertEquals(ReferenceTypeDto.SET_NULL, fk0.getOnDelete()); - final TableBriefDto fk0table = fk0.getTable(); - assertNull(fk0table.getId()); - assertEquals(DATABASE_1_ID, fk0table.getDatabaseId()); - assertEquals(TABLE_1_INTERNAL_NAME, fk0table.getName()); - assertEquals(TABLE_1_INTERNAL_NAME, fk0table.getInternalName()); - assertNotNull(fk0.getOnDelete()); - assertNotNull(fk0.getOnUpdate()); - assertNotNull(fk0.getReferencedTable()); - assertEquals(TABLE_2_INTERNALNAME, fk0.getReferencedTable().getName()); - assertEquals(TABLE_2_INTERNALNAME, fk0.getReferencedTable().getInternalName()); - } - - @Test - public void inspectTable_multipleForeignKeyReferences_succeeds() throws TableNotFoundException, SQLException { - - /* test */ - final TableDto response = schemaService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "complex_foreign_keys"); - final ConstraintsDto constraints = response.getConstraints(); - final List<ForeignKeyDto> foreignKeys = constraints.getForeignKeys(); - assertEquals(1, foreignKeys.size()); - final ForeignKeyDto fk0 = foreignKeys.get(0); - assertNotNull(fk0.getName()); - assertNotNull(fk0.getReferences()); - final ForeignKeyReferenceDto fk0ref0 = fk0.getReferences().get(0); - assertNull(fk0ref0.getId()); - assertNotNull(fk0ref0.getColumn()); - assertNotNull(fk0ref0.getReferencedColumn()); - assertNotNull(fk0ref0.getForeignKey()); - assertEquals(DATABASE_1_ID, fk0ref0.getColumn().getDatabaseId()); - assertNull(fk0ref0.getColumn().getId()); - assertNull(fk0ref0.getColumn().getTableId()); - assertEquals("weather_id", fk0ref0.getColumn().getName()); - assertEquals("weather_id", fk0ref0.getColumn().getInternalName()); - assertEquals(DATABASE_1_ID, fk0ref0.getReferencedColumn().getDatabaseId()); - assertNull(fk0ref0.getReferencedColumn().getId()); - assertNull(fk0ref0.getReferencedColumn().getTableId()); - assertEquals("id", fk0ref0.getReferencedColumn().getName()); - assertEquals("id", fk0ref0.getReferencedColumn().getInternalName()); - final ForeignKeyReferenceDto fk0ref1 = fk0.getReferences().get(1); - assertNull(fk0ref1.getId()); - assertNotNull(fk0ref1.getColumn()); - assertNotNull(fk0ref1.getReferencedColumn()); - assertNotNull(fk0ref1.getForeignKey()); - assertEquals(DATABASE_1_ID, fk0ref1.getColumn().getDatabaseId()); - assertNull(fk0ref1.getColumn().getId()); - assertNull(fk0ref1.getColumn().getTableId()); - assertEquals("other_id", fk0ref1.getColumn().getName()); - assertEquals("other_id", fk0ref1.getColumn().getInternalName()); - assertEquals(DATABASE_1_ID, fk0ref1.getReferencedColumn().getDatabaseId()); - assertNull(fk0ref1.getReferencedColumn().getId()); - assertNull(fk0ref1.getReferencedColumn().getTableId()); - assertEquals("other_id", fk0ref1.getReferencedColumn().getName()); - assertEquals("other_id", fk0ref1.getReferencedColumn().getInternalName()); - final TableBriefDto fk0refT0 = fk0.getTable(); - assertNull(fk0refT0.getId()); - assertEquals(DATABASE_1_ID, fk0refT0.getDatabaseId()); - assertEquals("complex_foreign_keys", fk0refT0.getName()); - assertEquals("complex_foreign_keys", fk0refT0.getInternalName()); - assertNotNull(fk0.getReferencedTable()); - assertEquals("complex_primary_key", fk0.getReferencedTable().getName()); - assertEquals("complex_primary_key", fk0.getReferencedTable().getInternalName()); - assertNotNull(fk0.getOnDelete()); - assertNotNull(fk0.getOnUpdate()); - } - - @Test - public void inspectTable_multiplePrimaryKey_succeeds() throws TableNotFoundException, SQLException { - - /* test */ - final TableDto response = schemaService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "complex_primary_key"); - final ConstraintsDto constraints = response.getConstraints(); - final List<PrimaryKeyDto> primaryKey = new LinkedList<>(constraints.getPrimaryKey()); - assertEquals(2, primaryKey.size()); - final PrimaryKeyDto pk0 = primaryKey.get(0); - assertNull(pk0.getId()); - assertNotNull(pk0.getTable()); - assertNull(pk0.getTable().getId()); - assertEquals("complex_primary_key", pk0.getTable().getName()); - assertEquals("complex_primary_key", pk0.getTable().getInternalName()); - assertNotNull(pk0.getColumn()); - assertNull(pk0.getColumn().getId()); - assertNull(pk0.getColumn().getTableId()); - assertEquals(DATABASE_1_ID, pk0.getColumn().getDatabaseId()); - assertNull(pk0.getColumn().getAlias()); - assertEquals("id", pk0.getColumn().getName()); - assertEquals("id", pk0.getColumn().getInternalName()); - assertEquals(ColumnTypeDto.BIGINT, pk0.getColumn().getColumnType()); - final PrimaryKeyDto pk1 = primaryKey.get(1); - assertNull(pk1.getId()); - assertNotNull(pk1.getTable()); - assertNull(pk1.getTable().getId()); - assertEquals("complex_primary_key", pk1.getTable().getName()); - assertEquals("complex_primary_key", pk1.getTable().getInternalName()); - assertNotNull(pk1.getColumn()); - assertNull(pk1.getColumn().getId()); - assertNull(pk1.getColumn().getTableId()); - assertEquals(DATABASE_1_ID, pk1.getColumn().getDatabaseId()); - assertNull(pk1.getColumn().getAlias()); - assertEquals("other_id", pk1.getColumn().getName()); - assertEquals("other_id", pk1.getColumn().getInternalName()); - assertEquals(ColumnTypeDto.BIGINT, pk1.getColumn().getColumnType()); - } - - @Test - public void inspectTable_exoticBoolean_succeeds() throws TableNotFoundException, SQLException { - - /* test */ - final TableDto response = schemaService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "exotic_boolean"); - final ConstraintsDto constraints = response.getConstraints(); - final List<PrimaryKeyDto> primaryKey = new LinkedList<>(constraints.getPrimaryKey()); - assertEquals(1, primaryKey.size()); - final PrimaryKeyDto pk0 = primaryKey.get(0); - assertNull(pk0.getId()); - assertNotNull(pk0.getTable()); - assertNull(pk0.getTable().getId()); - assertEquals("exotic_boolean", pk0.getTable().getName()); - assertEquals("exotic_boolean", pk0.getTable().getInternalName()); - assertNotNull(pk0.getColumn()); - assertNull(pk0.getColumn().getId()); - assertNull(pk0.getColumn().getTableId()); - assertEquals(DATABASE_1_ID, pk0.getColumn().getDatabaseId()); - assertNull(pk0.getColumn().getAlias()); - assertEquals("bool_default", pk0.getColumn().getName()); - assertEquals("bool_default", pk0.getColumn().getInternalName()); - assertEquals(ColumnTypeDto.BOOL, pk0.getColumn().getColumnType()); - final List<ColumnDto> columns = response.getColumns(); - assertEquals(3, columns.size()); - assertColumn(columns.get(0), null, null, DATABASE_1_ID, "bool_default", "bool_default", ColumnTypeDto.BOOL, null, 0L, false, null); - assertColumn(columns.get(1), null, null, DATABASE_1_ID, "bool_tinyint", "bool_tinyint", ColumnTypeDto.BOOL, null, 0L, false, null); - assertColumn(columns.get(2), null, null, DATABASE_1_ID, "bool_tinyint_unsigned", "bool_tinyint_unsigned", ColumnTypeDto.BOOL, null, 0L, false, null); - } - - @Test - public void inspectView_succeeds() throws SQLException, ViewNotFoundException { - - /* test */ - final ViewDto response = schemaService.inspectView(DATABASE_1_PRIVILEGED_DTO, "not_in_metadata_db2"); - assertEquals("not_in_metadata_db2", response.getInternalName()); - assertEquals("not_in_metadata_db2", response.getName()); - assertEquals(DATABASE_1_ID, response.getVdbid()); - assertEquals(DATABASE_1_OWNER, response.getOwner().getId()); - assertFalse(response.getIsInitialView()); - assertEquals(DATABASE_1_PUBLIC, response.getIsPublic()); - assertTrue(response.getQuery().length() >= 69); - assertNotNull(response.getQueryHash()); - assertEquals(4, response.getColumns().size()); - final ViewColumnDto column0 = response.getColumns().get(0); - assertNotNull(column0.getName()); - assertEquals("date", column0.getInternalName()); - assertEquals(DATABASE_1_ID, column0.getDatabaseId()); - final ViewColumnDto column1 = response.getColumns().get(1); - assertNotNull(column1.getName()); - assertEquals("location", column1.getInternalName()); - assertEquals(DATABASE_1_ID, column1.getDatabaseId()); - final ViewColumnDto column2 = response.getColumns().get(2); - assertNotNull(column2.getName()); - assertEquals("MinTemp", column2.getInternalName()); - assertEquals(DATABASE_1_ID, column2.getDatabaseId()); - final ViewColumnDto column3 = response.getColumns().get(3); - assertNotNull(column3.getName()); - assertEquals("Rainfall", column3.getInternalName()); - assertEquals(DATABASE_1_ID, column3.getDatabaseId()); - } - - protected static void assertViewColumn(ViewColumnDto column, ViewColumnDto other) { - assertNotNull(column); - assertNotNull(other); - assertEquals(column.getId(), other.getId()); - assertEquals(column.getDatabaseId(), other.getDatabaseId()); - assertEquals(column.getName(), other.getName()); - assertEquals(column.getInternalName(), other.getInternalName()); - assertEquals(column.getColumnType(), other.getColumnType()); - assertEquals(column.getSize(), other.getSize()); - assertEquals(column.getD(), other.getD()); - assertEquals(column.getIsNullAllowed(), other.getIsNullAllowed()); - assertEquals(column.getDescription(), other.getDescription()); - } - - protected static void assertColumn(ColumnDto column, Long id, Long tableId, Long databaseId, String name, - String internalName, ColumnTypeDto type, Long size, Long d, Boolean nullAllowed, - String description) { - log.trace("assert column: {}", internalName); - assertNotNull(column); - assertEquals(id, column.getId()); - assertEquals(tableId, column.getTableId()); - assertEquals(databaseId, column.getDatabaseId()); - assertEquals(name, column.getName()); - assertEquals(internalName, column.getInternalName()); - assertEquals(type, column.getColumnType()); - assertEquals(size, column.getSize()); - assertEquals(d, column.getD()); - assertEquals(nullAllowed, column.getIsNullAllowed()); - assertEquals(description, column.getDescription()); - } - -} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java index 0eab9c6ff3..886d492cd7 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java @@ -21,7 +21,6 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.sql.SQLException; import java.util.List; -import java.util.Map; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.when; @@ -45,8 +44,8 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); } @Test @@ -108,7 +107,7 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { /* test */ persist_generic(QUERY_2_ID, List.of(IDENTIFIER_5_BRIEF_DTO), true); - final QueryDto response = queryService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_2_ID); + final QueryDto response = queryService.findById(DATABASE_1_DTO, QUERY_2_ID); assertEquals(2L, response.getId()); assertTrue(response.getIsPersisted()); } @@ -124,30 +123,11 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { /* test */ persist_generic(QUERY_1_ID, List.of(IDENTIFIER_2_BRIEF_DTO), false); - final QueryDto response = queryService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID); + final QueryDto response = queryService.findById(DATABASE_1_DTO, QUERY_1_ID); assertEquals(1L, response.getId()); assertFalse(response.getIsPersisted()); } - @Test - public void createQueryStore_succeeds() throws SQLException, QueryStoreCreateException, InterruptedException { - - /* mock */ - MariaDbConfig.dropQueryStore(DATABASE_1_PRIVILEGED_DTO); - - /* test */ - createQueryStore_generic(DATABASE_1_INTERNALNAME); - } - - @Test - public void createQueryStore_fails() { - - /* test */ - assertThrows(QueryStoreCreateException.class, () -> { - createQueryStore_generic(DATABASE_1_INTERNALNAME); - }); - } - protected void findById_generic(Long queryId) throws RemoteUnavailableException, SQLException, UserNotFoundException, QueryNotFoundException, MetadataServiceException, DatabaseNotFoundException, InterruptedException { @@ -160,10 +140,10 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { .thenReturn(List.of(IDENTIFIER_2_BRIEF_DTO)); when(metadataServiceGateway.getUserById(USER_1_ID)) .thenReturn(USER_1_DTO); - MariaDbConfig.insertQueryStore(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, USER_1_ID); + MariaDbConfig.insertQueryStore(DATABASE_1_DTO, QUERY_1_DTO, USER_1_ID); /* test */ - final QueryDto response = queryService.findById(DATABASE_1_PRIVILEGED_DTO, queryId); + final QueryDto response = queryService.findById(DATABASE_1_DTO, queryId); assertEquals(QUERY_1_ID, response.getId()); } @@ -175,13 +155,13 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { Thread.sleep(1000) /* wait for test container some more */; /* mock */ - MariaDbConfig.insertQueryStore(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, USER_1_ID); - MariaDbConfig.insertQueryStore(DATABASE_1_PRIVILEGED_DTO, QUERY_2_DTO, USER_1_ID); + MariaDbConfig.insertQueryStore(DATABASE_1_DTO, QUERY_1_DTO, USER_1_ID); + MariaDbConfig.insertQueryStore(DATABASE_1_DTO, QUERY_2_DTO, USER_1_ID); when(metadataServiceGateway.getIdentifiers(DATABASE_1_ID, null)) .thenReturn(List.of(IDENTIFIER_2_BRIEF_DTO, IDENTIFIER_5_BRIEF_DTO)); /* test */ - return queryService.findAll(DATABASE_1_PRIVILEGED_DTO, filterPersisted); + return queryService.findAll(DATABASE_1_DTO, filterPersisted); } protected void persist_generic(Long queryId, List<IdentifierBriefDto> identifiers, Boolean persist) @@ -194,23 +174,11 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getIdentifiers(DATABASE_1_ID, queryId)) .thenReturn(identifiers); - MariaDbConfig.insertQueryStore(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, USER_1_ID); - MariaDbConfig.insertQueryStore(DATABASE_1_PRIVILEGED_DTO, QUERY_2_DTO, USER_1_ID); - - /* test */ - queryService.persist(DATABASE_1_PRIVILEGED_DTO, queryId, persist); - } - - protected void createQueryStore_generic(String databaseName) throws SQLException, QueryStoreCreateException, - InterruptedException { - - /* pre-condition */ - Thread.sleep(1000) /* wait for test container some more */; + MariaDbConfig.insertQueryStore(DATABASE_1_DTO, QUERY_1_DTO, USER_1_ID); + MariaDbConfig.insertQueryStore(DATABASE_1_DTO, QUERY_2_DTO, USER_1_ID); /* test */ - queryService.createQueryStore(CONTAINER_1_PRIVILEGED_DTO, databaseName); - final List<Map<String, Object>> response = MariaDbConfig.listQueryStore(DATABASE_1_PRIVILEGED_DTO); - assertEquals(0, response.size()); + queryService.persist(DATABASE_1_DTO, queryId, persist); } } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java index 383c44770f..f8687d8e0d 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java @@ -2,18 +2,7 @@ package at.tuwien.service; import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.table.*; -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.constraints.ConstraintsCreateDto; -import at.tuwien.api.database.table.constraints.ConstraintsDto; -import at.tuwien.api.database.table.constraints.foreign.ForeignKeyCreateDto; -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.database.table.internal.TableCreateDto; import at.tuwien.config.MariaDbConfig; import at.tuwien.config.MariaDbContainerConfig; import at.tuwien.config.S3Config; @@ -22,7 +11,10 @@ import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.test.AbstractUnitTest; import com.google.common.io.Files; import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -44,9 +36,11 @@ import java.io.IOException; import java.math.BigDecimal; import java.sql.SQLException; import java.time.Instant; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; -import static at.tuwien.service.SchemaServiceIntegrationTest.assertColumn; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.when; @@ -88,11 +82,11 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); - MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_3_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_3_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_2_INTERNALNAME); + MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_3_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_3_DTO); /* s3 */ if (s3Client.listBuckets().buckets().stream().noneMatch(b -> b.name().equals(s3Config.getS3Bucket()))) { s3Client.createBucket(CreateBucketRequest.builder() @@ -124,13 +118,13 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - tableService.updateTuple(TABLE_1_PRIVILEGED_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.updateTuple(TABLE_1_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("1", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); // <<< assertEquals("Vienna", result.get(0).get("location")); // <<< @@ -158,13 +152,13 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - tableService.updateTuple(TABLE_1_PRIVILEGED_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.updateTuple(TABLE_1_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("4", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); // <<< assertEquals("Vienna", result.get(0).get("location")); // <<< @@ -191,13 +185,13 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - tableService.updateTuple(TABLE_1_PRIVILEGED_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.updateTuple(TABLE_1_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("1", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); // <<< assertEquals("Vienna", result.get(0).get("location")); // <<< @@ -224,13 +218,13 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - tableService.updateTuple(TABLE_1_PRIVILEGED_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.updateTuple(TABLE_1_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("1", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); // <<< assertEquals("Vienna", result.get(0).get("location")); // <<< @@ -255,13 +249,13 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - tableService.createTuple(TABLE_1_PRIVILEGED_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.createTuple(TABLE_1_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("4", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); assertEquals("Vienna", result.get(0).get("location")); @@ -283,17 +277,17 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_PRIVILEGED_DTO); + .thenReturn(TABLE_8_DTO); s3Client.putObject(PutObjectRequest.builder() .key("s3key") .bucket(s3Config.getS3Bucket()) .build(), RequestBody.fromFile(new File("src/test/resources/csv/keyboard.csv"))); /* test */ - tableService.createTuple(TABLE_8_PRIVILEGED_DTO, request); - final List<Map<String, byte[]>> result = MariaDbConfig.selectQueryByteArr(DATABASE_3_PRIVILEGED_DTO, "SELECT raw FROM mfcc WHERE raw IS NOT NULL", Set.of("raw")); + tableService.createTuple(TABLE_8_DTO, request); + final List<Map<String, byte[]>> result = MariaDbConfig.selectQueryByteArr(DATABASE_3_DTO, "SELECT raw FROM mfcc WHERE raw IS NOT NULL", Set.of("raw")); assertNotNull(result.get(0).get("raw")); assertArrayEquals(Files.toByteArray(new File("src/test/resources/csv/keyboard.csv")), result.get(0).get("raw")); } @@ -315,13 +309,13 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - tableService.createTuple(TABLE_1_PRIVILEGED_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.createTuple(TABLE_1_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("4", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); assertEquals("Vienna", result.get(0).get("location")); @@ -341,13 +335,13 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - tableService.deleteTuple(TABLE_1_PRIVILEGED_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id FROM weather_aus WHERE id = 1", Set.of("id")); + tableService.deleteTuple(TABLE_1_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id FROM weather_aus WHERE id = 1", Set.of("id")); assertEquals(0, result.size()); } @@ -365,173 +359,22 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_PRIVILEGED_DTO); + .thenReturn(CONTAINER_1_DTO); when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_PRIVILEGED_DTO); + .thenReturn(TABLE_1_DTO); /* test */ - tableService.deleteTuple(TABLE_1_PRIVILEGED_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id FROM weather_aus WHERE id = 1", Set.of("id")); + tableService.deleteTuple(TABLE_1_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id FROM weather_aus WHERE id = 1", Set.of("id")); assertEquals(0, result.size()); } - @Test - public void getSchemas_succeeds() throws TableNotFoundException, SQLException, DatabaseMalformedException { - - /* test */ - final List<TableDto> response = tableService.getSchemas(DATABASE_1_PRIVILEGED_DTO); - assertEquals(4, response.size()); - final TableDto table0 = response.get(0); - Assertions.assertEquals("complex_foreign_keys", table0.getInternalName()); - Assertions.assertEquals("complex_foreign_keys", table0.getName()); - Assertions.assertEquals(DATABASE_1_ID, table0.getTdbid()); - assertTrue(table0.getIsVersioned()); - Assertions.assertEquals(DATABASE_1_PUBLIC, table0.getIsPublic()); - final List<ColumnDto> columns0 = table0.getColumns(); - assertNotNull(columns0); - Assertions.assertEquals(3, columns0.size()); - assertColumn(columns0.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); - assertColumn(columns0.get(1), null, null, DATABASE_1_ID, "weather_id", "weather_id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); - assertColumn(columns0.get(2), null, null, DATABASE_1_ID, "other_id", "other_id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); - final ConstraintsDto constraints0 = table0.getConstraints(); - assertNotNull(constraints0); - assertEquals(1, constraints0.getPrimaryKey().size()); - final PrimaryKeyDto pk0 = new LinkedList<>(constraints0.getPrimaryKey()).get(0); - assertNull(pk0.getId()); - assertNull(pk0.getColumn().getId()); - assertEquals("id", pk0.getColumn().getName()); - assertEquals("id", pk0.getColumn().getInternalName()); - assertEquals(1, constraints0.getForeignKeys().size()); - final ForeignKeyDto fk0 = constraints0.getForeignKeys().get(0); - assertNotNull(fk0.getName()); - assertNull(fk0.getTable().getId()); - assertEquals("complex_foreign_keys", fk0.getTable().getName()); - assertEquals("complex_foreign_keys", fk0.getTable().getInternalName()); - assertNull(fk0.getReferencedTable().getId()); - assertEquals("complex_primary_key", fk0.getReferencedTable().getName()); - assertEquals("complex_primary_key", fk0.getReferencedTable().getInternalName()); - assertEquals(2, fk0.getReferences().size()); - final ForeignKeyReferenceDto fk0r0 = fk0.getReferences().get(0); - assertEquals("weather_id", fk0r0.getColumn().getName()); - assertEquals("weather_id", fk0r0.getColumn().getInternalName()); - assertNotNull(fk0r0.getColumn().getName()); - assertNotNull(fk0r0.getForeignKey()); - assertEquals("id", fk0r0.getReferencedColumn().getName()); - assertEquals("id", fk0r0.getReferencedColumn().getInternalName()); - final ForeignKeyReferenceDto fk0r1 = fk0.getReferences().get(1); - assertEquals("other_id", fk0r1.getColumn().getName()); - assertEquals("other_id", fk0r1.getColumn().getInternalName()); - assertNotNull(fk0r1.getColumn().getName()); - assertNotNull(fk0r1.getForeignKey()); - assertEquals("other_id", fk0r1.getReferencedColumn().getName()); - assertEquals("other_id", fk0r1.getReferencedColumn().getInternalName()); - assertEquals(0, constraints0.getChecks().size()); - assertEquals(0, constraints0.getUniques().size()); - /* table 1 */ - final TableDto table1 = response.get(1); - Assertions.assertEquals("complex_primary_key", table1.getInternalName()); - Assertions.assertEquals("complex_primary_key", table1.getName()); - Assertions.assertEquals(DATABASE_1_ID, table1.getTdbid()); - assertTrue(table1.getIsVersioned()); - Assertions.assertEquals(DATABASE_1_PUBLIC, table1.getIsPublic()); - final List<ColumnDto> columns1 = table1.getColumns(); - assertNotNull(columns1); - Assertions.assertEquals(2, columns1.size()); - assertColumn(columns1.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); - assertColumn(columns1.get(1), null, null, DATABASE_1_ID, "other_id", "other_id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); - final ConstraintsDto constraints1 = table1.getConstraints(); - assertNotNull(constraints1); - assertEquals(2, constraints1.getPrimaryKey().size()); - final PrimaryKeyDto pk10 = new LinkedList<>(constraints1.getPrimaryKey()).get(0); - assertNull(pk10.getId()); - assertNull(pk10.getColumn().getId()); - assertEquals("id", pk10.getColumn().getName()); - assertEquals("id", pk10.getColumn().getInternalName()); - final PrimaryKeyDto pk11 = new LinkedList<>(constraints1.getPrimaryKey()).get(1); - assertNull(pk11.getId()); - assertNull(pk11.getColumn().getId()); - assertEquals("other_id", pk11.getColumn().getName()); - assertEquals("other_id", pk11.getColumn().getInternalName()); - assertEquals(0, constraints1.getForeignKeys().size()); - assertEquals(0, constraints1.getChecks().size()); - assertEquals(0, constraints1.getUniques().size()); - /* table 2 */ - final TableDto table2 = response.get(2); - Assertions.assertEquals("exotic_boolean", table2.getInternalName()); - Assertions.assertEquals("exotic_boolean", table2.getName()); - Assertions.assertEquals(DATABASE_1_ID, table2.getTdbid()); - assertTrue(table2.getIsVersioned()); - Assertions.assertEquals(DATABASE_1_PUBLIC, table2.getIsPublic()); - final List<ColumnDto> columns2 = table2.getColumns(); - assertNotNull(columns2); - Assertions.assertEquals(3, columns2.size()); - assertColumn(columns2.get(0), null, null, DATABASE_1_ID, "bool_default", "bool_default", ColumnTypeDto.BOOL, null, 0L, false, null); - assertColumn(columns2.get(1), null, null, DATABASE_1_ID, "bool_tinyint", "bool_tinyint", ColumnTypeDto.BOOL, null, 0L, false, null); - assertColumn(columns2.get(2), null, null, DATABASE_1_ID, "bool_tinyint_unsigned", "bool_tinyint_unsigned", ColumnTypeDto.BOOL, null, 0L, false, null); - final ConstraintsDto constraints2 = table2.getConstraints(); - assertNotNull(constraints2); - final Set<PrimaryKeyDto> primaryKey2 = constraints2.getPrimaryKey(); - Assertions.assertEquals(1, primaryKey2.size()); - final Set<String> checks2 = constraints2.getChecks(); - Assertions.assertEquals(0, checks2.size()); - final List<UniqueDto> uniques2 = constraints2.getUniques(); - Assertions.assertEquals(0, uniques2.size()); - /* table 3 */ - final TableDto table3 = response.get(3); - Assertions.assertEquals("not_in_metadata_db", table3.getInternalName()); - Assertions.assertEquals("not_in_metadata_db", table3.getName()); - Assertions.assertEquals(DATABASE_1_ID, table3.getTdbid()); - assertTrue(table3.getIsVersioned()); - Assertions.assertEquals(DATABASE_1_PUBLIC, table3.getIsPublic()); - final List<ColumnDto> columns3 = table3.getColumns(); - assertNotNull(columns3); - Assertions.assertEquals(5, columns3.size()); - assertColumn(columns3.get(0), null, null, DATABASE_1_ID, "id", "id", ColumnTypeDto.BIGINT, 19L, 0L, false, null); - assertColumn(columns3.get(1), null, null, DATABASE_1_ID, "given_name", "given_name", ColumnTypeDto.VARCHAR, 255L, null, false, null); - assertColumn(columns3.get(2), null, null, DATABASE_1_ID, "middle_name", "middle_name", ColumnTypeDto.VARCHAR, 255L, null, true, null); - assertColumn(columns3.get(3), null, null, DATABASE_1_ID, "family_name", "family_name", ColumnTypeDto.VARCHAR, 255L, null, false, null); - assertColumn(columns3.get(4), null, null, DATABASE_1_ID, "age", "age", ColumnTypeDto.INT, 10L, 0L, false, null); - final ConstraintsDto constraints3 = table3.getConstraints(); - assertNotNull(constraints3); - final Set<PrimaryKeyDto> primaryKey3 = constraints3.getPrimaryKey(); - Assertions.assertEquals(1, primaryKey3.size()); - final Set<String> checks3 = constraints3.getChecks(); - Assertions.assertEquals(1, checks3.size()); - Assertions.assertEquals(Set.of("`age` > 0 and `age` < 120"), checks3); - final List<UniqueDto> uniques3 = constraints3.getUniques(); - Assertions.assertEquals(1, uniques3.size()); - Assertions.assertEquals(2, uniques3.get(0).getColumns().size()); - Assertions.assertEquals("not_in_metadata_db", uniques3.get(0).getTable().getInternalName()); - Assertions.assertEquals("given_name", uniques3.get(0).getColumns().get(0).getInternalName()); - Assertions.assertEquals("family_name", uniques3.get(0).getColumns().get(1).getInternalName()); - } - - @Test - public void create_succeeds() throws TableNotFoundException, TableMalformedException, SQLException, - TableExistsException { - - /* test */ - final TableDto response = tableService.createTable(DATABASE_1_PRIVILEGED_DTO, TABLE_4_CREATE_INTERNAL_DTO); - assertEquals(TABLE_4_NAME, response.getName()); - assertEquals(TABLE_4_INTERNALNAME, response.getInternalName()); - final List<ColumnDto> columns = response.getColumns(); - assertEquals(TABLE_4_COLUMNS.size(), columns.size()); - assertColumn(columns.get(0), null, null, DATABASE_1_ID, "timestamp", "timestamp", ColumnTypeDto.TIMESTAMP, null, null, false, null); - assertColumn(columns.get(1), null, null, DATABASE_1_ID, "value", "value", ColumnTypeDto.DECIMAL, 10L, 10L, true, null); - final ConstraintsDto constraints = response.getConstraints(); - assertNotNull(constraints); - final Set<PrimaryKeyDto> primaryKey = constraints.getPrimaryKey(); - Assertions.assertEquals(1, primaryKey.size()); - final Set<String> checks = constraints.getChecks(); - Assertions.assertEquals(0, checks.size()); - } - @Test @Disabled("Not stable CI/CD") public void getStatistics_succeeds() throws TableMalformedException, SQLException, TableNotFoundException { /* test */ - final TableStatisticDto response = tableService.getStatistics(TABLE_2_PRIVILEGED_DTO); + final TableStatisticDto response = tableService.getStatistics(DATABASE_1_DTO, TABLE_2_DTO); assertEquals(TABLE_2_COLUMNS.size(), response.getColumns().size()); log.trace("response rows: {}", response.getRows()); assertEquals(3L, response.getRows()); @@ -556,116 +399,22 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { assertNotNull(column4.getStdDev()); } - @Test - public void create_malformed_fails() { - final at.tuwien.api.database.table.internal.TableCreateDto request = TableCreateDto.builder() - .name("missing_foreign_key") - .columns(List.of()) - .constraints(ConstraintsCreateDto.builder() - .foreignKeys(List.of(ForeignKeyCreateDto.builder() - .columns(List.of("i_do_not_exist")) - .referencedTable("neither_do_i") - .referencedColumns(List.of("behold")) - .build())) - .build()) - .build(); - - /* test */ - assertThrows(TableMalformedException.class, () -> { - tableService.createTable(DATABASE_1_PRIVILEGED_DTO, request); - }); - } - - @Test - public void create_compositePrimaryKey_fails() throws TableNotFoundException, TableMalformedException, SQLException, - TableExistsException { - final at.tuwien.api.database.table.internal.TableCreateDto request = TableCreateDto.builder() - .name("composite_primary_key") - .columns(List.of(ColumnCreateDto.builder() - .name("name") - .type(ColumnTypeDto.VARCHAR) - .size(255L) - .nullAllowed(false) - .build(), - ColumnCreateDto.builder() - .name("lat") - .type(ColumnTypeDto.DECIMAL) - .size(10L) - .d(10L) - .nullAllowed(false) - .build(), - ColumnCreateDto.builder() - .name("lng") - .type(ColumnTypeDto.DECIMAL) - .size(10L) - .d(10L) - .nullAllowed(false) - .build())) - .constraints(ConstraintsCreateDto.builder() - .primaryKey(Set.of("lat", "lng")) - .foreignKeys(List.of()) - .checks(Set.of()) - .uniques(List.of()) - .build()) - .build(); - - /* test */ - final TableDto response = tableService.createTable(DATABASE_1_PRIVILEGED_DTO, request); - assertEquals("composite_primary_key", response.getName()); - assertEquals("composite_primary_key", response.getInternalName()); - final List<ColumnDto> columns = response.getColumns(); - assertEquals(3, columns.size()); - assertColumn(columns.get(0), null, null, DATABASE_1_ID, "name", "name", ColumnTypeDto.VARCHAR, 255L, null, false, null); - assertColumn(columns.get(1), null, null, DATABASE_1_ID, "lat", "lat", ColumnTypeDto.DECIMAL, 10L, 10L, false, null); - assertColumn(columns.get(2), null, null, DATABASE_1_ID, "lng", "lng", ColumnTypeDto.DECIMAL, 10L, 10L, false, null); - final ConstraintsDto constraints = response.getConstraints(); - assertNotNull(constraints); - final Set<String> checks = constraints.getChecks(); - assertNotNull(checks); - assertEquals(0, checks.size()); - final List<PrimaryKeyDto> primaryKeys = new LinkedList<>(constraints.getPrimaryKey()); - assertNotNull(primaryKeys); - assertEquals(2, primaryKeys.size()); - assertEquals("lat", primaryKeys.get(0).getColumn().getInternalName()); - assertEquals("lng", primaryKeys.get(1).getColumn().getInternalName()); - final List<ForeignKeyDto> foreignKeys = constraints.getForeignKeys(); - assertNotNull(foreignKeys); - assertEquals(0, foreignKeys.size()); - final List<UniqueDto> uniques = constraints.getUniques(); - assertNotNull(uniques); - assertEquals(0, uniques.size()); - } - - @Test - public void create_needSequence_succeeds() throws TableNotFoundException, TableMalformedException, SQLException, - TableExistsException { - - /* mock */ - MariaDbConfig.dropTable(DATABASE_1_PRIVILEGED_DTO, TABLE_1_INTERNAL_NAME); - - /* test */ - final TableDto response = tableService.createTable(DATABASE_1_PRIVILEGED_DTO, TABLE_1_CREATE_INTERNAL_DTO); - assertEquals(TABLE_1_NAME, response.getName()); - assertEquals(TABLE_1_INTERNAL_NAME, response.getInternalName()); - assertEquals(TABLE_1_COLUMNS.size(), response.getColumns().size()); - } - @Test public void delete_succeeds() throws SQLException, QueryMalformedException { /* test */ - tableService.delete(TABLE_1_PRIVILEGED_DTO); + tableService.delete(TABLE_1_DTO); } @Test public void delete_notFound_fails() throws SQLException { /* mock */ - MariaDbConfig.createDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); + MariaDbConfig.createDatabase(CONTAINER_1_DTO, DATABASE_2_INTERNALNAME); /* test */ assertThrows(QueryMalformedException.class, () -> { - tableService.delete(TABLE_5_PRIVILEGED_DTO); + tableService.delete(TABLE_5_DTO); }); } @@ -673,7 +422,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void getCount_succeeds() throws SQLException, QueryMalformedException { /* test */ - final Long response = tableService.getCount(TABLE_1_PRIVILEGED_DTO, null); + final Long response = tableService.getCount(TABLE_1_DTO, null); assertEquals(3, response); } @@ -681,7 +430,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void getCount_timestamp_succeeds() throws SQLException, QueryMalformedException { /* test */ - final Long response = tableService.getCount(TABLE_1_PRIVILEGED_DTO, Instant.ofEpochSecond(0)); + final Long response = tableService.getCount(TABLE_1_DTO, Instant.ofEpochSecond(0)); assertEquals(0, response); } @@ -689,11 +438,11 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void getCount_notFound_fails() throws SQLException { /* mock */ - MariaDbConfig.createDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); + MariaDbConfig.createDatabase(CONTAINER_1_DTO, DATABASE_2_INTERNALNAME); /* test */ assertThrows(QueryMalformedException.class, () -> { - tableService.getCount(TABLE_5_PRIVILEGED_DTO, null); + tableService.getCount(TABLE_5_DTO, null); }); } @@ -701,7 +450,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void history_succeeds() throws SQLException, TableNotFoundException { /* test */ - final List<TableHistoryDto> response = tableService.history(TABLE_1_PRIVILEGED_DTO, 1000L); + final List<TableHistoryDto> response = tableService.history(TABLE_1_DTO, 1000L); assertEquals(1, response.size()); final TableHistoryDto history0 = response.get(0); assertNotNull(history0.getTimestamp()); @@ -713,11 +462,11 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void history_notFound_fails() throws SQLException { /* mock */ - MariaDbConfig.createDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); + MariaDbConfig.createDatabase(CONTAINER_1_DTO, DATABASE_2_INTERNALNAME); /* test */ assertThrows(TableNotFoundException.class, () -> { - tableService.history(TABLE_5_PRIVILEGED_DTO, null); + tableService.history(TABLE_5_DTO, null); }); } @@ -739,7 +488,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { .build(), RequestBody.fromFile(new File("src/test/resources/csv/weather_aus.csv"))); /* test */ - tableService.importDataset(TABLE_1_PRIVILEGED_DTO, request); + tableService.importDataset(TABLE_1_DTO, request); } @Test @@ -760,7 +509,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* test */ assertThrows(TableMalformedException.class, () -> { - tableService.importDataset(TABLE_1_PRIVILEGED_DTO, request); + tableService.importDataset(TABLE_1_DTO, request); }); } @@ -782,7 +531,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* test */ assertThrows(MalformedException.class, () -> { - tableService.importDataset(TABLE_1_PRIVILEGED_DTO, request); + tableService.importDataset(TABLE_1_DTO, request); }); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java index b5555611c6..658c099724 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java @@ -1,12 +1,8 @@ package at.tuwien.service; -import at.tuwien.api.database.ViewColumnDto; -import at.tuwien.api.database.ViewDto; import at.tuwien.config.MariaDbConfig; import at.tuwien.config.MariaDbContainerConfig; -import at.tuwien.exception.DatabaseMalformedException; import at.tuwien.exception.ViewMalformedException; -import at.tuwien.exception.ViewNotFoundException; import at.tuwien.test.AbstractUnitTest; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; @@ -20,9 +16,6 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import java.sql.SQLException; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; @Log4j2 @SpringBootTest @@ -40,47 +33,15 @@ public class ViewServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); } @Test public void delete_succeeds() throws SQLException, ViewMalformedException { /* test */ - viewService.delete(DATABASE_1_PRIVILEGED_DTO, VIEW_1_INTERNAL_NAME); - } - - @Test - public void create_succeeds() throws SQLException, ViewMalformedException { - - /* test */ - viewService.create(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO); - } - - @Test - public void getSchemas_succeeds() throws SQLException, ViewNotFoundException, DatabaseMalformedException { - - /* test */ - final List<ViewDto> response = viewService.getSchemas(DATABASE_1_PRIVILEGED_DTO); - final ViewDto view0 = response.get(0); - assertEquals("not_in_metadata_db2", view0.getName()); - assertEquals("not_in_metadata_db2", view0.getInternalName()); - assertEquals(DATABASE_1_ID, view0.getVdbid()); - assertEquals(DATABASE_1_OWNER, view0.getOwner().getId()); - assertFalse(view0.getIsInitialView()); - assertEquals(DATABASE_1_PUBLIC, view0.getIsPublic()); - assertTrue(view0.getQuery().length() >= 69); - assertNotNull(view0.getQueryHash()); - assertEquals(4, view0.getColumns().size()); - final ViewColumnDto column0a = view0.getColumns().get(0); - assertEquals("date", column0a.getInternalName()); - final ViewColumnDto column1a = view0.getColumns().get(1); - assertEquals("location", column1a.getInternalName()); - final ViewColumnDto column2a = view0.getColumns().get(2); - assertEquals("MinTemp", column2a.getInternalName()); - final ViewColumnDto column3a = view0.getColumns().get(3); - assertEquals("Rainfall", column3a.getInternalName()); + viewService.delete(VIEW_1_DTO); } } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/config/CacheConfig.java b/dbrepo-data-service/services/src/main/java/at/tuwien/config/CacheConfig.java index 45654157d1..c798537b5b 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/config/CacheConfig.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/config/CacheConfig.java @@ -1,11 +1,11 @@ package at.tuwien.config; -import at.tuwien.api.container.internal.PrivilegedContainerDto; +import at.tuwien.api.container.ContainerDto; import at.tuwien.api.database.DatabaseAccessDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.ViewDto; +import at.tuwien.api.database.table.TableDto; +import at.tuwien.api.user.UserDto; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.beans.factory.annotation.Value; @@ -22,13 +22,13 @@ public class CacheConfig { private Long credentialCacheTimeout; @Bean - public Cache<UUID, PrivilegedUserDto> userCache() { - return new ExpiryCache<UUID, PrivilegedUserDto>().build(); + public Cache<UUID, UserDto> userCache() { + return new ExpiryCache<UUID, UserDto>().build(); } @Bean - public Cache<Long, PrivilegedViewDto> viewCache() { - return new ExpiryCache<Long, PrivilegedViewDto>().build(); + public Cache<Long, ViewDto> viewCache() { + return new ExpiryCache<Long, ViewDto>().build(); } @Bean @@ -37,18 +37,18 @@ public class CacheConfig { } @Bean - public Cache<Long, PrivilegedTableDto> tableCache() { - return new ExpiryCache<Long, PrivilegedTableDto>().build(); + public Cache<Long, TableDto> tableCache() { + return new ExpiryCache<Long, TableDto>().build(); } @Bean - public Cache<Long, PrivilegedDatabaseDto> databaseCache() { - return new ExpiryCache<Long, PrivilegedDatabaseDto>().build(); + public Cache<Long, DatabaseDto> databaseCache() { + return new ExpiryCache<Long, DatabaseDto>().build(); } @Bean - public Cache<Long, PrivilegedContainerDto> containerCache() { - return new ExpiryCache<Long, PrivilegedContainerDto>().build(); + public Cache<Long, ContainerDto> containerCache() { + return new ExpiryCache<Long, ContainerDto>().build(); } class ExpiryCache<K, T> { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java index e2851071da..f7fe2f2075 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java @@ -1,13 +1,12 @@ package at.tuwien.gateway; -import at.tuwien.api.container.internal.PrivilegedContainerDto; +import at.tuwien.api.container.ContainerDto; import at.tuwien.api.database.DatabaseAccessDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.ViewDto; +import at.tuwien.api.database.table.TableDto; import at.tuwien.api.identifier.IdentifierBriefDto; import at.tuwien.api.user.UserDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; import at.tuwien.exception.*; import jakarta.validation.constraints.NotNull; @@ -20,12 +19,12 @@ public interface MetadataServiceGateway { * Get a container with given id from the metadata service. * * @param containerId The container id - * @return The container with privileged connection information, if successful. + * @return The container with connection information, if successful. * @throws ContainerNotFoundException The table was not found in the metadata service. * @throws RemoteUnavailableException The remote service is not available. * @throws MetadataServiceException The remote service returned invalid data. */ - PrivilegedContainerDto getContainerById(Long containerId) throws RemoteUnavailableException, + ContainerDto getContainerById(Long containerId) throws RemoteUnavailableException, ContainerNotFoundException, MetadataServiceException; /** @@ -37,7 +36,7 @@ public interface MetadataServiceGateway { * @throws RemoteUnavailableException The remote service is not available. * @throws MetadataServiceException The remote service returned invalid data. */ - PrivilegedDatabaseDto getDatabaseById(Long id) throws DatabaseNotFoundException, RemoteUnavailableException, + DatabaseDto getDatabaseById(Long id) throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException; /** @@ -50,7 +49,7 @@ public interface MetadataServiceGateway { * @throws RemoteUnavailableException The remote service is not available. * @throws MetadataServiceException The remote service returned invalid data. */ - PrivilegedTableDto getTableById(Long databaseId, Long id) throws TableNotFoundException, RemoteUnavailableException, + TableDto getTableById(Long databaseId, Long id) throws TableNotFoundException, RemoteUnavailableException, MetadataServiceException; /** @@ -63,7 +62,7 @@ public interface MetadataServiceGateway { * @throws RemoteUnavailableException The remote service is not available. * @throws MetadataServiceException The remote service returned invalid data. */ - PrivilegedViewDto getViewById(Long databaseId, Long id) throws RemoteUnavailableException, ViewNotFoundException, + ViewDto getViewById(Long databaseId, Long id) throws RemoteUnavailableException, ViewNotFoundException, MetadataServiceException; /** @@ -77,18 +76,6 @@ public interface MetadataServiceGateway { */ UserDto getUserById(UUID userId) throws RemoteUnavailableException, UserNotFoundException, MetadataServiceException; - /** - * Get a user with given user id from the metadata service. - * - * @param userId The user id. - * @return The user, if successful. - * @throws RemoteUnavailableException The remote service is not available and invalid data was returned. - * @throws UserNotFoundException The user was not found in the metadata service. - * @throws MetadataServiceException The remote service returned invalid data. - */ - PrivilegedUserDto getPrivilegedUserById(UUID userId) throws RemoteUnavailableException, UserNotFoundException, - MetadataServiceException; - /** * Get database access for a given user and database id from the metadata service. * diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java index 7d834992cc..57d6ffab7c 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java @@ -1,17 +1,12 @@ package at.tuwien.gateway.impl; import at.tuwien.api.container.ContainerDto; -import at.tuwien.api.container.image.ImageDto; -import at.tuwien.api.container.internal.PrivilegedContainerDto; import at.tuwien.api.database.DatabaseAccessDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; import at.tuwien.api.database.table.TableDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.api.identifier.IdentifierBriefDto; import at.tuwien.api.user.UserDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; import at.tuwien.config.GatewayConfig; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; @@ -36,27 +31,24 @@ import java.util.UUID; @Service public class MetadataServiceGatewayImpl implements MetadataServiceGateway { - private final RestTemplate restTemplate; private final RestTemplate internalRestTemplate; private final GatewayConfig gatewayConfig; private final MetadataMapper metadataMapper; @Autowired public MetadataServiceGatewayImpl(@Qualifier("internalRestTemplate") RestTemplate internalRestTemplate, - RestTemplate restTemplate, GatewayConfig gatewayConfig, - MetadataMapper metadataMapper) { - this.restTemplate = restTemplate; + GatewayConfig gatewayConfig, MetadataMapper metadataMapper) { this.internalRestTemplate = internalRestTemplate; this.gatewayConfig = gatewayConfig; this.metadataMapper = metadataMapper; } @Override - public PrivilegedContainerDto getContainerById(Long containerId) throws RemoteUnavailableException, + public ContainerDto getContainerById(Long containerId) throws RemoteUnavailableException, ContainerNotFoundException, MetadataServiceException { final ResponseEntity<ContainerDto> response; final String url = "/api/container/" + containerId; - log.debug("get privileged container info from metadata service: {}", url); + log.debug("get container info from metadata service: {}", url); try { response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, ContainerDto.class); @@ -73,16 +65,16 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { } final List<String> expectedHeaders = List.of("X-Username", "X-Password"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { - log.error("Failed to find all privileged container headers"); + log.error("Failed to find all container headers"); log.debug("expected headers: {}", expectedHeaders); log.debug("found headers: {}", response.getHeaders().keySet()); - throw new MetadataServiceException("Failed to find all privileged container headers"); + throw new MetadataServiceException("Failed to find all container headers"); } if (response.getBody() == null) { log.error("Failed to find container with id {}: body is empty", containerId); throw new MetadataServiceException("Failed to find container with id " + containerId + ": body is empty"); } - final PrivilegedContainerDto container = metadataMapper.containerDtoToPrivilegedContainerDto(response.getBody()); + final ContainerDto container = metadataMapper.containerDtoToContainerDto(response.getBody()); container.setUsername(response.getHeaders().get("X-Username").get(0)); container.setPassword(response.getHeaders().get("X-Password").get(0)); container.setLastRetrieved(Instant.now()); @@ -90,13 +82,13 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { } @Override - public PrivilegedDatabaseDto getDatabaseById(Long id) throws DatabaseNotFoundException, RemoteUnavailableException, + public DatabaseDto getDatabaseById(Long id) throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException { - final ResponseEntity<PrivilegedDatabaseDto> response; + final ResponseEntity<DatabaseDto> response; final String url = "/api/database/" + id; - log.debug("get privileged database info from metadata service: {}", url); + log.debug("get database info from metadata service: {}", url); try { - response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, PrivilegedDatabaseDto.class); + response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, DatabaseDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { log.error("Failed to find database with id {}: {}", id, e.getMessage()); throw new RemoteUnavailableException("Failed to find database: " + e.getMessage(), e); @@ -110,16 +102,16 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { } final List<String> expectedHeaders = List.of("X-Username", "X-Password", "X-Host", "X-Port"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { - log.error("Failed to find all privileged database headers"); + log.error("Failed to find all database headers"); log.debug("expected headers: {}", expectedHeaders); log.debug("found headers: {}", response.getHeaders().keySet()); - throw new MetadataServiceException("Failed to find all privileged database headers"); + throw new MetadataServiceException("Failed to find all database headers"); } if (response.getBody() == null) { log.error("Failed to find database with id {}: body is empty", id); throw new MetadataServiceException("Failed to find database with id " + id + ": body is empty"); } - final PrivilegedDatabaseDto database = response.getBody(); + final DatabaseDto database = response.getBody(); database.getContainer().setUsername(response.getHeaders().get("X-Username").get(0)); database.getContainer().setPassword(response.getHeaders().get("X-Password").get(0)); database.getContainer().setHost(response.getHeaders().get("X-Host").get(0)); @@ -129,11 +121,11 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { } @Override - public PrivilegedTableDto getTableById(Long databaseId, Long id) throws TableNotFoundException, + public TableDto getTableById(Long databaseId, Long id) throws TableNotFoundException, RemoteUnavailableException, MetadataServiceException { final ResponseEntity<TableDto> response; final String url = "/api/database/" + databaseId + "/table/" + id; - log.debug("get privileged table info from metadata service: {}", url); + log.debug("get table info from metadata service: {}", url); try { response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, TableDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { @@ -149,33 +141,33 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { } final List<String> expectedHeaders = List.of("X-Type", "X-Host", "X-Port", "X-Username", "X-Password", "X-Database", "X-Table"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { - log.error("Failed to find all privileged table headers"); + log.error("Failed to find all table headers"); log.debug("expected headers: {}", expectedHeaders); log.debug("found headers: {}", response.getHeaders().keySet()); - throw new MetadataServiceException("Failed to find all privileged table headers"); + throw new MetadataServiceException("Failed to find all table headers"); } if (response.getBody() == null) { log.error("Failed to find table with id {}: body is empty", id); throw new MetadataServiceException("Failed to find table with id " + id + ": body is empty"); } - final PrivilegedTableDto table = metadataMapper.tableDtoToPrivilegedTableDto(response.getBody()); - table.getDatabase().getContainer().getImage().setJdbcMethod(response.getHeaders().get("X-Type").get(0)); - table.getDatabase().getContainer().setHost(response.getHeaders().get("X-Host").get(0)); - table.getDatabase().getContainer().setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0))); - table.getDatabase().getContainer().setUsername(response.getHeaders().get("X-Username").get(0)); - table.getDatabase().getContainer().setPassword(response.getHeaders().get("X-Password").get(0)); - table.getDatabase().setInternalName(response.getHeaders().get("X-Database").get(0)); + final TableDto table = metadataMapper.tableDtoToTableDto(response.getBody()); + table.setJdbcMethod(response.getHeaders().get("X-Type").get(0)); + table.setHost(response.getHeaders().get("X-Host").get(0)); + table.setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0))); + table.setUsername(response.getHeaders().get("X-Username").get(0)); + table.setPassword(response.getHeaders().get("X-Password").get(0)); + table.setDatabase(response.getHeaders().get("X-Database").get(0)); table.setInternalName(response.getHeaders().get("X-Table").get(0)); table.setLastRetrieved(Instant.now()); return table; } @Override - public PrivilegedViewDto getViewById(Long databaseId, Long id) throws RemoteUnavailableException, + public ViewDto getViewById(Long databaseId, Long id) throws RemoteUnavailableException, ViewNotFoundException, MetadataServiceException { final ResponseEntity<ViewDto> response; final String url = "/api/database/" + databaseId + "/view/" + id; - log.debug("get privileged view info from metadata service: {}", url); + log.debug("get view info from metadata service: {}", url); try { response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, ViewDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { @@ -191,28 +183,22 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { } final List<String> expectedHeaders = List.of("X-Type", "X-Host", "X-Port", "X-Username", "X-Password", "X-Database", "X-View"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { - log.error("Failed to find all privileged view headers"); + log.error("Failed to find all view headers"); log.debug("expected headers: {}", expectedHeaders); log.debug("found headers: {}", response.getHeaders().keySet()); - throw new MetadataServiceException("Failed to find all privileged view headers"); + throw new MetadataServiceException("Failed to find all view headers"); } if (response.getBody() == null) { log.error("Failed to find view with id {}: body is empty", id); throw new MetadataServiceException("Failed to find view with id " + id + ": body is empty"); } - final PrivilegedViewDto view = metadataMapper.viewDtoToPrivilegedViewDto(response.getBody()); - view.setDatabase(PrivilegedDatabaseDto.builder() - .internalName(response.getHeaders().get("X-Database").get(0)) - .container(PrivilegedContainerDto.builder() - .host(response.getHeaders().get("X-Host").get(0)) - .port(Integer.parseInt(response.getHeaders().get("X-Port").get(0))) - .username(response.getHeaders().get("X-Username").get(0)) - .password(response.getHeaders().get("X-Password").get(0)) - .image(ImageDto.builder() - .jdbcMethod(response.getHeaders().get("X-Type").get(0)) - .build()) - .build()) - .build()); + final ViewDto view = metadataMapper.viewDtoToViewDto(response.getBody()); + view.setJdbcMethod(response.getHeaders().get("X-Type").get(0)); + view.setHost(response.getHeaders().get("X-Host").get(0)); + view.setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0))); + view.setUsername(response.getHeaders().get("X-Username").get(0)); + view.setPassword(response.getHeaders().get("X-Password").get(0)); + view.setDatabase(response.getHeaders().get("X-Database").get(0)); view.setInternalName(response.getHeaders().get("X-View").get(0)); view.setLastRetrieved(Instant.now()); return view; @@ -223,33 +209,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { MetadataServiceException { final ResponseEntity<UserDto> response; final String url = "/api/user/" + userId; - log.debug("get user info from metadata service: {}", url); - try { - response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, UserDto.class); - } catch (ResourceAccessException | HttpServerErrorException e) { - log.error("Failed to find user with id {}: {}", userId, e.getMessage()); - throw new RemoteUnavailableException("Failed to find user: " + e.getMessage(), e); - } catch (HttpClientErrorException.NotFound e) { - log.error("Failed to find user with id {}: not found: {}", userId, e.getMessage()); - throw new UserNotFoundException("Failed to find user: " + e.getMessage(), e); - } - if (!response.getStatusCode().equals(HttpStatus.OK)) { - log.error("Failed to find user with id {}: service responded unsuccessful: {}", userId, response.getStatusCode()); - throw new MetadataServiceException("Failed to find user: service responded unsuccessful: " + response.getStatusCode()); - } - if (response.getBody() == null) { - log.error("Failed to find user with id {}: body is empty", userId); - throw new MetadataServiceException("Failed to find user with id " + userId + ": body is empty"); - } - return response.getBody(); - } - - @Override - public PrivilegedUserDto getPrivilegedUserById(UUID userId) throws RemoteUnavailableException, UserNotFoundException, - MetadataServiceException { - final ResponseEntity<UserDto> response; - final String url = "/api/user/" + userId; - log.debug("get privileged user info from metadata service: {}", url); + log.debug("get user info from metadata service: {}", url); try { response = internalRestTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, UserDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { @@ -265,16 +225,16 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { } final List<String> expectedHeaders = List.of("X-Username", "X-Password"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { - log.error("Failed to find all privileged user headers"); + log.error("Failed to find all user headers"); log.debug("expected headers: {}", expectedHeaders); log.debug("found headers: {}", response.getHeaders().keySet()); - throw new MetadataServiceException("Failed to find all privileged user headers"); + throw new MetadataServiceException("Failed to find all user headers"); } if (response.getBody() == null) { log.error("Failed to find user with id {}: body is empty", userId); throw new MetadataServiceException("Failed to find user with id " + userId + ": body is empty"); } - final PrivilegedUserDto user = metadataMapper.userDtoToPrivilegedUserDto(response.getBody()); + final UserDto user = metadataMapper.userDtoToUserDto(response.getBody()); user.setUsername(response.getHeaders().get("X-Username").get(0)); user.setPassword(response.getHeaders().get("X-Password").get(0)); user.setLastRetrieved(Instant.now()); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java b/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java index fac47a3d80..e31455a75e 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java @@ -1,7 +1,9 @@ package at.tuwien.listener; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; -import at.tuwien.exception.*; +import at.tuwien.api.database.table.TableDto; +import at.tuwien.exception.MetadataServiceException; +import at.tuwien.exception.RemoteUnavailableException; +import at.tuwien.exception.TableNotFoundException; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.service.QueueService; import com.fasterxml.jackson.core.type.TypeReference; @@ -57,7 +59,7 @@ public class DefaultListener implements MessageListener { log.trace("received message for table with id {} of database id {}: {} bytes", tableId, databaseId, message.getMessageProperties().getContentLength()); final Map<String, Object> body; try { - final PrivilegedTableDto table = metadataServiceGateway.getTableById(databaseId, tableId); + final TableDto table = metadataServiceGateway.getTableById(databaseId, tableId); body = objectMapper.readValue(message.getBody(), typeRef); queueService.insert(table, body); } catch (IOException e) { 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 db848cab7e..7f5b63b21c 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 @@ -1,12 +1,12 @@ package at.tuwien.mapper; +import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.TupleDeleteDto; import at.tuwien.api.database.table.TupleDto; import at.tuwien.api.database.table.TupleUpdateDto; import at.tuwien.api.database.table.columns.ColumnCreateDto; import at.tuwien.api.database.table.columns.ColumnDto; import at.tuwien.api.database.table.columns.ColumnTypeDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.exception.QueryMalformedException; import at.tuwien.exception.TableMalformedException; import at.tuwien.utils.MariaDbUtil; @@ -519,7 +519,7 @@ public interface MariaDbMapper { return statement.toString(); } - default String tupleToRawDeleteQuery(PrivilegedTableDto table, TupleDeleteDto data) throws TableMalformedException { + default String tupleToRawDeleteQuery(TableDto table, TupleDeleteDto data) throws TableMalformedException { log.trace("table csv to delete query, table.id={}, data.keys={}", table.getId(), data.getKeys()); if (table.getColumns().isEmpty()) { throw new TableMalformedException("Columns are not known"); @@ -540,14 +540,14 @@ public interface MariaDbMapper { return statement.toString(); } - default String tupleToRawUpdateQuery(PrivilegedTableDto table, TupleUpdateDto data) + default String tupleToRawUpdateQuery(TableDto table, TupleUpdateDto data) throws TableMalformedException { if (table.getColumns().isEmpty()) { throw new TableMalformedException("Columns are not known"); } /* parameterized query for prepared statement */ final StringBuilder statement = new StringBuilder("UPDATE `") - .append(table.getDatabase().getInternalName()) + .append(table.getDatabase()) .append("`.`") .append(table.getInternalName()) .append("` SET "); @@ -579,13 +579,13 @@ public interface MariaDbMapper { return statement.toString(); } - default String tupleToRawCreateQuery(PrivilegedTableDto table, TupleDto data) throws TableMalformedException { + default String tupleToRawCreateQuery(TableDto table, TupleDto data) throws TableMalformedException { if (table.getColumns().isEmpty()) { throw new TableMalformedException("Columns are not known"); } /* parameterized query for prepared statement */ final StringBuilder statement = new StringBuilder("INSERT INTO `") - .append(table.getDatabase().getInternalName()) + .append(table.getDatabase()) .append("`.`") .append(table.getInternalName()) .append("` ("); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java index 0adfafa8f9..359e251ea2 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java @@ -2,28 +2,23 @@ package at.tuwien.mapper; import at.tuwien.api.container.ContainerDto; import at.tuwien.api.container.image.ImageDto; -import at.tuwien.api.container.internal.PrivilegedContainerDto; import at.tuwien.api.database.DatabaseBriefDto; import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.ViewColumnDto; import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.table.TableBriefDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.columns.ColumnDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.api.identifier.IdentifierBriefDto; import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.UserBriefDto; import at.tuwien.api.user.UserDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; -@Mapper(componentModel = "spring", imports = {PrivilegedDatabaseDto.class, PrivilegedContainerDto.class, ImageDto.class}) +@Mapper(componentModel = "spring", imports = {DatabaseDto.class, ContainerDto.class, ImageDto.class}) public interface MetadataMapper { org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MetadataMapper.class); @@ -32,28 +27,21 @@ public interface MetadataMapper { return subset.getQueryHash(); } - PrivilegedContainerDto containerDtoToPrivilegedContainerDto(ContainerDto data); + ContainerDto containerDtoToContainerDto(ContainerDto data); - DatabaseDto privilegedDatabaseDtoToDatabaseDto(PrivilegedDatabaseDto data); - - DatabaseBriefDto privilegedDatabaseDtoToDatabaseBriefDto(PrivilegedDatabaseDto data); - - TableDto privilegedTableDtoToTableDto(PrivilegedTableDto data); + DatabaseBriefDto databaseDtoToDatabaseBriefDto(DatabaseDto data); ColumnDto viewColumnDtoToColumnDto(ViewColumnDto data); ViewColumnDto columnDtoToViewColumnDto(ColumnDto data); - @Mappings({ - @Mapping(target = "database", expression = "java(PrivilegedDatabaseDto.builder().container(PrivilegedContainerDto.builder().image(new ImageDto()).build()).build())") - }) - PrivilegedTableDto tableDtoToPrivilegedTableDto(TableDto data); + TableDto tableDtoToTableDto(TableDto data); - PrivilegedViewDto viewDtoToPrivilegedViewDto(ViewDto data); + ViewDto viewDtoToViewDto(ViewDto data); - ContainerDto privilegedContainerDtoToContainerDto(PrivilegedContainerDto data); + ContainerDto ContainerDtoToContainerDto(ContainerDto data); - PrivilegedUserDto userDtoToPrivilegedUserDto(UserDto data); + UserDto userDtoToUserDto(UserDto data); UserBriefDto userDtoToUserBriefDto(UserDto data); @@ -64,6 +52,8 @@ public interface MetadataMapper { IdentifierBriefDto identifierDtoToIdentifierBriefDto(IdentifierDto data); + TableDto databaseDtoToTableDto(DatabaseDto data); + default String metricToUri(String baseUrl, Long databaseId, Long tableId, Long subsetId, Long viewId) { final StringBuilder uri = new StringBuilder(baseUrl) .append("/database/") diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/AccessService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/AccessService.java index 89707ce8f2..c42fc28101 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/AccessService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/AccessService.java @@ -1,8 +1,8 @@ package at.tuwien.service; import at.tuwien.api.database.AccessTypeDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.user.UserDto; import at.tuwien.exception.DatabaseMalformedException; import java.sql.SQLException; @@ -18,7 +18,7 @@ public interface AccessService { * @throws SQLException The connection to the database could not be established. * @throws DatabaseMalformedException The database schema is malformed. */ - void create(PrivilegedDatabaseDto database, PrivilegedUserDto user, AccessTypeDto access) throws SQLException, + void create(DatabaseDto database, UserDto user, AccessTypeDto access) throws SQLException, DatabaseMalformedException; /** @@ -30,7 +30,7 @@ public interface AccessService { * @throws SQLException The connection to the database could not be established. * @throws DatabaseMalformedException The database schema is malformed. */ - void update(PrivilegedDatabaseDto database, PrivilegedUserDto user, AccessTypeDto access) throws SQLException, + void update(DatabaseDto database, UserDto user, AccessTypeDto access) throws SQLException, DatabaseMalformedException; /** @@ -41,6 +41,5 @@ public interface AccessService { * @throws SQLException The connection to the database could not be established. * @throws DatabaseMalformedException The database schema is malformed. */ - void delete(PrivilegedDatabaseDto database, PrivilegedUserDto user) throws SQLException, - DatabaseMalformedException; + void delete(DatabaseDto database, UserDto user) throws SQLException, DatabaseMalformedException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/ContainerService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/ContainerService.java new file mode 100644 index 0000000000..4f9e92ed78 --- /dev/null +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/ContainerService.java @@ -0,0 +1,33 @@ +package at.tuwien.service; + +import at.tuwien.api.container.ContainerDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.internal.CreateDatabaseDto; +import at.tuwien.exception.DatabaseMalformedException; +import at.tuwien.exception.QueryStoreCreateException; + +import java.sql.SQLException; + +public interface ContainerService { + + /** + * Creates a database in the given container. + * @param container The container. + * @param data The database metadata. + * @return The created database, if successful. + * @throws SQLException The connection to the database could not be established. + * @throws DatabaseMalformedException The database schema is malformed. + */ + DatabaseDto createDatabase(ContainerDto container, CreateDatabaseDto data) throws SQLException, + DatabaseMalformedException; + + /** + * Creates the query store in the container and database. + * + * @param container The container. + * @param databaseName The database name. + * @throws SQLException The connection to the database could not be established. + * @throws QueryStoreCreateException The query store could not be created. + */ + void createQueryStore(ContainerDto container, String databaseName) throws SQLException, QueryStoreCreateException; +} diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/CredentialService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/CredentialService.java index ff09d6672f..fe3df343db 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/CredentialService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/CredentialService.java @@ -1,11 +1,11 @@ package at.tuwien.service; -import at.tuwien.api.container.internal.PrivilegedContainerDto; +import at.tuwien.api.container.ContainerDto; import at.tuwien.api.database.DatabaseAccessDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.ViewDto; +import at.tuwien.api.database.table.TableDto; +import at.tuwien.api.user.UserDto; import at.tuwien.exception.*; import java.util.UUID; @@ -22,7 +22,7 @@ public interface CredentialService { * @throws RemoteUnavailableException The remote service is not available. * @throws MetadataServiceException The remote service returned invalid data. */ - PrivilegedDatabaseDto getDatabase(Long id) throws DatabaseNotFoundException, RemoteUnavailableException, + DatabaseDto getDatabase(Long id) throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException; /** @@ -35,7 +35,7 @@ public interface CredentialService { * @throws RemoteUnavailableException The remote service is not available. * @throws MetadataServiceException The remote service returned invalid data. */ - PrivilegedContainerDto getContainer(Long id) throws ContainerNotFoundException, RemoteUnavailableException, + ContainerDto getContainer(Long id) throws ContainerNotFoundException, RemoteUnavailableException, MetadataServiceException; /** @@ -49,7 +49,7 @@ public interface CredentialService { * @throws RemoteUnavailableException The remote service is not available. * @throws MetadataServiceException The remote service returned invalid data. */ - PrivilegedTableDto getTable(Long databaseId, Long tableId) throws RemoteUnavailableException, + TableDto getTable(Long databaseId, Long tableId) throws RemoteUnavailableException, MetadataServiceException, TableNotFoundException; /** @@ -63,7 +63,7 @@ public interface CredentialService { * @throws RemoteUnavailableException The remote service is not available. * @throws MetadataServiceException The remote service returned invalid data. */ - PrivilegedViewDto getView(Long databaseId, Long viewId) throws RemoteUnavailableException, + ViewDto getView(Long databaseId, Long viewId) throws RemoteUnavailableException, MetadataServiceException, ViewNotFoundException; /** @@ -76,7 +76,7 @@ public interface CredentialService { * @throws RemoteUnavailableException The remote service is not available. * @throws MetadataServiceException The remote service returned invalid data. */ - PrivilegedUserDto getUser(UUID id) throws RemoteUnavailableException, MetadataServiceException, + UserDto getUser(UUID id) throws RemoteUnavailableException, MetadataServiceException, UserNotFoundException; /** diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java index 271b2abb82..5a120f44d2 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java @@ -1,27 +1,92 @@ package at.tuwien.service; -import at.tuwien.api.container.internal.PrivilegedContainerDto; import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.database.internal.CreateDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; +import at.tuwien.api.database.ViewCreateDto; +import at.tuwien.api.database.ViewDto; +import at.tuwien.api.database.table.TableDto; +import at.tuwien.api.database.table.internal.TableCreateDto; import at.tuwien.api.user.internal.UpdateUserPasswordDto; import at.tuwien.exception.*; import java.sql.SQLException; +import java.util.List; public interface DatabaseService { /** - * Creates a database in the given container. - * @param container The container. - * @param data The database metadata. - * @return The created database, if successful. + * Inspects the schema (columns with names, data types) of a view with given name in the given database. + * @param database The database. + * @param viewName The view name. + * @return The inspected view if successful. * @throws SQLException The connection to the database could not be established. - * @throws DatabaseMalformedException The database schema is malformed. + * @throws ViewNotFoundException The view was not found in the given database. + */ + ViewDto inspectView(DatabaseDto database, String viewName) throws SQLException, ViewNotFoundException; + + /** + * Creates a table in given data database with table definition. + * + * @param database The data database object. + * @param data The table definition. + * @return The generated table. + * @throws SQLException Query statement is malformed. + * @throws TableMalformedException The table schema is malformed. + * @throws TableExistsException The table name already exists in the information_schema. + * @throws TableNotFoundException The table could not be inspected in the metadata database. */ - PrivilegedDatabaseDto create(PrivilegedContainerDto container, CreateDatabaseDto data) throws SQLException, + TableDto createTable(DatabaseDto database, TableCreateDto data) throws SQLException, + TableMalformedException, TableExistsException, TableNotFoundException; + + Boolean existsView(DatabaseDto database, String viewName) throws SQLException, + QueryMalformedException; + + /** + * Creates a view in given data database with view definition. + * @param database The data database object. + * @param data The view definition. + * @return The generated view. + * @throws SQLException + * @throws ViewMalformedException + */ + ViewDto createView(DatabaseDto database, ViewCreateDto data) throws SQLException, + ViewMalformedException; + + /** + * Gets the metadata schema for a given database. + * + * @param database The database. + * @return The list of view metadata. + * @throws SQLException The connection to the data database was unsuccessful. + * @throws DatabaseMalformedException The columns that are referenced in the views are unknown to the Metadata Database. Call {@link TableService#getSchemas(DatabaseDto)} beforehand. + * @throws ViewNotFoundException The view with given name was not found. + */ + List<ViewDto> exploreViews(DatabaseDto database) throws SQLException, DatabaseMalformedException, + ViewNotFoundException; + + /** + * Get table schemas from the information_schema in the data database. + * + * @param database The data database object. + * @return List of tables, if successful. + * @throws SQLException Failed to parse SQL query, contains invalid syntax. + * @throws TableNotFoundException The table could not be inspected in the data database. + * @throws DatabaseMalformedException The database inspection was unsuccessful, likely due to a bug in the mapping. + */ + List<TableDto> exploreTables(DatabaseDto database) throws SQLException, TableNotFoundException, DatabaseMalformedException; + /** + * Inspects the schema (columns with names, data types, unique-, check-, primary- and foreign key constraints) of + * a table with given name in the given database. + * + * @param database The database. + * @param tableName The table name. + * @return The inspected table if successful. + * @throws SQLException The connection to the database could not be established. + * @throws TableNotFoundException The table was not found in the given database. + */ + TableDto inspectTable(DatabaseDto database, String tableName) throws SQLException, TableNotFoundException; + /** * Updates a user's password in a given database. * @param database The database. @@ -29,6 +94,6 @@ public interface DatabaseService { * @throws SQLException The connection to the database could not be established. * @throws DatabaseMalformedException The database schema is malformed. */ - void update(PrivilegedDatabaseDto database, UpdateUserPasswordDto data) throws SQLException, + void update(DatabaseDto database, UpdateUserPasswordDto data) throws SQLException, DatabaseMalformedException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/QueueService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/QueueService.java index 79a23932b5..6a03f5d767 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/QueueService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/QueueService.java @@ -1,6 +1,6 @@ package at.tuwien.service; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; +import at.tuwien.api.database.table.TableDto; import java.sql.SQLException; import java.util.Map; @@ -14,5 +14,5 @@ public interface QueueService { * @param data The data. * @throws SQLException The connection to the database could not be established. */ - void insert(PrivilegedTableDto table, Map<String, Object> data) throws SQLException; + void insert(TableDto table, Map<String, Object> data) throws SQLException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/SchemaService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/SchemaService.java deleted file mode 100644 index f5ef05b44a..0000000000 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/SchemaService.java +++ /dev/null @@ -1,33 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.table.TableDto; -import at.tuwien.exception.*; - -import java.sql.SQLException; - -public interface SchemaService { - - /** - * Inspects the schema (columns with names, data types, unique-, check-, primary- and foreign key constraints) of - * a table with given name in the given database. - * @param database The database. - * @param tableName The table name. - * @return The inspected table if successful. - * @throws SQLException The connection to the database could not be established. - * @throws TableNotFoundException The table was not found in the given database. - */ - TableDto inspectTable(PrivilegedDatabaseDto database, String tableName) throws SQLException, - TableNotFoundException; - - /** - * Inspects the schema (columns with names, data types) of a view with given name in the given database. - * @param database The database. - * @param viewName The table name. - * @return The inspected view if successful. - * @throws SQLException The connection to the database could not be established. - * @throws ViewNotFoundException The view was not found in the given database. - */ - ViewDto inspectView(PrivilegedDatabaseDto database, String viewName) throws SQLException, ViewNotFoundException; -} diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java index 4a3455fbc4..b2de5cecca 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java @@ -1,7 +1,6 @@ package at.tuwien.service; -import at.tuwien.api.container.internal.PrivilegedContainerDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.exception.*; import org.apache.spark.sql.Dataset; @@ -14,17 +13,6 @@ import java.util.UUID; public interface SubsetService { - /** - * Creates the query store in the container and database. - * - * @param container The container. - * @param databaseName The database name. - * @throws SQLException The connection to the database could not be established. - * @throws QueryStoreCreateException The query store could not be created. - */ - void createQueryStore(PrivilegedContainerDto container, String databaseName) throws SQLException, - QueryStoreCreateException; - /** * Retrieve data from a subset in a database and optionally paginate with number of page and size of results. * @@ -38,7 +26,7 @@ public interface SubsetService { * @throws QueryMalformedException The mapped query produced a database error. * @throws TableNotFoundException The database table is malformed. */ - Dataset<Row> getData(PrivilegedDatabaseDto database, QueryDto subset, Long page, Long size) + Dataset<Row> getData(DatabaseDto database, QueryDto subset, Long page, Long size) throws ViewMalformedException, SQLException, QueryMalformedException, TableNotFoundException; /** @@ -52,7 +40,7 @@ public interface SubsetService { * @throws QueryStoreInsertException The query store refused to insert the query. * @throws SQLException The connection to the database could not be established. */ - Long create(PrivilegedDatabaseDto database, String statement, Instant timestamp, UUID userId) + Long create(DatabaseDto database, String statement, Instant timestamp, UUID userId) throws QueryStoreInsertException, SQLException; /** @@ -64,7 +52,7 @@ public interface SubsetService { * @throws TableMalformedException The table is malformed. * @throws SQLException The connection to the database could not be established. */ - Long reExecuteCount(PrivilegedDatabaseDto database, QueryDto query) throws TableMalformedException, + Long reExecuteCount(DatabaseDto database, QueryDto query) throws TableMalformedException, SQLException, QueryMalformedException; /** @@ -75,11 +63,11 @@ public interface SubsetService { * @return The list of queries. * @throws SQLException The connection to the database could not be established. * @throws QueryNotFoundException The query was not found for re-execution. - * @throws RemoteUnavailableException The privileged database information could not be found in the Metadata Service. + * @throws RemoteUnavailableException The database information could not be found in the Metadata Service. * @throws DatabaseNotFoundException The database was not found in the Metadata Service. * @throws MetadataServiceException The Metadata Service responded unexpected. */ - List<QueryDto> findAll(PrivilegedDatabaseDto database, Boolean filterPersisted) throws SQLException, + List<QueryDto> findAll(DatabaseDto database, Boolean filterPersisted) throws SQLException, QueryNotFoundException, RemoteUnavailableException, DatabaseNotFoundException, MetadataServiceException; /** @@ -93,7 +81,7 @@ public interface SubsetService { * @throws QueryMalformedException The mapped query produced a database error. * @throws TableMalformedException The database table is malformed. */ - Long executeCountNonPersistent(PrivilegedDatabaseDto database, String statement, Instant timestamp) + Long executeCountNonPersistent(DatabaseDto database, String statement, Instant timestamp) throws SQLException, QueryMalformedException, TableMalformedException; /** @@ -104,12 +92,12 @@ public interface SubsetService { * @return The query. * @throws QueryNotFoundException The query store did not return a query. * @throws SQLException The connection to the database could not be established. - * @throws RemoteUnavailableException The privileged database information could not be found in the Metadata Service. + * @throws RemoteUnavailableException The database information could not be found in the Metadata Service. * @throws UserNotFoundException The user that created the query was not found in the Metadata Service. * @throws DatabaseNotFoundException The database metadata was not found in the Metadata Service. * @throws MetadataServiceException Communication with the Metadata Service failed. */ - QueryDto findById(PrivilegedDatabaseDto database, Long queryId) throws QueryNotFoundException, SQLException, + QueryDto findById(DatabaseDto database, Long queryId) throws QueryNotFoundException, SQLException, RemoteUnavailableException, UserNotFoundException, DatabaseNotFoundException, MetadataServiceException; /** @@ -122,7 +110,7 @@ public interface SubsetService { * @throws SQLException The connection to the database could not be established. * @throws QueryStoreInsertException The query store failed to insert the query. */ - Long storeQuery(PrivilegedDatabaseDto database, String query, Instant timestamp, UUID userId) throws SQLException, + Long storeQuery(DatabaseDto database, String query, Instant timestamp, UUID userId) throws SQLException, QueryStoreInsertException; /** @@ -134,7 +122,7 @@ public interface SubsetService { * @throws SQLException The connection to the database could not be established. * @throws QueryStorePersistException The query store failed to persist/unpersist the query. */ - void persist(PrivilegedDatabaseDto database, Long queryId, Boolean persist) throws SQLException, + void persist(DatabaseDto database, Long queryId, Boolean persist) throws SQLException, QueryStorePersistException; /** @@ -144,5 +132,5 @@ public interface SubsetService { * @throws SQLException The connection to the database could not be established. * @throws QueryStoreGCException The query store failed to delete stale queries. */ - void deleteStaleQueries(PrivilegedDatabaseDto database) throws SQLException, QueryStoreGCException; + void deleteStaleQueries(DatabaseDto database) throws SQLException, QueryStoreGCException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java index 0564639874..b74c491abe 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java @@ -1,11 +1,9 @@ package at.tuwien.service; import at.tuwien.api.SortTypeDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.table.*; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; -import at.tuwien.api.database.table.internal.TableCreateDto; import at.tuwien.exception.*; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; @@ -16,57 +14,19 @@ import java.util.List; public interface TableService { - /** - * Get table schemas from the information_schema in the data database. - * - * @param database The data database privileged object. - * @return List of tables, if successful. - * @throws SQLException Failed to parse SQL query, contains invalid syntax. - * @throws TableNotFoundException The table could not be inspected in the data database. - * @throws DatabaseMalformedException The database inspection was unsuccessful, likely due to a bug in the mapping. - */ - List<TableDto> getSchemas(PrivilegedDatabaseDto database) throws SQLException, TableNotFoundException, - DatabaseMalformedException; - /** * Generate table statistic for a given table. Only numerical columns are calculated. * + * @param database The database. * @param table The table. * @return The table statistic, if successful. * @throws SQLException Failed to parse SQL query, contains invalid syntax. * @throws TableMalformedException The table statistic generation was unsuccessful, likely due to a bug in the mapping. * @throws TableNotFoundException The table could not be inspected in the data database. */ - TableStatisticDto getStatistics(PrivilegedTableDto table) throws SQLException, TableMalformedException, + TableStatisticDto getStatistics(DatabaseDto database, TableDto table) throws SQLException, TableMalformedException, TableNotFoundException; - /** - * Finds a table with given data database and table name. - * - * @param database The data database. - * @param tableName The table name. - * @return The table, if successful. - * @throws TableNotFoundException The table could not be inspected in the data database. - * @throws SQLException Failed to parse SQL query, contains invalid syntax. - * @throws QueryMalformedException The inspection query is malformed. - */ - TableDto find(PrivilegedDatabaseDto database, String tableName) throws TableNotFoundException, SQLException, - QueryMalformedException; - - /** - * Creates a table in given data database with table definition. - * - * @param database The data database privileged object. - * @param data The table definition. - * @return The created table, if successful. - * @throws SQLException Query statement is malformed. - * @throws TableMalformedException The table schema is malformed. - * @throws TableExistsException The table name already exists in the information_schema. - * @throws TableNotFoundException The table could not be inspected in the metadata database. - */ - TableDto createTable(PrivilegedDatabaseDto database, TableCreateDto data) throws SQLException, - TableMalformedException, TableExistsException, TableNotFoundException; - /** * Updating table description. * @@ -76,7 +36,7 @@ public interface TableService { * @throws TableMalformedException The table schema is malformed. * @throws TableNotFoundException The table could not be inspected in the metadata database. */ - void updateTable(PrivilegedTableDto table, TableUpdateDto data) throws SQLException, + void updateTable(TableDto table, TableUpdateDto data) throws SQLException, TableMalformedException, TableNotFoundException; /** @@ -86,7 +46,7 @@ public interface TableService { * @throws SQLException Failed to parse SQL query, contains invalid syntax. * @throws QueryMalformedException The drop table query is malformed. */ - void delete(PrivilegedTableDto table) throws SQLException, QueryMalformedException; + void delete(TableDto table) throws SQLException, QueryMalformedException; /** * Obtains the table history for a given table object. @@ -97,7 +57,7 @@ public interface TableService { * @throws SQLException Failed to parse SQL query, contains invalid syntax. * @throws TableNotFoundException The table could not be found in the data database. */ - List<TableHistoryDto> history(PrivilegedTableDto table, Long size) throws SQLException, TableNotFoundException; + List<TableHistoryDto> history(TableDto table, Long size) throws SQLException, TableNotFoundException; /** * Obtains the table data tuples count at time. @@ -108,7 +68,7 @@ public interface TableService { * @throws SQLException Failed to parse SQL query, contains invalid syntax. * @throws QueryMalformedException The count query is malformed, likely due to a bug in the application. */ - Long getCount(PrivilegedTableDto table, Instant timestamp) throws SQLException, + Long getCount(TableDto table, Instant timestamp) throws SQLException, QueryMalformedException; /** @@ -123,7 +83,7 @@ public interface TableService { * @throws SQLException Failed to parse SQL query, contains invalid syntax. * @throws QueryMalformedException The import query is malformed, likely due to a bug in the application. */ - void importDataset(PrivilegedTableDto table, ImportDto data) throws MalformedException, StorageNotFoundException, + void importDataset(TableDto table, ImportDto data) throws MalformedException, StorageNotFoundException, StorageUnavailableException, SQLException, QueryMalformedException, TableMalformedException; /** @@ -135,7 +95,7 @@ public interface TableService { * @throws TableMalformedException The tuple is malformed and does not fit the table schema. * @throws QueryMalformedException The delete query is malformed, likely due to a bug in the application. */ - void deleteTuple(PrivilegedTableDto table, TupleDeleteDto data) throws SQLException, + void deleteTuple(TableDto table, TupleDeleteDto data) throws SQLException, TableMalformedException, QueryMalformedException; /** @@ -149,7 +109,7 @@ public interface TableService { * @throws StorageUnavailableException Failed to establish a connection with the Storage Service. * @throws StorageNotFoundException The storage service was not able to find the dataset for import. */ - void createTuple(PrivilegedTableDto table, TupleDto data) throws SQLException, + void createTuple(TableDto table, TupleDto data) throws SQLException, QueryMalformedException, TableMalformedException, StorageUnavailableException, StorageNotFoundException; /** @@ -161,10 +121,10 @@ public interface TableService { * @throws QueryMalformedException The update query is malformed, likely due to a bug in the application. * @throws TableMalformedException The tuple is malformed and does not fit the table schema. */ - void updateTuple(PrivilegedTableDto table, TupleUpdateDto data) throws SQLException, + void updateTuple(TableDto table, TupleUpdateDto data) throws SQLException, QueryMalformedException, TableMalformedException; - Dataset<Row> getData(PrivilegedDatabaseDto database, String tableOrView, Instant timestamp, + Dataset<Row> getData(DatabaseDto database, String tableOrView, Instant timestamp, Long page, Long size, SortTypeDto sortDirection, String sortColumn) throws QueryMalformedException, TableNotFoundException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/ViewService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/ViewService.java index ec7a723261..8f721f9974 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/ViewService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/ViewService.java @@ -1,66 +1,22 @@ package at.tuwien.service; -import at.tuwien.api.database.ViewCreateDto; import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; -import at.tuwien.api.database.query.QueryDto; -import at.tuwien.exception.DatabaseMalformedException; import at.tuwien.exception.QueryMalformedException; import at.tuwien.exception.ViewMalformedException; -import at.tuwien.exception.ViewNotFoundException; import java.sql.SQLException; import java.time.Instant; -import java.util.List; public interface ViewService { - Boolean existsByName(PrivilegedDatabaseDto database, String name) throws SQLException, - QueryMalformedException; - - /** - * Gets the metadata schema for a given database. - * - * @param database The database. - * @return The list of view metadata. - * @throws SQLException The connection to the data database was unsuccessful. - * @throws DatabaseMalformedException The columns that are referenced in the views are unknown to the Metadata Database. Call {@link TableService#getSchemas(PrivilegedDatabaseDto)} beforehand. - * @throws ViewNotFoundException The view with given name was not found. - */ - List<ViewDto> getSchemas(PrivilegedDatabaseDto database) throws SQLException, DatabaseMalformedException, - ViewNotFoundException; - - /** - * Creates a view if not already exists. - * @param database - * @param subset - * @return - * @throws ViewMalformedException - * @throws SQLException - */ - ViewDto create(PrivilegedDatabaseDto database, QueryDto subset) throws ViewMalformedException, SQLException; - - /** - * Creates a view in the given data database. - * - * @param database The data database. - * @param data The view. - * @throws SQLException The connection to the data database was unsuccessful. - * @throws ViewMalformedException The query is malformed and was rejected by the data database. - */ - ViewDto create(PrivilegedDatabaseDto database, ViewCreateDto data) throws SQLException, - ViewMalformedException; - /** * Deletes a view. * - * @param database The database. - * @param viewName The view name. + * @param view The view. * @throws SQLException The connection to the data database was unsuccessful. * @throws ViewMalformedException The query is malformed and was rejected by the data database. */ - void delete(PrivilegedDatabaseDto database, String viewName) throws SQLException, ViewMalformedException; + void delete(ViewDto view) throws SQLException, ViewMalformedException; /** * Counts tuples in a view at system-versioned timestamp. @@ -71,5 +27,5 @@ public interface ViewService { * @throws SQLException The connection to the data database was unsuccessful. * @throws QueryMalformedException The query is malformed and was rejected by the data database. */ - Long count(PrivilegedViewDto view, Instant timestamp) throws SQLException, QueryMalformedException; + Long count(ViewDto view, Instant timestamp) throws SQLException, QueryMalformedException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java index c50ac2f0d6..16ace6dc9e 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java @@ -1,8 +1,8 @@ package at.tuwien.service.impl; import at.tuwien.api.database.AccessTypeDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.user.UserDto; import at.tuwien.exception.DatabaseMalformedException; import at.tuwien.mapper.MariaDbMapper; import at.tuwien.service.AccessService; @@ -17,7 +17,7 @@ import java.sql.SQLException; @Log4j2 @Service -public class AccessServiceMariaDbImpl extends HibernateConnector implements AccessService { +public class AccessServiceMariaDbImpl extends DataConnector<DatabaseDto> implements AccessService { @Value("${dbrepo.grant.default.read}") private String grantDefaultRead; @@ -33,9 +33,9 @@ public class AccessServiceMariaDbImpl extends HibernateConnector implements Acce } @Override - public void create(PrivilegedDatabaseDto database, PrivilegedUserDto user, AccessTypeDto access) + public void create(DatabaseDto database, UserDto user, AccessTypeDto access) throws SQLException, DatabaseMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { /* create user if not exists */ @@ -71,9 +71,9 @@ public class AccessServiceMariaDbImpl extends HibernateConnector implements Acce } @Override - public void update(PrivilegedDatabaseDto database, PrivilegedUserDto user, AccessTypeDto access) + public void update(DatabaseDto database, UserDto user, AccessTypeDto access) throws DatabaseMalformedException, SQLException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { /* grant access */ @@ -96,9 +96,9 @@ public class AccessServiceMariaDbImpl extends HibernateConnector implements Acce } @Override - public void delete(PrivilegedDatabaseDto database, PrivilegedUserDto user) throws DatabaseMalformedException, + public void delete(DatabaseDto database, UserDto user) throws DatabaseMalformedException, SQLException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { /* revoke access */ @@ -109,7 +109,7 @@ public class AccessServiceMariaDbImpl extends HibernateConnector implements Acce /* apply access rights */ start = System.currentTimeMillis(); connection.prepareStatement(mariaDbMapper.databaseFlushPrivilegesQuery()) - .execute(); + .execute(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceMariaDbImpl.java new file mode 100644 index 0000000000..fc9b2d97c3 --- /dev/null +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceMariaDbImpl.java @@ -0,0 +1,103 @@ +package at.tuwien.service.impl; + +import at.tuwien.api.container.ContainerDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.internal.CreateDatabaseDto; +import at.tuwien.api.user.UserBriefDto; +import at.tuwien.config.RabbitConfig; +import at.tuwien.exception.DatabaseMalformedException; +import at.tuwien.exception.QueryStoreCreateException; +import at.tuwien.mapper.MariaDbMapper; +import at.tuwien.service.ContainerService; +import com.mchange.v2.c3p0.ComboPooledDataSource; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.sql.Connection; +import java.sql.SQLException; + +@Log4j2 +@Service +public class ContainerServiceMariaDbImpl extends DataConnector<ContainerDto> implements ContainerService { + + private final RabbitConfig rabbitConfig; + private final MariaDbMapper mariaDbMapper; + + @Autowired + public ContainerServiceMariaDbImpl(RabbitConfig rabbitConfig, MariaDbMapper mariaDbMapper) { + this.rabbitConfig = rabbitConfig; + this.mariaDbMapper = mariaDbMapper; + } + + @Override + public DatabaseDto createDatabase(ContainerDto container, CreateDatabaseDto data) throws SQLException, + DatabaseMalformedException { + final ComboPooledDataSource dataSource = getDataSource(container); + final Connection connection = dataSource.getConnection(); + try { + /* create database if not exists */ + final long start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.databaseCreateDatabaseQuery(data.getInternalName())) + .execute(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + log.error("Failed to create database access: {}", e.getMessage()); + throw new DatabaseMalformedException("Failed to create database access: " + e.getMessage(), e); + } finally { + dataSource.close(); + } + log.info("Created database with name {}", data.getInternalName()); + return DatabaseDto.builder() + .internalName(data.getInternalName()) + .exchangeName(rabbitConfig.getExchangeName()) + .owner(UserBriefDto.builder() + .id(data.getUserId()) + .build()) + .contact(UserBriefDto.builder() + .id(data.getUserId()) + .build()) + .container(container) + .build(); + } + + @Override + public void createQueryStore(ContainerDto container, String databaseName) throws SQLException, + QueryStoreCreateException { + final ComboPooledDataSource dataSource = getDataSource(container, databaseName); + final Connection connection = dataSource.getConnection(); + try { + /* create query store */ + long start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreCreateSequenceRawQuery()) + .execute(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreCreateTableRawQuery()) + .execute(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreCreateHashTableProcedureRawQuery()) + .execute(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreCreateStoreQueryProcedureRawQuery()) + .execute(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.queryStoreCreateInternalStoreQueryProcedureRawQuery()) + .execute(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + log.error("Failed to create query store: {}", e.getMessage()); + throw new QueryStoreCreateException("Failed to create query store: " + e.getMessage(), e); + } finally { + dataSource.close(); + } + log.info("Created query store in database with name {}", databaseName); + } +} diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/CredentialServiceImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/CredentialServiceImpl.java index 05b9e0a3fe..fbc800bf47 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/CredentialServiceImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/CredentialServiceImpl.java @@ -1,11 +1,11 @@ package at.tuwien.service.impl; -import at.tuwien.api.container.internal.PrivilegedContainerDto; +import at.tuwien.api.container.ContainerDto; import at.tuwien.api.database.DatabaseAccessDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; -import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.ViewDto; +import at.tuwien.api.database.table.TableDto; +import at.tuwien.api.user.UserDto; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.service.CredentialService; @@ -21,19 +21,19 @@ import java.util.UUID; public class CredentialServiceImpl implements CredentialService { private final MetadataServiceGateway gateway; - private final Cache<UUID, PrivilegedUserDto> userCache; - private final Cache<Long, PrivilegedViewDto> viewCache; + private final Cache<UUID, UserDto> userCache; + private final Cache<Long, ViewDto> viewCache; private final Cache<Long, DatabaseAccessDto> accessCache; - private final Cache<Long, PrivilegedTableDto> tableCache; - private final Cache<Long, PrivilegedDatabaseDto> databaseCache; - private final Cache<Long, PrivilegedContainerDto> containerCache; + private final Cache<Long, TableDto> tableCache; + private final Cache<Long, DatabaseDto> databaseCache; + private final Cache<Long, ContainerDto> containerCache; @Autowired - public CredentialServiceImpl(MetadataServiceGateway gateway, Cache<UUID, PrivilegedUserDto> userCache, - Cache<Long, PrivilegedViewDto> viewCache, Cache<Long, DatabaseAccessDto> accessCache, - Cache<Long, PrivilegedTableDto> tableCache, - Cache<Long, PrivilegedDatabaseDto> databaseCache, - Cache<Long, PrivilegedContainerDto> containerCache) { + public CredentialServiceImpl(MetadataServiceGateway gateway, Cache<UUID, UserDto> userCache, + Cache<Long, ViewDto> viewCache, Cache<Long, DatabaseAccessDto> accessCache, + Cache<Long, TableDto> tableCache, + Cache<Long, DatabaseDto> databaseCache, + Cache<Long, ContainerDto> containerCache) { this.gateway = gateway; this.userCache = userCache; this.viewCache = viewCache; @@ -44,29 +44,29 @@ public class CredentialServiceImpl implements CredentialService { } @Override - public PrivilegedDatabaseDto getDatabase(Long id) throws DatabaseNotFoundException, RemoteUnavailableException, + public DatabaseDto getDatabase(Long id) throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException { - final PrivilegedDatabaseDto cacheDatabase = databaseCache.getIfPresent(id); + final DatabaseDto cacheDatabase = databaseCache.getIfPresent(id); if (cacheDatabase != null) { log.trace("found database with id {} in cache", id); return cacheDatabase; } log.debug("database with id {} not it cache (anymore): reload from metadata service", id); - final PrivilegedDatabaseDto database = gateway.getDatabaseById(id); + final DatabaseDto database = gateway.getDatabaseById(id); databaseCache.put(id, database); return database; } @Override - public PrivilegedTableDto getTable(Long databaseId, Long tableId) throws RemoteUnavailableException, + public TableDto getTable(Long databaseId, Long tableId) throws RemoteUnavailableException, MetadataServiceException, TableNotFoundException { - final PrivilegedTableDto cacheTable = tableCache.getIfPresent(tableId); + final TableDto cacheTable = tableCache.getIfPresent(tableId); if (cacheTable != null) { log.trace("found table with id {} in cache", tableId); return cacheTable; } log.debug("table with id {} not it cache (anymore): reload from metadata service", tableId); - final PrivilegedTableDto table = gateway.getTableById(databaseId, tableId); + final TableDto table = gateway.getTableById(databaseId, tableId); tableCache.put(tableId, table); return table; } @@ -78,43 +78,43 @@ public class CredentialServiceImpl implements CredentialService { } @Override - public PrivilegedContainerDto getContainer(Long id) throws RemoteUnavailableException, MetadataServiceException, + public ContainerDto getContainer(Long id) throws RemoteUnavailableException, MetadataServiceException, ContainerNotFoundException { - final PrivilegedContainerDto cacheContainer = containerCache.getIfPresent(id); + final ContainerDto cacheContainer = containerCache.getIfPresent(id); if (cacheContainer != null) { log.trace("found container with id {} in cache", id); return cacheContainer; } log.debug("container with id {} not it cache (anymore): reload from metadata service", id); - final PrivilegedContainerDto container = gateway.getContainerById(id); + final ContainerDto container = gateway.getContainerById(id); containerCache.put(id, container); return container; } @Override - public PrivilegedViewDto getView(Long databaseId, Long viewId) throws RemoteUnavailableException, + public ViewDto getView(Long databaseId, Long viewId) throws RemoteUnavailableException, MetadataServiceException, ViewNotFoundException { - final PrivilegedViewDto cacheView = viewCache.getIfPresent(viewId); + final ViewDto cacheView = viewCache.getIfPresent(viewId); if (cacheView != null) { log.trace("found view with id {} in cache", viewId); return cacheView; } log.debug("view with id {} not it cache (anymore): reload from metadata service", viewId); - final PrivilegedViewDto view = gateway.getViewById(databaseId, viewId); + final ViewDto view = gateway.getViewById(databaseId, viewId); viewCache.put(viewId, view); return view; } @Override - public PrivilegedUserDto getUser(UUID id) throws RemoteUnavailableException, MetadataServiceException, + public UserDto getUser(UUID id) throws RemoteUnavailableException, MetadataServiceException, UserNotFoundException { - final PrivilegedUserDto cacheUser = userCache.getIfPresent(id); + final UserDto cacheUser = userCache.getIfPresent(id); if (cacheUser != null) { log.trace("found user with id {} in cache", id); return cacheUser; } log.debug("user with id {} not it cache (anymore): reload from metadata service", id); - final PrivilegedUserDto user = gateway.getPrivilegedUserById(id); + final UserDto user = gateway.getUserById(id); userCache.put(id, user); return user; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java new file mode 100644 index 0000000000..9fbee30fa6 --- /dev/null +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java @@ -0,0 +1,66 @@ +package at.tuwien.service.impl; + +import at.tuwien.api.CacheableDto; +import com.mchange.v2.c3p0.ComboPooledDataSource; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +public abstract class DataConnector<T extends CacheableDto> { + + public ComboPooledDataSource getDataSource(T entity) { + final long start = System.currentTimeMillis(); + final ComboPooledDataSource dataSource = new ComboPooledDataSource(); + dataSource.setJdbcUrl(getJdbcUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPassword(), + entity.getDatabase())); + dataSource.setUser(entity.getUsername()); + dataSource.setPassword(entity.getPassword()); + dataSource.setInitialPoolSize(5); + dataSource.setMinPoolSize(5); + dataSource.setAcquireIncrement(5); + dataSource.setMaxPoolSize(20); + dataSource.setMaxStatements(100); + return dataSource; + } + + public ComboPooledDataSource getDataSource(T entity, String databaseName) { + final long start = System.currentTimeMillis(); + final ComboPooledDataSource dataSource = new ComboPooledDataSource(); + dataSource.setJdbcUrl(getJdbcUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPassword(), databaseName)); + dataSource.setUser(entity.getUsername()); + dataSource.setPassword(entity.getPassword()); + dataSource.setInitialPoolSize(5); + dataSource.setMinPoolSize(5); + dataSource.setAcquireIncrement(5); + dataSource.setMaxPoolSize(20); + dataSource.setMaxStatements(100); + return dataSource; + } + + public String getSparkUrl(String jdbcMethod, String host, String password, String databaseName) { + final StringBuilder sb = new StringBuilder(getJdbcUrl(jdbcMethod, host, password, databaseName)) + .append("?sessionVariables=sql_mode='ANSI_QUOTES'"); + log.trace("mapped container to spark url: {}", sb.toString()); + return sb.toString(); + } + + public String getSparkUrl(T entity) { + return getSparkUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPassword(), entity.getDatabase()); + } + + public String getJdbcUrl(String jdbcMethod, String host, String password, String databaseName) { + final StringBuilder stringBuilder = new StringBuilder("jdbc:") + .append(jdbcMethod) + .append("://") + .append(host) + .append(":") + .append(password); + if (databaseName != null) { + stringBuilder.append("/") + .append(databaseName); + } + return stringBuilder.toString(); + } + +} diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java index a15c22f55e..2b5af71d1c 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java @@ -1,72 +1,353 @@ package at.tuwien.service.impl; -import at.tuwien.api.container.internal.PrivilegedContainerDto; -import at.tuwien.api.database.internal.CreateDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.user.UserBriefDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.ViewCreateDto; +import at.tuwien.api.database.ViewDto; +import at.tuwien.api.database.table.TableDto; +import at.tuwien.api.database.table.constraints.unique.UniqueDto; +import at.tuwien.api.database.table.internal.TableCreateDto; import at.tuwien.api.user.internal.UpdateUserPasswordDto; -import at.tuwien.config.RabbitConfig; -import at.tuwien.exception.DatabaseMalformedException; +import at.tuwien.config.QueryConfig; +import at.tuwien.exception.*; +import at.tuwien.mapper.DataMapper; import at.tuwien.mapper.MariaDbMapper; +import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.DatabaseService; +import at.tuwien.service.TableService; +import at.tuwien.service.ViewService; +import com.google.common.hash.Hashing; import com.mchange.v2.c3p0.ComboPooledDataSource; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; +import java.nio.charset.StandardCharsets; import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; +import java.util.LinkedList; +import java.util.List; @Log4j2 @Service -public class DatabaseServiceMariaDbImpl extends HibernateConnector implements DatabaseService { +public class DatabaseServiceMariaDbImpl extends DataConnector<DatabaseDto> implements DatabaseService { - private final RabbitConfig rabbitConfig; + private final DataMapper dataMapper; + private final QueryConfig queryConfig; + private final ViewService viewService; + private final TableService tableService; private final MariaDbMapper mariaDbMapper; + private final MetadataMapper metadataMapper; @Autowired - public DatabaseServiceMariaDbImpl(RabbitConfig rabbitConfig, MariaDbMapper mariaDbMapper) { - this.rabbitConfig = rabbitConfig; + public DatabaseServiceMariaDbImpl(DataMapper dataMapper, QueryConfig queryConfig, ViewService viewService, + TableService tableService, MariaDbMapper mariaDbMapper, + @Qualifier("metadataMapper") MetadataMapper metadataMapper) { + this.dataMapper = dataMapper; + this.queryConfig = queryConfig; + this.viewService = viewService; + this.tableService = tableService; this.mariaDbMapper = mariaDbMapper; + this.metadataMapper = metadataMapper; } @Override - public PrivilegedDatabaseDto create(PrivilegedContainerDto container, CreateDatabaseDto data) throws SQLException, - DatabaseMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(container, null); + public ViewDto inspectView(DatabaseDto database, String viewName) throws SQLException, ViewNotFoundException { + final ComboPooledDataSource dataSource = getDataSource(database); + final Connection connection = dataSource.getConnection(); + try { + /* obtain only view metadata */ + long start = System.currentTimeMillis(); + final PreparedStatement statement1 = connection.prepareStatement(mariaDbMapper.databaseViewSelectRawQuery()); + statement1.setString(1, database.getInternalName()); + statement1.setString(2, viewName); + log.trace("1={}, 2={}", database.getInternalName(), viewName); + final ResultSet resultSet1 = statement1.executeQuery(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + if (!resultSet1.next()) { + throw new ViewNotFoundException("Failed to find view in the information schema"); + } + final ViewDto view = dataMapper.schemaResultSetToView(database, resultSet1); + view.setVdbid(database.getId()); + view.setOwner(database.getOwner()); + /* obtain view columns */ + start = System.currentTimeMillis(); + final PreparedStatement statement2 = connection.prepareStatement(mariaDbMapper.databaseTableColumnsSelectRawQuery()); + statement2.setString(1, database.getInternalName()); + statement2.setString(2, view.getInternalName()); + log.trace("1={}, 2={}", database.getInternalName(), view.getInternalName()); + final ResultSet resultSet2 = statement2.executeQuery(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + TableDto tmp = TableDto.builder() + .columns(new LinkedList<>()) + .build(); + while (resultSet2.next()) { + tmp = dataMapper.resultSetToTable(resultSet2, tmp); + } + view.setColumns(tmp.getColumns() + .stream() + .map(metadataMapper::columnDtoToViewColumnDto) + .toList()); + view.getColumns() + .forEach(column -> column.setDatabaseId(database.getId())); + log.debug("obtained metadata for view {}.{}", database.getInternalName(), view.getInternalName()); + return view; + } finally { + dataSource.close(); + } + } + + @Override + public TableDto createTable(DatabaseDto database, TableCreateDto data) throws SQLException, + TableMalformedException, TableExistsException { + final String tableName = mariaDbMapper.nameToInternalName(data.getName()); + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { - /* create database if not exists */ + /* create table if not exists */ final long start = System.currentTimeMillis(); - connection.prepareStatement(mariaDbMapper.databaseCreateDatabaseQuery(data.getInternalName())) + connection.prepareStatement(mariaDbMapper.tableCreateDtoToCreateTableRawQuery(data)) .execute(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); } catch (SQLException e) { connection.rollback(); - log.error("Failed to create database access: {}", e.getMessage()); - throw new DatabaseMalformedException("Failed to create database access: " + e.getMessage(), e); + if (e.getMessage().contains("already exists")) { + log.error("Failed to create table: already exists"); + throw new TableExistsException("Failed to create table: already exists", e); + } + log.error("Failed to create table: {}", e.getMessage()); + throw new TableMalformedException("Failed to create table: " + e.getMessage(), e); + } finally { + dataSource.close(); + } + log.info("Created table with name {}", tableName); + final TableDto table = metadataMapper.databaseDtoToTableDto(database); + table.setInternalName(tableName); + return table; + } + + @Override + public Boolean existsView(DatabaseDto database, String viewName) throws SQLException, + QueryMalformedException { + final ComboPooledDataSource dataSource = getDataSource(database); + final Connection connection = dataSource.getConnection(); + final Boolean queryResult; + try { + /* find view data */ + final long start = System.currentTimeMillis(); + final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.selectExistsTableOrViewRawQuery()); + statement.setString(1, database.getInternalName()); + statement.setString(2, viewName); + final ResultSet resultSet = statement.executeQuery(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + queryResult = mariaDbMapper.resultSetToBoolean(resultSet); + } catch (SQLException e) { + log.error("Failed to prepare statement {}", e.getMessage()); + throw new QueryMalformedException("Failed to prepare statement: " + e.getMessage(), e); } finally { dataSource.close(); } - log.info("Created database with name {}", data.getInternalName()); - return PrivilegedDatabaseDto.builder() - .internalName(data.getInternalName()) - .exchangeName(rabbitConfig.getExchangeName()) - .owner(UserBriefDto.builder() - .id(data.getUserId()) - .build()) - .contact(UserBriefDto.builder() - .id(data.getUserId()) - .build()) - .container(container) + return queryResult; + } + + @Override + public ViewDto createView(DatabaseDto database, ViewCreateDto data) throws SQLException, + ViewMalformedException { + final ComboPooledDataSource dataSource = getDataSource(database); + final Connection connection = dataSource.getConnection(); + ViewDto view = ViewDto.builder() + .name(data.getName()) + .internalName(mariaDbMapper.nameToInternalName(data.getName())) + .query(data.getQuery()) + .queryHash(Hashing.sha256() + .hashString(data.getQuery(), StandardCharsets.UTF_8) + .toString()) + .isPublic(database.getIsPublic()) + .owner(database.getOwner()) + .identifiers(new LinkedList<>()) + .isInitialView(false) + .vdbid(database.getId()) + .columns(new LinkedList<>()) .build(); + try { + /* create view if not exists */ + final long start = System.currentTimeMillis(); + connection.prepareStatement(mariaDbMapper.viewCreateRawQuery(view.getInternalName(), data.getQuery())) + .execute(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + /* select view columns */ + final PreparedStatement statement2 = connection.prepareStatement(mariaDbMapper.databaseTableColumnsSelectRawQuery()); + statement2.setString(1, database.getInternalName()); + statement2.setString(2, view.getInternalName()); + final ResultSet resultSet2 = statement2.executeQuery(); + while (resultSet2.next()) { + view = dataMapper.resultSetToTable(resultSet2, view, queryConfig); + } + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + log.error("Failed to create view: {}", e.getMessage()); + throw new ViewMalformedException("Failed to create view: " + e.getMessage(), e); + } finally { + dataSource.close(); + } + log.info("Created view with name {}", view.getName()); + return view; + } + + @Override + public List<ViewDto> exploreViews(DatabaseDto database) throws SQLException, DatabaseMalformedException, + ViewNotFoundException { + final ComboPooledDataSource dataSource = getDataSource(database); + final Connection connection = dataSource.getConnection(); + final List<ViewDto> views = new LinkedList<>(); + try { + /* inspect tables before views */ + final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.databaseViewsSelectRawQuery()); + statement.setString(1, database.getInternalName()); + final long start = System.currentTimeMillis(); + final ResultSet resultSet1 = statement.executeQuery(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + while (resultSet1.next()) { + final String viewName = resultSet1.getString(1); + if (viewName.length() == 64) { + log.trace("view {}.{} seems to be a subset view (name length = 64), skip.", database.getInternalName(), viewName); + continue; + } + if (database.getViews().stream().anyMatch(v -> v.getInternalName().equals(viewName))) { + log.trace("view {}.{} already known to metadata database, skip.", database.getInternalName(), viewName); + continue; + } + if (database.getTables().stream().noneMatch(t -> t.getInternalName().equals(viewName))) { + views.add(inspectView(database, viewName)); + } + } + } catch (SQLException e) { + log.error("Failed to get view schemas: {}", e.getMessage()); + throw new DatabaseMalformedException("Failed to get view schemas: " + e.getMessage(), e); + } finally { + dataSource.close(); + } + log.info("Found {} view schema(s)", views.size()); + return views; + } + + @Override + public List<TableDto> exploreTables(DatabaseDto database) throws SQLException, TableNotFoundException, + DatabaseMalformedException { + final ComboPooledDataSource dataSource = getDataSource(database); + final Connection connection = dataSource.getConnection(); + final List<TableDto> tables = new LinkedList<>(); + try { + /* inspect tables before views */ + final long start = System.currentTimeMillis(); + final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.databaseTablesSelectRawQuery()); + statement.setString(1, database.getInternalName()); + final ResultSet resultSet1 = statement.executeQuery(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + while (resultSet1.next()) { + final String tableName = resultSet1.getString(1); + if (database.getTables().stream().anyMatch(t -> t.getInternalName().equals(tableName))) { + log.trace("view {}.{} already known to metadata database, skip.", database.getInternalName(), tableName); + continue; + } + final TableDto table = inspectTable(database, tableName); + if (database.getTables().stream().noneMatch(t -> t.getInternalName().equals(tableName))) { + tables.add(table); + } + } + } catch (SQLException e) { + log.error("Failed to get table schemas: {}", e.getMessage()); + throw new DatabaseMalformedException("Failed to get table schemas: " + e.getMessage(), e); + } finally { + dataSource.close(); + } + log.info("Found {} table schema(s)", tables.size()); + return tables; + } + + @Override + public TableDto inspectTable(DatabaseDto database, String tableName) throws SQLException, TableNotFoundException { + log.trace("inspecting table: {}.{}", database.getInternalName(), tableName); + final ComboPooledDataSource dataSource = getDataSource(database); + final Connection connection = dataSource.getConnection(); + try { + /* obtain only table metadata */ + long start = System.currentTimeMillis(); + final PreparedStatement statement1 = connection.prepareStatement(mariaDbMapper.databaseTableSelectRawQuery()); + statement1.setString(1, database.getInternalName()); + statement1.setString(2, tableName); + log.trace("1={}, 2={}", database.getInternalName(), tableName); + TableDto table = dataMapper.schemaResultSetToTable(database, statement1.executeQuery()); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + /* obtain columns metadata */ + start = System.currentTimeMillis(); + final PreparedStatement statement2 = connection.prepareStatement(mariaDbMapper.databaseTableColumnsSelectRawQuery()); + statement2.setString(1, database.getInternalName()); + statement2.setString(2, tableName); + log.trace("1={}, 2={}", database.getInternalName(), tableName); + final ResultSet resultSet2 = statement2.executeQuery(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + while (resultSet2.next()) { + table = dataMapper.resultSetToTable(resultSet2, table); + } + /* obtain check constraints metadata */ + start = System.currentTimeMillis(); + final PreparedStatement statement3 = connection.prepareStatement(mariaDbMapper.columnsCheckConstraintSelectRawQuery()); + statement3.setString(1, database.getInternalName()); + statement3.setString(2, tableName); + log.trace("1={}, 2={}", database.getInternalName(), tableName); + final ResultSet resultSet3 = statement3.executeQuery(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + while (resultSet3.next()) { + final String clause = resultSet3.getString(1); + table.getConstraints() + .getChecks() + .add(clause); + log.trace("found check clause: {}", clause); + } + /* obtain column constraints metadata */ + start = System.currentTimeMillis(); + final PreparedStatement statement4 = connection.prepareStatement(mariaDbMapper.databaseTableConstraintsSelectRawQuery()); + statement4.setString(1, database.getInternalName()); + statement4.setString(2, tableName); + log.trace("1={}, 2={}", database.getInternalName(), tableName); + final ResultSet resultSet4 = statement4.executeQuery(); + log.trace("executed statement in {} ms", System.currentTimeMillis() - start); + while (resultSet4.next()) { + table = dataMapper.resultSetToConstraint(resultSet4, table); + for (UniqueDto uk : table.getConstraints().getUniques()) { + uk.setTable(metadataMapper.tableDtoToTableBriefDto(table)); + final TableDto tmpTable = table; + uk.getColumns() + .forEach(column -> { + column.setTableId(tmpTable.getId()); + column.setDatabaseId(database.getId()); + }); + } + } + table.setTdbid(database.getId()); + table.setOwner(database.getOwner()); + final TableDto tmpTable = table; + tmpTable.getColumns() + .forEach(column -> { + column.setTableId(tmpTable.getId()); + column.setDatabaseId(database.getId()); + }); + log.debug("obtained metadata for table {}.{}", database.getInternalName(), tableName); + return tmpTable; + } finally { + dataSource.close(); + } } @Override - public void update(PrivilegedDatabaseDto database, UpdateUserPasswordDto data) throws SQLException, + public void update(DatabaseDto database, UpdateUserPasswordDto data) throws SQLException, DatabaseMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { /* update user password */ diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/HibernateConnector.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/HibernateConnector.java deleted file mode 100644 index 242cefd557..0000000000 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/HibernateConnector.java +++ /dev/null @@ -1,53 +0,0 @@ -package at.tuwien.service.impl; - -import at.tuwien.api.container.internal.PrivilegedContainerDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import com.mchange.v2.c3p0.ComboPooledDataSource; -import lombok.extern.log4j.Log4j2; -import org.springframework.stereotype.Service; - -@Log4j2 -@Service -public abstract class HibernateConnector { - - public ComboPooledDataSource getPrivilegedDataSource(PrivilegedContainerDto container, String databaseName) { - final long start = System.currentTimeMillis(); - final ComboPooledDataSource dataSource = new ComboPooledDataSource(); - dataSource.setJdbcUrl(url(container, databaseName)); - dataSource.setUser(container.getUsername()); - dataSource.setPassword(container.getPassword()); - dataSource.setInitialPoolSize(5); - dataSource.setMinPoolSize(5); - dataSource.setAcquireIncrement(5); - dataSource.setMaxPoolSize(20); - dataSource.setMaxStatements(100); - log.trace("created pooled data source {} in {} ms, user={}", url(container, databaseName), System.currentTimeMillis() - start, container.getUsername()); - return dataSource; - } - - public ComboPooledDataSource getPrivilegedDataSource(PrivilegedDatabaseDto database) { - return getPrivilegedDataSource(database.getContainer(), database.getInternalName()); - } - - public String getSparkUrl(PrivilegedContainerDto container, String databaseName) { - final StringBuilder sb = new StringBuilder(url(container, databaseName)) - .append("?sessionVariables=sql_mode='ANSI_QUOTES'"); - log.trace("mapped container to spark url: {}", sb.toString()); - return sb.toString(); - } - - private String url(PrivilegedContainerDto container, String databaseName) { - final StringBuilder stringBuilder = new StringBuilder("jdbc:") - .append(container.getImage().getJdbcMethod()) - .append("://") - .append(container.getHost()) - .append(":") - .append(container.getPort()); - if (databaseName != null) { - stringBuilder.append("/") - .append(databaseName); - } - return stringBuilder.toString(); - } - -} diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java index d5127e050e..1a26a84ef6 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java @@ -1,7 +1,7 @@ package at.tuwien.service.impl; +import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.columns.ColumnDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.mapper.DataMapper; import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.QueueService; @@ -18,7 +18,7 @@ import java.util.Optional; @Log4j2 @Service -public class QueueServiceRabbitMqImpl extends HibernateConnector implements QueueService { +public class QueueServiceRabbitMqImpl extends DataConnector<TableDto> implements QueueService { private final DataMapper dataMapper; private final MetadataMapper metadataMapper; @@ -30,13 +30,13 @@ public class QueueServiceRabbitMqImpl extends HibernateConnector implements Queu } @Override - public void insert(PrivilegedTableDto table, Map<String, Object> data) throws SQLException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); + public void insert(TableDto table, Map<String, Object> data) throws SQLException { + final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); try { final int[] idx = new int[]{1}; final PreparedStatement preparedStatement = connection.prepareStatement( - dataMapper.rabbitMqTupleToInsertOrUpdateQuery(metadataMapper.privilegedTableDtoToTableDto(table), data)); + dataMapper.rabbitMqTupleToInsertOrUpdateQuery(metadataMapper.tableDtoToTableDto(table), data)); for (Map.Entry<String, Object> entry : data.entrySet()) { final Optional<ColumnDto> optional = table.getColumns().stream().filter(c -> c.getInternalName().equals(entry.getKey())).findFirst(); if (optional.isEmpty()) { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java deleted file mode 100644 index e2b0c984e0..0000000000 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SchemaServiceMariaDbImpl.java +++ /dev/null @@ -1,164 +0,0 @@ -package at.tuwien.service.impl; - -import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.table.TableDto; -import at.tuwien.api.database.table.constraints.unique.UniqueDto; -import at.tuwien.exception.TableNotFoundException; -import at.tuwien.exception.ViewNotFoundException; -import at.tuwien.mapper.DataMapper; -import at.tuwien.mapper.MariaDbMapper; -import at.tuwien.mapper.MetadataMapper; -import at.tuwien.service.SchemaService; -import com.mchange.v2.c3p0.ComboPooledDataSource; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.LinkedList; - -@Log4j2 -@Service -public class SchemaServiceMariaDbImpl extends HibernateConnector implements SchemaService { - - private final DataMapper dataMapper; - private final MariaDbMapper mariaDbMapper; - private final MetadataMapper metadataMapper; - - @Autowired - public SchemaServiceMariaDbImpl(DataMapper dataMapper, MariaDbMapper mariaDbMapper, MetadataMapper metadataMapper) { - this.dataMapper = dataMapper; - this.mariaDbMapper = mariaDbMapper; - this.metadataMapper = metadataMapper; - } - - @Override - public TableDto inspectTable(PrivilegedDatabaseDto database, String tableName) throws SQLException, - TableNotFoundException { - log.trace("inspecting table: {}.{}", database.getInternalName(), tableName); - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); - final Connection connection = dataSource.getConnection(); - try { - /* obtain only table metadata */ - long start = System.currentTimeMillis(); - final PreparedStatement statement1 = connection.prepareStatement(mariaDbMapper.databaseTableSelectRawQuery()); - statement1.setString(1, database.getInternalName()); - statement1.setString(2, tableName); - log.trace("1={}, 2={}", database.getInternalName(), tableName); - TableDto table = dataMapper.schemaResultSetToTable(metadataMapper.privilegedDatabaseDtoToDatabaseDto(database), statement1.executeQuery()); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - /* obtain columns metadata */ - start = System.currentTimeMillis(); - final PreparedStatement statement2 = connection.prepareStatement(mariaDbMapper.databaseTableColumnsSelectRawQuery()); - statement2.setString(1, database.getInternalName()); - statement2.setString(2, tableName); - log.trace("1={}, 2={}", database.getInternalName(), tableName); - final ResultSet resultSet2 = statement2.executeQuery(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - while (resultSet2.next()) { - table = dataMapper.resultSetToTable(resultSet2, table); - } - /* obtain check constraints metadata */ - start = System.currentTimeMillis(); - final PreparedStatement statement3 = connection.prepareStatement(mariaDbMapper.columnsCheckConstraintSelectRawQuery()); - statement3.setString(1, database.getInternalName()); - statement3.setString(2, tableName); - log.trace("1={}, 2={}", database.getInternalName(), tableName); - final ResultSet resultSet3 = statement3.executeQuery(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - while (resultSet3.next()) { - final String clause = resultSet3.getString(1); - table.getConstraints() - .getChecks() - .add(clause); - log.trace("found check clause: {}", clause); - } - /* obtain column constraints metadata */ - start = System.currentTimeMillis(); - final PreparedStatement statement4 = connection.prepareStatement(mariaDbMapper.databaseTableConstraintsSelectRawQuery()); - statement4.setString(1, database.getInternalName()); - statement4.setString(2, tableName); - log.trace("1={}, 2={}", database.getInternalName(), tableName); - final ResultSet resultSet4 = statement4.executeQuery(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - while (resultSet4.next()) { - table = dataMapper.resultSetToConstraint(resultSet4, table); - for (UniqueDto uk : table.getConstraints().getUniques()) { - uk.setTable(metadataMapper.tableDtoToTableBriefDto(table)); - final TableDto tmpTable = table; - uk.getColumns() - .forEach(column -> { - column.setTableId(tmpTable.getId()); - column.setDatabaseId(database.getId()); - }); - } - } - table.setTdbid(database.getId()); - table.setOwner(database.getOwner()); - final TableDto tmpTable = table; - tmpTable.getColumns() - .forEach(column -> { - column.setTableId(tmpTable.getId()); - column.setDatabaseId(database.getId()); - }); - log.debug("obtained metadata for table {}.{}", database.getInternalName(), tableName); - return tmpTable; - } finally { - dataSource.close(); - } - } - - @Override - public ViewDto inspectView(PrivilegedDatabaseDto privilegedDatabase, String viewName) throws SQLException, - ViewNotFoundException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(privilegedDatabase); - final Connection connection = dataSource.getConnection(); - final DatabaseDto database = metadataMapper.privilegedDatabaseDtoToDatabaseDto(privilegedDatabase); - try { - /* obtain only view metadata */ - long start = System.currentTimeMillis(); - final PreparedStatement statement1 = connection.prepareStatement(mariaDbMapper.databaseViewSelectRawQuery()); - statement1.setString(1, database.getInternalName()); - statement1.setString(2, viewName); - log.trace("1={}, 2={}", database.getInternalName(), viewName); - final ResultSet resultSet1 = statement1.executeQuery(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - if (!resultSet1.next()) { - throw new ViewNotFoundException("Failed to find view in the information schema"); - } - ViewDto view = dataMapper.schemaResultSetToView(database, resultSet1); - view.setVdbid(database.getId()); - view.setOwner(database.getOwner()); - /* obtain view columns */ - start = System.currentTimeMillis(); - final PreparedStatement statement2 = connection.prepareStatement(mariaDbMapper.databaseTableColumnsSelectRawQuery()); - statement2.setString(1, database.getInternalName()); - statement2.setString(2, viewName); - log.trace("1={}, 2={}", database.getInternalName(), viewName); - final ResultSet resultSet2 = statement2.executeQuery(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - TableDto tmp = TableDto.builder() - .columns(new LinkedList<>()) - .build(); - while (resultSet2.next()) { - tmp = dataMapper.resultSetToTable(resultSet2, tmp); - } - view.setColumns(tmp.getColumns() - .stream() - .map(metadataMapper::columnDtoToViewColumnDto) - .toList()); - view.getColumns() - .forEach(column -> column.setDatabaseId(database.getId())); - log.debug("obtained metadata for view {}.{}", database.getInternalName(), viewName); - return view; - } finally { - dataSource.close(); - } - } - -} diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java index fb244bb301..62b7c77a84 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java @@ -1,7 +1,7 @@ package at.tuwien.service.impl; -import at.tuwien.api.container.internal.PrivilegedContainerDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.ViewCreateDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.identifier.IdentifierBriefDto; import at.tuwien.api.identifier.IdentifierTypeDto; @@ -10,6 +10,7 @@ import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.mapper.DataMapper; import at.tuwien.mapper.MariaDbMapper; import at.tuwien.mapper.MetadataMapper; +import at.tuwien.service.DatabaseService; import at.tuwien.service.SubsetService; import at.tuwien.service.TableService; import at.tuwien.service.ViewService; @@ -28,72 +29,41 @@ import java.util.UUID; @Log4j2 @Service -public class SubsetServiceMariaDbImpl extends HibernateConnector implements SubsetService { +public class SubsetServiceMariaDbImpl extends DataConnector<DatabaseDto> implements SubsetService { private final DataMapper dataMapper; private final ViewService viewService; private final TableService tableService; private final MariaDbMapper mariaDbMapper; private final MetadataMapper metadataMapper; + private final DatabaseService databaseService; private final MetadataServiceGateway metadataServiceGateway; @Autowired public SubsetServiceMariaDbImpl(DataMapper dataMapper, ViewService viewService, TableService tableService, MariaDbMapper mariaDbMapper, MetadataMapper metadataMapper, - MetadataServiceGateway metadataServiceGateway) { + DatabaseService databaseService, MetadataServiceGateway metadataServiceGateway) { this.dataMapper = dataMapper; this.viewService = viewService; this.tableService = tableService; this.mariaDbMapper = mariaDbMapper; this.metadataMapper = metadataMapper; + this.databaseService = databaseService; this.metadataServiceGateway = metadataServiceGateway; } @Override - public void createQueryStore(PrivilegedContainerDto container, String databaseName) throws SQLException, - QueryStoreCreateException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(container, databaseName); - final Connection connection = dataSource.getConnection(); - try { - /* create query store */ - long start = System.currentTimeMillis(); - connection.prepareStatement(mariaDbMapper.queryStoreCreateSequenceRawQuery()) - .execute(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - start = System.currentTimeMillis(); - connection.prepareStatement(mariaDbMapper.queryStoreCreateTableRawQuery()) - .execute(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - start = System.currentTimeMillis(); - connection.prepareStatement(mariaDbMapper.queryStoreCreateHashTableProcedureRawQuery()) - .execute(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - start = System.currentTimeMillis(); - connection.prepareStatement(mariaDbMapper.queryStoreCreateStoreQueryProcedureRawQuery()) - .execute(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - start = System.currentTimeMillis(); - connection.prepareStatement(mariaDbMapper.queryStoreCreateInternalStoreQueryProcedureRawQuery()) - .execute(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - connection.commit(); - } catch (SQLException e) { - connection.rollback(); - log.error("Failed to create query store: {}", e.getMessage()); - throw new QueryStoreCreateException("Failed to create query store: " + e.getMessage(), e); - } finally { - dataSource.close(); - } - log.info("Created query store in database with name {}", databaseName); - } - - @Override - public Dataset<Row> getData(PrivilegedDatabaseDto database, QueryDto subset, Long page, Long size) + public Dataset<Row> getData(DatabaseDto database, QueryDto subset, Long page, Long size) throws ViewMalformedException, SQLException, QueryMalformedException, TableNotFoundException { final String viewName = metadataMapper.queryDtoToViewName(subset); - if (!viewService.existsByName(database, viewName)) { + if (!databaseService.existsView(database, viewName)) { log.warn("Missing internal view {} for subset with id {}: create it from subset query", viewName, subset.getId()); - viewService.create(database, subset); + databaseService.createView(database, ViewCreateDto.builder() + .isPublic(false) + .isSchemaPublic(false) + .name(viewName) + .query(subset.getQuery()) + .build()); } else { log.debug("internal view {}.{} for subset with id {} exists", database.getInternalName(), viewName, subset.getId()); } @@ -101,22 +71,22 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs } @Override - public Long create(PrivilegedDatabaseDto database, String statement, Instant timestamp, UUID userId) + public Long create(DatabaseDto database, String statement, Instant timestamp, UUID userId) throws QueryStoreInsertException, SQLException { return storeQuery(database, statement, timestamp, userId); } @Override - public Long reExecuteCount(PrivilegedDatabaseDto database, QueryDto query) throws TableMalformedException, + public Long reExecuteCount(DatabaseDto database, QueryDto query) throws TableMalformedException, SQLException, QueryMalformedException { return executeCountNonPersistent(database, query.getQuery(), query.getExecution()); } @Override - public List<QueryDto> findAll(PrivilegedDatabaseDto database, Boolean filterPersisted) throws SQLException, + public List<QueryDto> findAll(DatabaseDto database, Boolean filterPersisted) throws SQLException, QueryNotFoundException, RemoteUnavailableException, DatabaseNotFoundException, MetadataServiceException { final List<IdentifierBriefDto> identifiers = metadataServiceGateway.getIdentifiers(database.getId(), null); - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { final long start = System.currentTimeMillis(); @@ -147,9 +117,9 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs } @Override - public Long executeCountNonPersistent(PrivilegedDatabaseDto database, String statement, Instant timestamp) + public Long executeCountNonPersistent(DatabaseDto database, String statement, Instant timestamp) throws SQLException, QueryMalformedException, TableMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { final long start = System.currentTimeMillis(); @@ -166,9 +136,9 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs } @Override - public QueryDto findById(PrivilegedDatabaseDto database, Long queryId) throws QueryNotFoundException, SQLException, + public QueryDto findById(DatabaseDto database, Long queryId) throws QueryNotFoundException, SQLException, RemoteUnavailableException, DatabaseNotFoundException, MetadataServiceException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { final long start = System.currentTimeMillis(); @@ -193,11 +163,11 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs } @Override - public Long storeQuery(PrivilegedDatabaseDto database, String query, Instant timestamp, UUID userId) throws SQLException, + public Long storeQuery(DatabaseDto database, String query, Instant timestamp, UUID userId) throws SQLException, QueryStoreInsertException { /* save */ final Long queryId; - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { /* insert query into query store */ @@ -228,9 +198,9 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs } @Override - public void persist(PrivilegedDatabaseDto database, Long queryId, Boolean persist) throws SQLException, + public void persist(DatabaseDto database, Long queryId, Boolean persist) throws SQLException, QueryStorePersistException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { /* update query */ @@ -250,8 +220,8 @@ public class SubsetServiceMariaDbImpl extends HibernateConnector implements Subs } @Override - public void deleteStaleQueries(PrivilegedDatabaseDto database) throws SQLException, QueryStoreGCException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + public void deleteStaleQueries(DatabaseDto database) throws SQLException, QueryStoreGCException { + final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); try { final long start = System.currentTimeMillis(); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java index c34f057e01..6586c8ba42 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java @@ -1,18 +1,16 @@ package at.tuwien.service.impl; import at.tuwien.api.SortTypeDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.table.*; 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.internal.PrivilegedTableDto; -import at.tuwien.api.database.table.internal.TableCreateDto; import at.tuwien.exception.*; import at.tuwien.mapper.DataMapper; import at.tuwien.mapper.MariaDbMapper; -import at.tuwien.service.SchemaService; +import at.tuwien.service.DatabaseService; import at.tuwien.service.StorageService; import at.tuwien.service.TableService; import at.tuwien.utils.MariaDbUtil; @@ -28,66 +26,35 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.time.Instant; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; @Log4j2 @Service -public class TableServiceMariaDbImpl extends HibernateConnector implements TableService { +public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements TableService { private final DataMapper dataMapper; private final SparkSession sparkSession; private final MariaDbMapper mariaDbMapper; - private final SchemaService schemaService; private final StorageService storageService; + private final DatabaseService databaseService; @Autowired public TableServiceMariaDbImpl(DataMapper dataMapper, SparkSession sparkSession, MariaDbMapper mariaDbMapper, - SchemaService schemaService, StorageService storageService) { + StorageService storageService, DatabaseService databaseService) { this.dataMapper = dataMapper; this.sparkSession = sparkSession; this.mariaDbMapper = mariaDbMapper; - this.schemaService = schemaService; this.storageService = storageService; + this.databaseService = databaseService; } @Override - public List<TableDto> getSchemas(PrivilegedDatabaseDto database) throws SQLException, TableNotFoundException, - DatabaseMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); - final Connection connection = dataSource.getConnection(); - final List<TableDto> tables = new LinkedList<>(); - try { - /* inspect tables before views */ - final long start = System.currentTimeMillis(); - final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.databaseTablesSelectRawQuery()); - statement.setString(1, database.getInternalName()); - final ResultSet resultSet1 = statement.executeQuery(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - while (resultSet1.next()) { - final String tableName = resultSet1.getString(1); - if (database.getTables().stream().anyMatch(t -> t.getInternalName().equals(tableName))) { - log.trace("view {}.{} already known to metadata database, skip.", database.getInternalName(), tableName); - continue; - } - final TableDto table = schemaService.inspectTable(database, tableName); - if (database.getTables().stream().noneMatch(t -> t.getInternalName().equals(table.getInternalName()))) { - tables.add(table); - } - } - } catch (SQLException e) { - log.error("Failed to get table schemas: {}", e.getMessage()); - throw new DatabaseMalformedException("Failed to get table schemas: " + e.getMessage(), e); - } finally { - dataSource.close(); - } - log.info("Found {} table schema(s)", tables.size()); - return tables; - } - - @Override - public TableStatisticDto getStatistics(PrivilegedTableDto table) throws SQLException, TableMalformedException, + public TableStatisticDto getStatistics(DatabaseDto database, TableDto table) throws SQLException, TableMalformedException, TableNotFoundException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); + final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); final TableStatisticDto statistic; try { @@ -95,14 +62,14 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final long start = System.currentTimeMillis(); final String query = mariaDbMapper.tableColumnStatisticsSelectRawQuery(table.getColumns(), table.getInternalName()); if (query == null) { - log.debug("table {}.{} does not have columns that can be analysed for statistical properties (i.e. no numeric columns)", table.getDatabase().getInternalName(), table.getInternalName()); + log.debug("table {}.{} does not have columns that can be analysed for statistical properties (i.e. no numeric columns)", database.getInternalName(), table.getInternalName()); statistic = null; } else { final ResultSet resultSet = connection.prepareStatement(query) .executeQuery(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); statistic = dataMapper.resultSetToTableStatistic(resultSet); - final TableDto tmpTable = schemaService.inspectTable(table.getDatabase(), table.getInternalName()); + final TableDto tmpTable = databaseService.inspectTable(database, table.getInternalName()); statistic.setAvgRowLength(tmpTable.getAvgRowLength()); statistic.setDataLength(tmpTable.getDataLength()); statistic.setMaxDataLength(tmpTable.getMaxDataLength()); @@ -126,44 +93,9 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table } @Override - public TableDto find(PrivilegedDatabaseDto database, String tableName) throws TableNotFoundException, SQLException { - return schemaService.inspectTable(database, tableName); - } - - @Override - public TableDto createTable(PrivilegedDatabaseDto database, TableCreateDto data) throws SQLException, - TableMalformedException, TableExistsException, TableNotFoundException { - final String tableName = mariaDbMapper.nameToInternalName(data.getName()); - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); - final Connection connection = dataSource.getConnection(); - try { - /* create table if not exists */ - final long start = System.currentTimeMillis(); - connection.prepareStatement(mariaDbMapper.tableCreateDtoToCreateTableRawQuery(data)) - .execute(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - connection.commit(); - } catch (SQLException e) { - connection.rollback(); - if (e.getMessage().contains("already exists")) { - log.error("Failed to create table: already exists"); - throw new TableExistsException("Failed to create table: already exists", e); - } - log.error("Failed to create table: {}", e.getMessage()); - throw new TableMalformedException("Failed to create table: " + e.getMessage(), e); - } finally { - dataSource.close(); - } - log.info("Created table with name {}", tableName); - final TableDto table = find(database, tableName); - table.setName(data.getName()); - return table; - } - - @Override - public void updateTable(PrivilegedTableDto table, TableUpdateDto data) throws SQLException, + public void updateTable(TableDto table, TableUpdateDto data) throws SQLException, TableMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); + final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); try { /* create table if not exists */ @@ -189,14 +121,13 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table } @Override - public void delete(PrivilegedTableDto table) throws SQLException, QueryMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); - final String tableName = mariaDbMapper.nameToInternalName(table.getInternalName()); + public void delete(TableDto table) throws SQLException, QueryMalformedException { + final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); try { /* create table if not exists */ final long start = System.currentTimeMillis(); - connection.prepareStatement(mariaDbMapper.dropTableRawQuery(tableName)) + connection.prepareStatement(mariaDbMapper.dropTableRawQuery(table.getInternalName())) .execute(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); @@ -207,63 +138,63 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table } finally { dataSource.close(); } - log.info("Deleted table with name {}", tableName); + log.info("Deleted table with name {}", table.getInternalName()); } @Override - public List<TableHistoryDto> history(PrivilegedTableDto table, Long size) throws SQLException, + public List<TableHistoryDto> history(TableDto table, Long size) throws SQLException, TableNotFoundException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); + final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); final List<TableHistoryDto> history; try { /* find table data */ final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.selectHistoryRawQuery( - table.getDatabase().getInternalName(), table.getInternalName(), size)) + table.getDatabase(), table.getInternalName(), size)) .executeQuery(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); history = dataMapper.resultSetToTableHistory(resultSet); connection.commit(); } catch (SQLException e) { connection.rollback(); - log.error("Failed to find history for table {}.{}: {}", table.getDatabase().getInternalName(), table.getInternalName(), e.getMessage()); - throw new TableNotFoundException("Failed to find history for table " + table.getDatabase().getInternalName() + "." + table.getInternalName() + ": " + e.getMessage(), e); + log.error("Failed to find history for table {}.{}: {}", table.getDatabase(), table.getInternalName(), e.getMessage()); + throw new TableNotFoundException("Failed to find history for table " + table.getDatabase() + "." + table.getInternalName() + ": " + e.getMessage(), e); } finally { dataSource.close(); } - log.info("Find history for table {}.{}", table.getDatabase().getInternalName(), table.getInternalName()); + log.info("Find history for table {}.{}", table.getDatabase(), table.getInternalName()); return history; } @Override - public Long getCount(PrivilegedTableDto table, Instant timestamp) throws SQLException, + public Long getCount(TableDto table, Instant timestamp) throws SQLException, QueryMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); + final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); final Long queryResult; try { /* find table data */ final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.selectCountRawQuery( - table.getDatabase().getInternalName(), table.getInternalName(), timestamp)) + table.getDatabase(), table.getInternalName(), timestamp)) .executeQuery(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); queryResult = mariaDbMapper.resultSetToNumber(resultSet); connection.commit(); } catch (SQLException e) { connection.rollback(); - log.error("Failed to find row count from table {}.{}: {}", table.getDatabase().getInternalName(), table.getInternalName(), e.getMessage()); - throw new QueryMalformedException("Failed to find row count from table " + table.getDatabase().getInternalName() + "." + table.getInternalName() + ": " + e.getMessage(), e); + log.error("Failed to find row count from table {}.{}: {}", table.getDatabase(), table.getInternalName(), e.getMessage()); + throw new QueryMalformedException("Failed to find row count from table " + table.getDatabase() + "." + table.getInternalName() + ": " + e.getMessage(), e); } finally { dataSource.close(); } - log.info("Find row count from table {}.{}", table.getDatabase().getInternalName(), table.getInternalName()); + log.info("Find row count from table {}.{}", table.getDatabase(), table.getInternalName()); return queryResult; } @Override - public void importDataset(PrivilegedTableDto table, ImportDto data) throws MalformedException, + public void importDataset(TableDto table, ImportDto data) throws MalformedException, StorageNotFoundException, StorageUnavailableException, SQLException, QueryMalformedException, TableMalformedException { final List<String> columns = table.getColumns() @@ -273,8 +204,8 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final Dataset<Row> dataset = storageService.loadDataset(columns, data.getLocation(), String.valueOf(data.getSeparator()), data.getHeader()); final Properties properties = new Properties(); - properties.setProperty("user", table.getDatabase().getContainer().getUsername()); - properties.setProperty("password", table.getDatabase().getContainer().getPassword()); + properties.setProperty("user", table.getUsername()); + properties.setProperty("password", table.getPassword()); final String temporaryTable = table.getInternalName() + "_tmp"; try { log.trace("import dataset to temporary table: {}", temporaryTable); @@ -282,8 +213,7 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table .mode(SaveMode.Overwrite) .option("header", data.getHeader()) .option("inferSchema", "true") - .jdbc(getSparkUrl(table.getDatabase().getContainer(), table.getDatabase().getInternalName()), - temporaryTable, properties); + .jdbc(getSparkUrl(table), temporaryTable, properties); } catch (Exception e) { if (e instanceof AnalysisException exception) { final String message = exception.getSimpleMessage() @@ -295,7 +225,7 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table throw new MalformedException("Failed to write dataset: " + e.getMessage()) /* remove throwable on purpose, clutters the output */; } /* import .csv from sidecar to database */ - final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); + final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); try { /* import tuple */ @@ -314,15 +244,15 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table connection.commit(); dataSource.close(); } - log.info("Imported dataset into table: {}.{}", table.getDatabase().getInternalName(), table.getInternalName()); + log.info("Imported dataset into table: {}.{}", table.getDatabase(), table.getInternalName()); } @Override - public void deleteTuple(PrivilegedTableDto table, TupleDeleteDto data) throws SQLException, + public void deleteTuple(TableDto table, TupleDeleteDto data) throws SQLException, TableMalformedException, QueryMalformedException { log.trace("delete tuple: {}", data); /* prepare the statement */ - final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); + final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); try { /* import tuple */ @@ -344,11 +274,11 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table } finally { dataSource.close(); } - log.info("Deleted tuple(s) from table: {}.{}", table.getDatabase().getInternalName(), table.getInternalName()); + log.info("Deleted tuple(s) from table: {}.{}", table.getDatabase(), table.getInternalName()); } @Override - public void createTuple(PrivilegedTableDto table, TupleDto data) throws SQLException, QueryMalformedException, + public void createTuple(TableDto table, TupleDto data) throws SQLException, QueryMalformedException, TableMalformedException, StorageUnavailableException, StorageNotFoundException { log.trace("create tuple: {}", data); /* for each LOB-like data-column, retrieve the bytes and replace the value */ @@ -366,7 +296,7 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table .replace(key, blob); } /* prepare the statement */ - final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); + final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); try { /* create tuple */ @@ -388,15 +318,15 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table } finally { dataSource.close(); } - log.info("Created tuple(s) in table: {}.{}", table.getDatabase().getInternalName(), table.getInternalName()); + log.info("Created tuple(s) in table: {}.{}", table.getDatabase(), table.getInternalName()); } @Override - public void updateTuple(PrivilegedTableDto table, TupleUpdateDto data) throws SQLException, + public void updateTuple(TableDto table, TupleUpdateDto data) throws SQLException, QueryMalformedException, TableMalformedException { log.trace("update tuple: {}", data); /* prepare the statement */ - final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); + final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); try { final int[] idx = new int[]{1}; @@ -424,7 +354,7 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table } finally { dataSource.close(); } - log.info("Updated tuple(s) from table: {}.{}", table.getDatabase().getInternalName(), table.getInternalName()); + log.info("Updated tuple(s) from table: {}.{}", table.getDatabase(), table.getInternalName()); } public ColumnTypeDto getColumnType(List<ColumnDto> columns, String name) throws QueryMalformedException { @@ -439,7 +369,7 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table } @Override - public Dataset<Row> getData(PrivilegedDatabaseDto database, String tableOrView, Instant timestamp, + public Dataset<Row> getData(DatabaseDto database, String tableOrView, Instant timestamp, Long page, Long size, SortTypeDto sortDirection, String sortColumn) throws QueryMalformedException, TableNotFoundException { try { @@ -447,7 +377,8 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table properties.setProperty("user", database.getContainer().getUsername()); properties.setProperty("password", database.getContainer().getPassword()); return sparkSession.read() - .jdbc(getSparkUrl(database.getContainer(), database.getInternalName()), tableOrView, properties); + .jdbc(getSparkUrl(database.getJdbcMethod(), database.getHost(), database.getPassword(), + database.getInternalName()), tableOrView, properties); } catch (Exception e) { if (e instanceof ExtendedAnalysisException exception) { if (exception.getSimpleMessage().contains("TABLE_OR_VIEW_NOT_FOUND")) { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java index d85bdc53ac..3224c371a3 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java @@ -1,182 +1,39 @@ package at.tuwien.service.impl; -import at.tuwien.api.database.ViewCreateDto; import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; -import at.tuwien.api.database.query.QueryDto; -import at.tuwien.config.QueryConfig; -import at.tuwien.exception.DatabaseMalformedException; import at.tuwien.exception.QueryMalformedException; import at.tuwien.exception.ViewMalformedException; -import at.tuwien.exception.ViewNotFoundException; -import at.tuwien.mapper.DataMapper; import at.tuwien.mapper.MariaDbMapper; -import at.tuwien.mapper.MetadataMapper; -import at.tuwien.service.SchemaService; import at.tuwien.service.ViewService; -import com.google.common.hash.Hashing; import com.mchange.v2.c3p0.ComboPooledDataSource; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.nio.charset.StandardCharsets; import java.sql.Connection; -import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.time.Instant; -import java.util.LinkedList; -import java.util.List; @Log4j2 @Service -public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewService { +public class ViewServiceMariaDbImpl extends DataConnector<ViewDto> implements ViewService { - private final DataMapper dataMapper; - private final QueryConfig queryConfig; - private final SchemaService schemaService; private final MariaDbMapper mariaDbMapper; - private final MetadataMapper metadataMapper; @Autowired - public ViewServiceMariaDbImpl(DataMapper dataMapper, QueryConfig queryConfig, SchemaService schemaService, - MariaDbMapper mariaDbMapper, MetadataMapper metadataMapper) { - this.dataMapper = dataMapper; - this.queryConfig = queryConfig; - this.schemaService = schemaService; + public ViewServiceMariaDbImpl(MariaDbMapper mariaDbMapper) { this.mariaDbMapper = mariaDbMapper; - this.metadataMapper = metadataMapper; } @Override - public Boolean existsByName(PrivilegedDatabaseDto database, String name) throws SQLException, - QueryMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); - final Connection connection = dataSource.getConnection(); - final Boolean queryResult; - try { - /* find view data */ - final long start = System.currentTimeMillis(); - final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.selectExistsTableOrViewRawQuery()); - statement.setString(1, database.getInternalName()); - statement.setString(2, name); - final ResultSet resultSet = statement.executeQuery(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - queryResult = mariaDbMapper.resultSetToBoolean(resultSet); - } catch (SQLException e) { - log.error("Failed to prepare statement {}", e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement: " + e.getMessage(), e); - } finally { - dataSource.close(); - } - return queryResult; - } - - @Override - public List<ViewDto> getSchemas(PrivilegedDatabaseDto database) throws SQLException, DatabaseMalformedException, - ViewNotFoundException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); - final Connection connection = dataSource.getConnection(); - final List<ViewDto> views = new LinkedList<>(); - try { - /* inspect tables before views */ - final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.databaseViewsSelectRawQuery()); - statement.setString(1, database.getInternalName()); - final long start = System.currentTimeMillis(); - final ResultSet resultSet1 = statement.executeQuery(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - while (resultSet1.next()) { - final String viewName = resultSet1.getString(1); - if (viewName.length() == 64) { - log.trace("view {}.{} seems to be a subset view (name length = 64), skip.", database.getInternalName(), viewName); - continue; - } - if (database.getViews().stream().anyMatch(v -> v.getInternalName().equals(viewName))) { - log.trace("view {}.{} already known to metadata database, skip.", database.getInternalName(), viewName); - continue; - } - final ViewDto view; - view = schemaService.inspectView(database, viewName); - if (database.getTables().stream().noneMatch(t -> t.getInternalName().equals(view.getInternalName()))) { - views.add(view); - } - } - } catch (SQLException e) { - log.error("Failed to get view schemas: {}", e.getMessage()); - throw new DatabaseMalformedException("Failed to get view schemas: " + e.getMessage(), e); - } finally { - dataSource.close(); - } - log.info("Found {} view schema(s)", views.size()); - return views; - } - - @Override - public ViewDto create(PrivilegedDatabaseDto database, QueryDto subset) throws ViewMalformedException, - SQLException { - final ViewCreateDto data = ViewCreateDto.builder() - .name(metadataMapper.queryDtoToViewName(subset)) - .query(subset.getQuery()) - .isPublic(false) - .build(); - return create(database, data); - } - - @Override - public ViewDto create(PrivilegedDatabaseDto database, ViewCreateDto data) throws SQLException, - ViewMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); - final Connection connection = dataSource.getConnection(); - ViewDto view = ViewDto.builder() - .name(data.getName()) - .internalName(mariaDbMapper.nameToInternalName(data.getName())) - .query(data.getQuery()) - .queryHash(Hashing.sha256() - .hashString(data.getQuery(), StandardCharsets.UTF_8) - .toString()) - .isPublic(database.getIsPublic()) - .owner(database.getOwner()) - .identifiers(new LinkedList<>()) - .isInitialView(false) - .vdbid(database.getId()) - .columns(new LinkedList<>()) - .build(); - try { - /* create view if not exists */ - final long start = System.currentTimeMillis(); - connection.prepareStatement(mariaDbMapper.viewCreateRawQuery(view.getInternalName(), data.getQuery())) - .execute(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - /* select view columns */ - final PreparedStatement statement2 = connection.prepareStatement(mariaDbMapper.databaseTableColumnsSelectRawQuery()); - statement2.setString(1, database.getInternalName()); - statement2.setString(2, view.getInternalName()); - final ResultSet resultSet2 = statement2.executeQuery(); - while (resultSet2.next()) { - view = dataMapper.resultSetToTable(resultSet2, view, queryConfig); - } - connection.commit(); - } catch (SQLException e) { - connection.rollback(); - log.error("Failed to create view: {}", e.getMessage()); - throw new ViewMalformedException("Failed to create view: " + e.getMessage(), e); - } finally { - dataSource.close(); - } - log.info("Created view with name {}", view.getName()); - return view; - } - - @Override - public void delete(PrivilegedDatabaseDto database, String viewName) throws SQLException, ViewMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); + public void delete(ViewDto view) throws SQLException, ViewMalformedException { + final ComboPooledDataSource dataSource = getDataSource(view); final Connection connection = dataSource.getConnection(); try { /* drop view if exists */ final long start = System.currentTimeMillis(); - connection.prepareStatement(mariaDbMapper.dropViewRawQuery(viewName)) + connection.prepareStatement(mariaDbMapper.dropViewRawQuery(view.getInternalName())) .execute(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); connection.commit(); @@ -187,33 +44,32 @@ public class ViewServiceMariaDbImpl extends HibernateConnector implements ViewSe } finally { dataSource.close(); } - log.info("Deleted view {}.{}", database.getInternalName(), viewName); + log.info("Deleted view {}.{}", view.getDatabase(), view.getInternalName()); } - @Override - public Long count(PrivilegedViewDto view, Instant timestamp) throws SQLException, + public Long count(ViewDto view, Instant timestamp) throws SQLException, QueryMalformedException { - final ComboPooledDataSource dataSource = getPrivilegedDataSource(view.getDatabase()); + final ComboPooledDataSource dataSource = getDataSource(view); final Connection connection = dataSource.getConnection(); final Long queryResult; try { /* find view data */ final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.selectCountRawQuery( - view.getDatabase().getInternalName(), view.getInternalName(), timestamp)) + view.getDatabase(), view.getInternalName(), timestamp)) .executeQuery(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); queryResult = mariaDbMapper.resultSetToNumber(resultSet); connection.commit(); } catch (SQLException e) { connection.rollback(); - log.error("Failed to find row count from view {}.{}: {}", view.getDatabase().getInternalName(), view.getInternalName(), e.getMessage()); - throw new QueryMalformedException("Failed to find row count from view " + view.getDatabase().getInternalName() + "." + view.getInternalName() + ": " + e.getMessage(), e); + log.error("Failed to find row count from view {}.{}: {}", view.getDatabase(), view.getInternalName(), e.getMessage()); + throw new QueryMalformedException("Failed to find row count from view " + view.getDatabase() + "." + view.getInternalName() + ": " + e.getMessage(), e); } finally { dataSource.close(); } - log.info("Find row count from view {}.{}", view.getDatabase().getInternalName(), view.getInternalName()); + log.info("Find row count from view {}.{}", view.getDatabase(), view.getInternalName()); return queryResult; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java new file mode 100644 index 0000000000..66de637504 --- /dev/null +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java @@ -0,0 +1,45 @@ +package at.tuwien.api; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.time.Instant; + +@Getter +@Setter +@EqualsAndHashCode +@ToString +public abstract class CacheableDto { + + @JsonProperty("last_retrieved") + private Instant lastRetrieved; + + @ToString.Exclude + @JsonIgnore + private String jdbcMethod; + + @ToString.Exclude + @JsonIgnore + private String host; + + @ToString.Exclude + @JsonIgnore + private Integer port; + + @ToString.Exclude + @JsonIgnore + private String username; + + @ToString.Exclude + @JsonIgnore + private String password; + + @ToString.Exclude + @JsonIgnore + private String database; + +} diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/PrivilegedObjectDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/PrivilegedObjectDto.java deleted file mode 100644 index c88fcabccf..0000000000 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/PrivilegedObjectDto.java +++ /dev/null @@ -1,18 +0,0 @@ -package at.tuwien.api; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -import java.time.Instant; - -@Getter -@Setter -@ToString -public abstract class PrivilegedObjectDto { - - @JsonProperty("last_retrieved") - private Instant lastRetrieved; - -} diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java index 9928c8e54d..da782c2bb7 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java @@ -1,7 +1,9 @@ package at.tuwien.api.container; +import at.tuwien.api.CacheableDto; import at.tuwien.api.container.image.ImageDto; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -18,7 +20,7 @@ import java.time.Instant; @AllArgsConstructor @Jacksonized @ToString -public class ContainerDto { +public class ContainerDto extends CacheableDto { @NotNull private Long id; @@ -55,4 +57,25 @@ public class ContainerDto { @Schema(example = "10") private Long count; + /* lombok limitations prevent from convenient builder functions */ + + @JsonProperty("last_retrieved") + private Instant lastRetrieved; + + @ToString.Exclude + @JsonIgnore + private String jdbcMethod; + + @ToString.Exclude + @JsonIgnore + private String username; + + @ToString.Exclude + @JsonIgnore + private String password; + + @ToString.Exclude + @JsonIgnore + private String database; + } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/internal/PrivilegedContainerDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/internal/PrivilegedContainerDto.java deleted file mode 100644 index 9c414e5ef1..0000000000 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/internal/PrivilegedContainerDto.java +++ /dev/null @@ -1,60 +0,0 @@ -package at.tuwien.api.container.internal; - -import at.tuwien.api.PrivilegedObjectDto; -import at.tuwien.api.container.image.ImageDto; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.*; -import lombok.extern.jackson.Jacksonized; - -import java.time.Instant; - -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Jacksonized -@ToString -public class PrivilegedContainerDto extends PrivilegedObjectDto { - - @NotNull - private Long id; - - @NotBlank - @Schema(example = "Air Quality") - private String name; - - @NotBlank - @JsonProperty("internal_name") - @Schema(example = "data-db") - private String internalName; - - @NotBlank - private String host; - - @NotNull - private Integer port; - - @JsonProperty("ui_host") - private String uiHost; - - @JsonProperty("ui_port") - private Integer uiPort; - - @NotNull - private ImageDto image; - - @ToString.Exclude - private String username; - - @ToString.Exclude - private String password; - - @JsonProperty("last_retrieved") - private Instant lastRetrieved; - -} diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java index 46072e83dc..7b47a617ec 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java @@ -56,4 +56,7 @@ public class DatabaseBriefDto { @JsonProperty("owner_id") private UUID ownerId; + @JsonProperty("preview_image") + private String previewImage; + } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java index 5fc253c433..044e52df38 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java @@ -1,9 +1,11 @@ package at.tuwien.api.database; -import at.tuwien.api.container.ContainerBriefDto; -import at.tuwien.api.database.table.TableBriefDto; -import at.tuwien.api.identifier.IdentifierBriefDto; +import at.tuwien.api.CacheableDto; +import at.tuwien.api.container.ContainerDto; +import at.tuwien.api.database.table.TableDto; +import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.UserBriefDto; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -11,17 +13,18 @@ import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; +import java.time.Instant; import java.util.List; @Getter @Setter @Builder -@EqualsAndHashCode +@EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor @Jacksonized @ToString -public class DatabaseDto { +public class DatabaseDto extends CacheableDto { @NotNull private Long id; @@ -47,9 +50,9 @@ public class DatabaseDto { @Schema(example = "Air Quality") private String description; - private List<TableBriefDto> tables; + private List<TableDto> tables; - private List<ViewBriefDto> views; + private List<ViewDto> views; @NotNull @JsonProperty("is_public") @@ -62,13 +65,13 @@ public class DatabaseDto { private Boolean isSchemaPublic; @NotNull - private ContainerBriefDto container; + private ContainerDto container; private List<DatabaseAccessDto> accesses; - private List<IdentifierBriefDto> identifiers; + private List<IdentifierDto> identifiers; - private List<IdentifierBriefDto> subsets; + private List<IdentifierDto> subsets; @NotNull private UserBriefDto contact; @@ -79,4 +82,33 @@ public class DatabaseDto { @JsonProperty("preview_image") private String previewImage; + /* lombok limitations prevent from convenient builder functions */ + + @JsonProperty("last_retrieved") + private Instant lastRetrieved; + + @ToString.Exclude + @JsonIgnore + private String jdbcMethod; + + @ToString.Exclude + @JsonIgnore + private String host; + + @ToString.Exclude + @JsonIgnore + private Integer port; + + @ToString.Exclude + @JsonIgnore + private String username; + + @ToString.Exclude + @JsonIgnore + private String password; + + @ToString.Exclude + @JsonIgnore + private String database; + } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java index d1ee156e9b..13e64911f5 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java @@ -1,7 +1,9 @@ package at.tuwien.api.database; +import at.tuwien.api.CacheableDto; import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.UserBriefDto; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -9,17 +11,18 @@ import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; +import java.time.Instant; import java.util.List; @Getter @Setter @Builder -@EqualsAndHashCode +@EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor @Jacksonized @ToString -public class ViewDto { +public class ViewDto extends CacheableDto { @NotNull private Long id; @@ -66,4 +69,33 @@ public class ViewDto { @NotNull private List<ViewColumnDto> columns; + /* lombok limitations prevent from convenient builder functions */ + + @JsonProperty("last_retrieved") + private Instant lastRetrieved; + + @ToString.Exclude + @JsonIgnore + private String jdbcMethod; + + @ToString.Exclude + @JsonIgnore + private String host; + + @ToString.Exclude + @JsonIgnore + private Integer port; + + @ToString.Exclude + @JsonIgnore + private String username; + + @ToString.Exclude + @JsonIgnore + private String password; + + @ToString.Exclude + @JsonIgnore + private String database; + } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedDatabaseDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedDatabaseDto.java deleted file mode 100644 index 2335ea39ba..0000000000 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedDatabaseDto.java +++ /dev/null @@ -1,88 +0,0 @@ -package at.tuwien.api.database.internal; - -import at.tuwien.api.PrivilegedObjectDto; -import at.tuwien.api.container.internal.PrivilegedContainerDto; -import at.tuwien.api.database.DatabaseAccessDto; -import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.table.TableDto; -import at.tuwien.api.identifier.IdentifierDto; -import at.tuwien.api.user.UserBriefDto; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.*; -import lombok.extern.jackson.Jacksonized; - -import java.time.Instant; -import java.util.List; - -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Jacksonized -@ToString -public class PrivilegedDatabaseDto extends PrivilegedObjectDto { - - @NotNull - private Long id; - - @NotBlank - @Schema(example = "Air Quality") - private String name; - - @NotBlank - @JsonProperty("exchange_name") - @Schema(example = "dbrepo") - private String exchangeName; - - @JsonProperty("exchange_type") - @Schema(example = "topic") - private String exchangeType; - - @NotBlank - @JsonProperty("internal_name") - @Schema(example = "air_quality") - private String internalName; - - @Schema(example = "Air Quality") - private String description; - - private List<TableDto> tables; - - private List<ViewDto> views; - - @NotNull - @JsonProperty("is_public") - @Schema(example = "true") - private Boolean isPublic; - - @NotNull - @JsonProperty("is_schema_public") - @Schema(example = "true") - private Boolean isSchemaPublic; - - @NotNull - private PrivilegedContainerDto container; - - private List<DatabaseAccessDto> accesses; - - private List<IdentifierDto> identifiers; - - private List<IdentifierDto> subsets; - - @NotNull - private UserBriefDto contact; - - @NotNull - private UserBriefDto owner; - - @JsonProperty("preview_image") - private String previewImage; - - @JsonProperty("last_retrieved") - private Instant lastRetrieved; - -} diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedViewDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedViewDto.java deleted file mode 100644 index bda575f45d..0000000000 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/internal/PrivilegedViewDto.java +++ /dev/null @@ -1,77 +0,0 @@ -package at.tuwien.api.database.internal; - -import at.tuwien.api.PrivilegedObjectDto; -import at.tuwien.api.database.ViewColumnDto; -import at.tuwien.api.identifier.IdentifierDto; -import at.tuwien.api.user.UserBriefDto; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.*; -import lombok.extern.jackson.Jacksonized; - -import java.time.Instant; -import java.util.List; - -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Jacksonized -@ToString -public class PrivilegedViewDto extends PrivilegedObjectDto { - - @NotNull - private Long id; - - @NotNull - @JsonProperty("database_id") - private Long vdbid; - - @NotNull - private PrivilegedDatabaseDto database; - - @NotBlank - @Schema(example = "Air Quality") - private String name; - - private List<IdentifierDto> identifiers; - - @NotBlank - @Schema(example = "air_quality") - @JsonProperty("internal_name") - private String internalName; - - @JsonProperty("is_public") - @Schema(example = "true") - private Boolean isPublic; - - @JsonProperty("is_schema_public") - @Schema(example = "true") - private Boolean isSchemaPublic; - - @JsonProperty("initial_view") - @Schema(example = "true", description = "True if it is the default view for the database") - private Boolean isInitialView; - - @NotNull - @Schema(example = "SELECT `id` FROM `air_quality` ORDER BY `value` DESC") - private String query; - - @NotNull - @JsonProperty("query_hash") - @Schema(example = "7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916") - private String queryHash; - - @NotNull - private UserBriefDto owner; - - @NotNull - private List<ViewColumnDto> columns; - - @JsonProperty("last_retrieved") - private Instant lastRetrieved; - -} diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java index 67087d438d..cf18e321a4 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java @@ -1,9 +1,11 @@ package at.tuwien.api.database.table; +import at.tuwien.api.CacheableDto; import at.tuwien.api.database.table.columns.ColumnDto; import at.tuwien.api.database.table.constraints.ConstraintsDto; import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.UserBriefDto; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -12,17 +14,18 @@ import jakarta.validation.constraints.Size; import lombok.*; import lombok.extern.jackson.Jacksonized; +import java.time.Instant; import java.util.List; @Getter @Setter @Builder -@EqualsAndHashCode +@EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor @Jacksonized @ToString -public class TableDto { +public class TableDto extends CacheableDto { @NotNull private Long id; @@ -103,4 +106,33 @@ public class TableDto { @NotNull private ConstraintsDto constraints; + /* lombok limitations prevent from convenient builder functions */ + + @JsonProperty("last_retrieved") + private Instant lastRetrieved; + + @ToString.Exclude + @JsonIgnore + private String jdbcMethod; + + @ToString.Exclude + @JsonIgnore + private String host; + + @ToString.Exclude + @JsonIgnore + private Integer port; + + @ToString.Exclude + @JsonIgnore + private String username; + + @ToString.Exclude + @JsonIgnore + private String password; + + @ToString.Exclude + @JsonIgnore + private String database; + } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/PrivilegedTableDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/PrivilegedTableDto.java deleted file mode 100644 index 64b23f17c4..0000000000 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/PrivilegedTableDto.java +++ /dev/null @@ -1,115 +0,0 @@ -package at.tuwien.api.database.table.internal; - -import at.tuwien.api.PrivilegedObjectDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.table.columns.ColumnDto; -import at.tuwien.api.database.table.constraints.ConstraintsDto; -import at.tuwien.api.identifier.IdentifierDto; -import at.tuwien.api.user.UserBriefDto; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import lombok.*; -import lombok.extern.jackson.Jacksonized; - -import java.time.Instant; -import java.util.List; - -@Getter -@Setter -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Jacksonized -@ToString -@EqualsAndHashCode -public class PrivilegedTableDto extends PrivilegedObjectDto { - - @NotNull - private Long id; - - @NotNull - @JsonProperty("database_id") - private Long tdbid; - - @NotBlank - @Schema(example = "Air Quality") - private String name; - - @NotBlank - @JsonProperty("internal_name") - @Schema(example = "air_quality") - private String internalName; - - @Schema - private String alias; - - private List<IdentifierDto> identifiers; - - @NotNull - @JsonProperty("is_versioned") - @Schema(example = "true") - private Boolean isVersioned; - - @NotNull - @JsonProperty("is_schema_public") - @Schema(example = "true") - private Boolean isSchemaPublic; - - @NotNull - private UserBriefDto owner; - - @NotBlank - @JsonProperty("queue_name") - @Schema(example = "air_quality") - private String queueName; - - @JsonProperty("queue_type") - @Schema(example = "quorum") - private String queueType; - - @NotBlank - @JsonProperty("routing_key") - @Schema(example = "dbrepo.1.2") - private String routingKey; - - @Size(max = 2048) - @Schema(example = "Air Quality in Austria") - private String description; - - @NotNull - @JsonProperty("is_public") - @Schema(example = "true") - private Boolean isPublic; - - @JsonProperty("num_rows") - @Schema(example = "5") - private Long numRows; - - @JsonProperty("data_length") - @Schema(example = "16384", description = "in bytes") - private Long dataLength; - - @JsonProperty("max_data_length") - @Schema(example = "0", description = "in bytes") - private Long maxDataLength; - - @JsonProperty("avg_row_length") - @Schema(example = "3276", description = "in bytes") - private Long avgRowLength; - - @NotNull - private List<ColumnDto> columns; - - @NotNull - private ConstraintsDto constraints; - - @NotNull - private PrivilegedDatabaseDto database; - - @JsonProperty("last_retrieved") - private Instant lastRetrieved; - -} diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java index 0a36c561a3..82ff3b0fe7 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java @@ -1,6 +1,5 @@ package at.tuwien.api.identifier; -import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -8,7 +7,6 @@ import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; -import java.time.Instant; import java.util.List; import java.util.UUID; diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java index 343d582b55..43fb10201b 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java @@ -1,22 +1,25 @@ package at.tuwien.api.user; +import at.tuwien.api.CacheableDto; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; +import java.time.Instant; import java.util.UUID; @Getter @Setter @Builder -@EqualsAndHashCode +@EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor @Jacksonized @ToString -public class UserDto { +public class UserDto extends CacheableDto { @NotNull @Schema(example = "1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4") @@ -41,7 +44,14 @@ public class UserDto { @Schema(example = "Carberry") private String lastname; + @ToString.Exclude + @JsonIgnore + private String password; + @NotNull private UserAttributesDto attributes; + @JsonProperty("last_retrieved") + private Instant lastRetrieved; + } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/PrivilegedUserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/PrivilegedUserDto.java deleted file mode 100644 index 56e24cd815..0000000000 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/internal/PrivilegedUserDto.java +++ /dev/null @@ -1,58 +0,0 @@ -package at.tuwien.api.user.internal; - -import at.tuwien.api.PrivilegedObjectDto; -import at.tuwien.api.user.UserAttributesDto; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.*; -import lombok.extern.jackson.Jacksonized; - -import java.time.Instant; -import java.util.UUID; - -@Getter -@Setter -@Builder -@EqualsAndHashCode -@NoArgsConstructor -@AllArgsConstructor -@Jacksonized -@ToString -public class PrivilegedUserDto extends PrivilegedObjectDto { - - @NotNull - @Schema(example = "1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4") - private UUID id; - - @NotBlank - @Schema(example = "jcarberry", description = "Only contains lowercase characters") - private String username; - - @NotBlank - @Schema(example = "jcarberry") - private String password; - - @Schema(example = "Josiah Carberry") - private String name; - - @JsonProperty("qualified_name") - @Schema(example = "Josiah Carberry — @jcarberry") - private String qualifiedName; - - @JsonProperty("given_name") - @Schema(example = "Josiah") - private String firstname; - - @JsonProperty("family_name") - @Schema(example = "Carberry") - private String lastname; - - @NotNull - private UserAttributesDto attributes; - - @JsonProperty("last_retrieved") - private Instant lastRetrieved; - -} 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 c5482f7041..ab13affe7e 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 @@ -10,7 +10,6 @@ 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.internal.PrivilegedDatabaseDto; import at.tuwien.api.database.table.TableBriefDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.columns.ColumnCreateDto; @@ -120,7 +119,10 @@ public interface MetadataMapper { }) ContainerBriefDto containerToContainerBriefDto(Container data); - PrivilegedDatabaseDto databaseToPrivilegedDatabaseDto(Database data); + @Mappings({ + @Mapping(target = "previewImage", expression = "java(database.getImage() != null ? \"/api/database/\" + database.getId() + \"/image\" : null)") + }) + DatabaseDto databaseToDatabaseDto(Database database); @Mappings({ @Mapping(target = "titles", source = "."), @@ -531,7 +533,7 @@ public interface MetadataMapper { .build(); } - default TableDto customTableToTableDto(Table data) { + default TableDto tableToTableDto(Table data) { final TableDto table = TableDto.builder() .id(data.getId()) .name(data.getName()) @@ -620,7 +622,8 @@ public interface MetadataMapper { Unique uniqueDtoToUnique(UniqueDto data); @Mappings({ - @Mapping(target = "ownedBy", source = "owner.id") + @Mapping(target = "ownedBy", source = "owner.id"), + @Mapping(target = "database", ignore = true) }) Table tableDtoToTable(TableDto data); @@ -822,6 +825,9 @@ public interface MetadataMapper { .trim(); } + @Mappings({ + @Mapping(target = "database", ignore = true) + }) ViewDto viewToViewDto(View data); @Mappings({ @@ -831,6 +837,9 @@ public interface MetadataMapper { ViewBriefDto viewToViewBriefDto(View data); + @Mappings({ + @Mapping(target = "database", ignore = true) + }) View viewDtoToView(ViewDto data); /* keep */ @@ -852,60 +861,7 @@ public interface MetadataMapper { 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()) - .previewImage(data.getImage() != null ? "/api/database/" + data.getId() + "/image" : null) - .isPublic(data.getIsPublic()) - .isSchemaPublic(data.getIsSchemaPublic()) - .container(containerToContainerBriefDto(data.getContainer())) - .owner(userToUserBriefDto(data.getOwner())) - .contact(userToUserBriefDto(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::identifierToIdentifierBriefDto) - .toList())); - } - if (data.getTables() != null) { - database.setTables(new LinkedList<>(data.getTables() - .stream() - .map(this::tableToTableBriefDto) - .toList())); - } - if (data.getViews() != null) { - database.setViews(new LinkedList<>(data.getViews() - .stream() - .map(this::viewToViewBriefDto) - .toList())); - } - if (data.getAccesses() != null) { - database.setAccesses(new LinkedList<>(data.getAccesses() - .stream() - .filter(a -> !a.getUser().getIsInternal()) - .map(this::databaseAccessToDatabaseAccessDto) - .toList())); - } - if (data.getIdentifiers() != null) { - database.setIdentifiers(new LinkedList<>(data.getIdentifiers() - .stream() - .map(this::identifierToIdentifierBriefDto) - .toList())); - } - return database; - } + DatabaseBriefDto databaseDtoToDatabaseBriefDto(DatabaseDto data); @Mappings({ @Mapping(target = "ownerId", source = "owner.id") 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 ad72fb0756..7e03202480 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 @@ -5,8 +5,6 @@ import at.tuwien.api.error.ApiErrorDto; import at.tuwien.entities.container.Container; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; -import at.tuwien.entities.database.View; -import at.tuwien.entities.database.table.Table; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.mapper.MetadataMapper; @@ -73,7 +71,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { @Header(name = "Access-Control-Expose-Headers", description = "Expose `X-Count` custom header", schema = @Schema(implementation = String.class), required = true)}, content = {@Content( mediaType = "application/json", - array = @ArraySchema(schema = @Schema(implementation = DatabaseDto.class)))}), + array = @ArraySchema(schema = @Schema(implementation = DatabaseBriefDto.class)))}), }) public ResponseEntity<List<DatabaseBriefDto>> list(@RequestParam(name = "internal_name", required = false) String internalName, Principal principal) { @@ -118,7 +116,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { description = "Created a new database", content = {@Content( mediaType = "application/json", - schema = @Schema(implementation = DatabaseDto.class))}), + schema = @Schema(implementation = DatabaseBriefDto.class))}), @ApiResponse(responseCode = "400", description = "Database create query is malformed or image is not supported", content = {@Content( @@ -155,8 +153,8 @@ public class DatabaseEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<DatabaseDto> create(@Valid @RequestBody DatabaseCreateDto data, - @NotNull Principal principal) throws DataServiceException, + public ResponseEntity<DatabaseBriefDto> create(@Valid @RequestBody DatabaseCreateDto data, + @NotNull Principal principal) throws DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, ContainerNotFoundException, SearchServiceException, SearchServiceConnectionException, ContainerQuotaException { @@ -168,8 +166,8 @@ public class DatabaseEndpoint extends AbstractEndpoint { } final User caller = userService.findById(getId(principal)); return ResponseEntity.status(HttpStatus.CREATED) - .body(databaseMapper.customDatabaseToDatabaseDto( - databaseService.create(container, data, caller, userService.findAllInternalUsers()))); + .body(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + databaseService.create(container, data, caller, userService.findAllInternalUsers())))); } @PutMapping("/{databaseId}/metadata/table") @@ -184,7 +182,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { description = "Refreshed database tables metadata", content = {@Content( mediaType = "application/json", - schema = @Schema(implementation = DatabaseDto.class))}), + schema = @Schema(implementation = DatabaseBriefDto.class))}), @ApiResponse(responseCode = "400", description = "Failed to parse payload at search service", content = {@Content( @@ -211,8 +209,8 @@ public class DatabaseEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<DatabaseDto> refreshTableMetadata(@NotNull @PathVariable("databaseId") Long databaseId, - @NotNull Principal principal) throws DataServiceException, + public ResponseEntity<DatabaseBriefDto> refreshTableMetadata(@NotNull @PathVariable("databaseId") Long databaseId, + @NotNull Principal principal) throws DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, SearchServiceException, UserNotFoundException, SearchServiceConnectionException, NotAllowedException, QueryNotFoundException, MalformedException, TableNotFoundException { @@ -222,8 +220,8 @@ public class DatabaseEndpoint extends AbstractEndpoint { log.error("Failed to refresh database tables metadata: not owner"); throw new NotAllowedException("Failed to refresh tables metadata: not owner"); } - return ResponseEntity.ok(databaseMapper.customDatabaseToDatabaseDto( - databaseService.updateTableMetadata(database))); + return ResponseEntity.ok(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + databaseService.updateTableMetadata(database)))); } @PutMapping("/{databaseId}/metadata/view") @@ -238,7 +236,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { description = "Refreshed database views metadata", content = {@Content( mediaType = "application/json", - schema = @Schema(implementation = DatabaseDto.class))}), + schema = @Schema(implementation = DatabaseBriefDto.class))}), @ApiResponse(responseCode = "403", description = "Refresh view metadata is not permitted", content = {@Content( @@ -260,9 +258,9 @@ public class DatabaseEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<DatabaseDto> refreshViewMetadata(@NotNull @PathVariable("databaseId") Long databaseId, - @NotNull Principal principal) throws DataServiceException, - DataServiceConnectionException, DatabaseNotFoundException, SearchServiceException, UserNotFoundException, + public ResponseEntity<DatabaseBriefDto> refreshViewMetadata(@NotNull @PathVariable("databaseId") Long databaseId, + @NotNull Principal principal) throws DataServiceException, + DataServiceConnectionException, DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException, NotAllowedException, QueryNotFoundException, ViewNotFoundException { log.debug("endpoint refresh database metadata, databaseId={}, principal.name={}", databaseId, principal.getName()); final Database database = databaseService.findById(databaseId); @@ -270,8 +268,8 @@ public class DatabaseEndpoint extends AbstractEndpoint { log.error("Failed to refresh database views metadata: not owner"); throw new NotAllowedException("Failed to refresh database views metadata: not owner"); } - return ResponseEntity.ok(databaseMapper.customDatabaseToDatabaseDto( - databaseService.updateViewMetadata(database))); + return ResponseEntity.ok(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + databaseService.updateViewMetadata(database)))); } @PutMapping("/{databaseId}/visibility") @@ -286,7 +284,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { description = "Visibility modified successfully", content = {@Content( mediaType = "application/json", - schema = @Schema(implementation = DatabaseDto.class))}), + schema = @Schema(implementation = DatabaseBriefDto.class))}), @ApiResponse(responseCode = "400", description = "The visibility payload is malformed", content = {@Content( @@ -313,9 +311,9 @@ public class DatabaseEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<DatabaseDto> visibility(@NotNull @PathVariable("databaseId") Long databaseId, - @Valid @RequestBody DatabaseModifyVisibilityDto data, - @NotNull Principal principal) throws DatabaseNotFoundException, + public ResponseEntity<DatabaseBriefDto> visibility(@NotNull @PathVariable("databaseId") Long databaseId, + @Valid @RequestBody DatabaseModifyVisibilityDto data, + @NotNull Principal principal) throws DatabaseNotFoundException, NotAllowedException, SearchServiceException, SearchServiceConnectionException, UserNotFoundException { log.debug("endpoint modify database visibility, databaseId={}, data={}", databaseId, data); final Database database = databaseService.findById(databaseId); @@ -324,8 +322,8 @@ public class DatabaseEndpoint extends AbstractEndpoint { throw new NotAllowedException("Failed to modify database visibility: not owner"); } return ResponseEntity.accepted() - .body(databaseMapper.customDatabaseToDatabaseDto( - databaseService.modifyVisibility(database, data))); + .body(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + databaseService.modifyVisibility(database, data)))); } @PutMapping("/{databaseId}/owner") @@ -340,7 +338,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { description = "Transfer of ownership was successful", content = {@Content( mediaType = "application/json", - schema = @Schema(implementation = DatabaseDto.class))}), + schema = @Schema(implementation = DatabaseBriefDto.class))}), @ApiResponse(responseCode = "400", description = "Owner payload is malformed", content = {@Content( @@ -367,9 +365,9 @@ public class DatabaseEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<DatabaseDto> transfer(@NotNull @PathVariable("databaseId") Long databaseId, - @Valid @RequestBody DatabaseTransferDto data, - @NotNull Principal principal) throws NotAllowedException, + public ResponseEntity<DatabaseBriefDto> transfer(@NotNull @PathVariable("databaseId") Long databaseId, + @Valid @RequestBody DatabaseTransferDto data, + @NotNull Principal principal) throws NotAllowedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException, SearchServiceException, SearchServiceConnectionException { log.debug("endpoint transfer database, databaseId={}, transferDto.id={}", databaseId, data.getId()); @@ -380,8 +378,8 @@ public class DatabaseEndpoint extends AbstractEndpoint { throw new NotAllowedException("Failed to transfer database: not owner"); } return ResponseEntity.accepted() - .body(databaseMapper.customDatabaseToDatabaseDto( - databaseService.modifyOwner(database, newOwner))); + .body(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + databaseService.modifyOwner(database, newOwner)))); } @PutMapping("/{databaseId}/image") @@ -396,7 +394,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { description = "Modify of image was successful", content = {@Content( mediaType = "application/json", - schema = @Schema(implementation = DatabaseDto.class))}), + schema = @Schema(implementation = DatabaseBriefDto.class))}), @ApiResponse(responseCode = "403", description = "Modify of image is not permitted", content = {@Content( @@ -423,9 +421,9 @@ public class DatabaseEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<DatabaseDto> modifyImage(@NotNull @PathVariable("databaseId") Long databaseId, - @Valid @RequestBody DatabaseModifyImageDto data, - @NotNull Principal principal) throws NotAllowedException, + public ResponseEntity<DatabaseBriefDto> modifyImage(@NotNull @PathVariable("databaseId") Long databaseId, + @Valid @RequestBody DatabaseModifyImageDto data, + @NotNull Principal principal) throws NotAllowedException, DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException, StorageUnavailableException, StorageNotFoundException { log.debug("endpoint modify database image, databaseId={}, data.key={}", databaseId, data.getKey()); @@ -439,8 +437,8 @@ public class DatabaseEndpoint extends AbstractEndpoint { image = storageService.getBytes(data.getKey()); } return ResponseEntity.accepted() - .body(databaseMapper.customDatabaseToDatabaseDto( - databaseService.modifyImage(database, image))); + .body(databaseMapper.databaseDtoToDatabaseBriefDto(databaseMapper.databaseToDatabaseDto( + databaseService.modifyImage(database, image)))); } @GetMapping("/{databaseId}/image") @@ -480,7 +478,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { @Header(name = "Access-Control-Expose-Headers", description = "Expose custom headers", schema = @Schema(implementation = String.class))}, content = {@Content( mediaType = "application/json", - schema = @Schema(implementation = DatabaseDto.class))}), + schema = @Schema(implementation = DatabaseBriefDto.class))}), @ApiResponse(responseCode = "403", description = "Not allowed to view database", content = {@Content( @@ -546,7 +544,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { .toList()); database.setAccesses(List.of()); } - final DatabaseDto dto = databaseMapper.customDatabaseToDatabaseDto(database); + final DatabaseDto dto = databaseMapper.databaseToDatabaseDto(database); final HttpHeaders headers = new HttpHeaders(); if (isSystem(principal)) { headers.set("X-Username", database.getContainer().getPrivilegedUsername()); 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 9f4542fc02..39d7f61460 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 @@ -370,7 +370,7 @@ public class TableEndpoint extends AbstractEndpoint { endpointValidator.validateOnlyAccess(database, principal, true); endpointValidator.validateColumnCreateConstraints(data); return ResponseEntity.status(HttpStatus.CREATED) - .body(metadataMapper.customTableToTableDto( + .body(metadataMapper.tableToTableDto( tableService.createTable(database, data, principal))); } @@ -428,7 +428,7 @@ public class TableEndpoint extends AbstractEndpoint { throw new NotAllowedException("Failed to update table: not owner"); } return ResponseEntity.accepted() - .body(metadataMapper.customTableToTableDto( + .body(metadataMapper.tableToTableDto( tableService.updateTable(table, data))); } @@ -513,7 +513,7 @@ public class TableEndpoint extends AbstractEndpoint { } return ResponseEntity.ok() .headers(headers) - .body(metadataMapper.customTableToTableDto(table)); + .body(metadataMapper.tableToTableDto(table)); } @DeleteMapping("/{tableId}") diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java index 11d64faf8b..371e710fae 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java @@ -198,7 +198,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_1); /* test */ - final ResponseEntity<DatabaseDto> response = databaseEndpoint.refreshTableMetadata(DATABASE_1_ID, USER_1_PRINCIPAL); + final ResponseEntity<DatabaseBriefDto> response = databaseEndpoint.refreshTableMetadata(DATABASE_1_ID, USER_1_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } @@ -218,7 +218,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_1); /* test */ - final ResponseEntity<DatabaseDto> response = databaseEndpoint.refreshViewMetadata(DATABASE_1_ID, USER_1_PRINCIPAL); + final ResponseEntity<DatabaseBriefDto> response = databaseEndpoint.refreshViewMetadata(DATABASE_1_ID, USER_1_PRINCIPAL); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } @@ -682,7 +682,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_1); /* test */ - final ResponseEntity<DatabaseDto> response = databaseEndpoint.create(data, principal); + final ResponseEntity<DatabaseBriefDto> response = databaseEndpoint.create(data, principal); assertEquals(HttpStatus.CREATED, response.getStatusCode()); assertNotNull(response.getBody()); } @@ -704,7 +704,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { } /* test */ - final ResponseEntity<DatabaseDto> response = databaseEndpoint.visibility(databaseId, data, principal); + final ResponseEntity<DatabaseBriefDto> response = databaseEndpoint.visibility(databaseId, data, principal); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); assertNotNull(response.getBody()); } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java index daeb1c1a96..c423d6b1ae 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java @@ -2,7 +2,7 @@ package at.tuwien.gateway; import at.tuwien.ExportResourceDto; import at.tuwien.api.database.AccessTypeDto; -import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.DatabaseBriefDto; import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.table.TableDto; @@ -252,9 +252,9 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { DatabaseNotFoundException { /* mock */ - when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class))) + when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseBriefDto.class))) .thenReturn(ResponseEntity.status(HttpStatus.CREATED) - .body(DATABASE_1_DTO)); + .body(DATABASE_1_BRIEF_DTO)); /* test */ dataServiceGateway.createDatabase(DATABASE_1_CREATE_INTERNAL); @@ -266,7 +266,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ doThrow(HttpServerErrorException.class) .when(dataServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(DataServiceConnectionException.class, () -> { @@ -280,7 +280,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ doThrow(HttpClientErrorException.Unauthorized.class) .when(dataServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(DataServiceException.class, () -> { @@ -294,7 +294,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ doThrow(HttpClientErrorException.BadRequest.class) .when(dataServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(DataServiceException.class, () -> { @@ -306,7 +306,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { public void createDatabase_responseCode_fails() { /* mock */ - when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class))) + when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseBriefDto.class))) .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT) .build()); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/SearchServiceGatewayUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/SearchServiceGatewayUnitTest.java index b1ce21d4e5..b39dd06bac 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/SearchServiceGatewayUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/SearchServiceGatewayUnitTest.java @@ -1,7 +1,7 @@ package at.tuwien.gateway; import at.tuwien.test.AbstractUnitTest; -import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.DatabaseBriefDto; import at.tuwien.exception.*; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.Test; @@ -37,11 +37,11 @@ public class SearchServiceGatewayUnitTest extends AbstractUnitTest { @Test public void update_succeeds() throws DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException { - final ResponseEntity<DatabaseDto> mock = ResponseEntity.accepted() + final ResponseEntity<DatabaseBriefDto> mock = ResponseEntity.accepted() .build(); /* mock */ - when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class))) + when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class))) .thenReturn(mock); /* test */ @@ -50,11 +50,11 @@ public class SearchServiceGatewayUnitTest extends AbstractUnitTest { @Test public void update_badRequest_fails() { - final ResponseEntity<DatabaseDto> mock = ResponseEntity.status(HttpStatus.BAD_REQUEST) + final ResponseEntity<DatabaseBriefDto> mock = ResponseEntity.status(HttpStatus.BAD_REQUEST) .build(); /* mock */ - when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class))) + when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class))) .thenReturn(mock); /* test */ @@ -65,11 +65,11 @@ public class SearchServiceGatewayUnitTest extends AbstractUnitTest { @Test public void update_unexpectedResponse_fails() { - final ResponseEntity<DatabaseDto> mock = ResponseEntity.status(HttpStatus.OK) + final ResponseEntity<DatabaseBriefDto> mock = ResponseEntity.status(HttpStatus.OK) .build(); /* mock */ - when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class))) + when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class))) .thenReturn(mock); /* test */ @@ -84,7 +84,7 @@ public class SearchServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ doThrow(HttpServerErrorException.ServiceUnavailable.class) .when(restTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(SearchServiceConnectionException.class, () -> { @@ -98,7 +98,7 @@ public class SearchServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ doThrow(HttpClientErrorException.NotFound.class) .when(restTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(DatabaseNotFoundException.class, () -> { 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 index 9b778e67fb..6505506eea 100644 --- 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 @@ -1,13 +1,7 @@ package at.tuwien.mapper; -import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.database.ViewBriefDto; -import at.tuwien.api.database.ViewDto; -import at.tuwien.api.database.table.TableBriefDto; -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.identifier.Identifier; import at.tuwien.entities.identifier.IdentifierType; import at.tuwien.test.AbstractUnitTest; diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java index c647cdbd74..1c96e6283d 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java @@ -3,7 +3,7 @@ package at.tuwien.service; import at.tuwien.exception.*; import at.tuwien.test.AbstractUnitTest; import at.tuwien.api.database.AccessTypeDto; -import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.DatabaseBriefDto; import at.tuwien.entities.database.AccessType; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; @@ -80,7 +80,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(Void.class))) .thenReturn(ResponseEntity.status(HttpStatus.CREATED) .build()); - when(searchServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class))) + when(searchServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class))) .thenReturn(ResponseEntity.accepted() .build()); @@ -155,7 +155,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpClientErrorException.BadRequest.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(SearchServiceException.class, () -> { @@ -174,7 +174,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpClientErrorException.Unauthorized.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(SearchServiceException.class, () -> { @@ -193,7 +193,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpClientErrorException.NotFound.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(DatabaseNotFoundException.class, () -> { @@ -212,7 +212,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpServerErrorException.InternalServerError.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(SearchServiceConnectionException.class, () -> { @@ -230,7 +230,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class))) .thenReturn(ResponseEntity.accepted() .build()); - when(searchServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class))) + when(searchServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class))) .thenReturn(ResponseEntity.accepted() .build()); @@ -305,7 +305,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpClientErrorException.BadRequest.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(SearchServiceException.class, () -> { @@ -324,7 +324,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpClientErrorException.Unauthorized.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(SearchServiceException.class, () -> { @@ -343,7 +343,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpClientErrorException.NotFound.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(DatabaseNotFoundException.class, () -> { @@ -362,7 +362,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpServerErrorException.InternalServerError.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(SearchServiceConnectionException.class, () -> { @@ -382,7 +382,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.DELETE), any(HttpEntity.class), eq(Void.class))) .thenReturn(ResponseEntity.accepted() .build()); - when(searchServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class))) + when(searchServiceRestTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class))) .thenReturn(ResponseEntity.accepted() .build()); @@ -445,7 +445,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpClientErrorException.BadRequest.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(SearchServiceException.class, () -> { @@ -466,7 +466,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpClientErrorException.Unauthorized.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(SearchServiceException.class, () -> { @@ -487,7 +487,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpClientErrorException.NotFound.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(DatabaseNotFoundException.class, () -> { @@ -508,7 +508,7 @@ public class AccessServiceUnitTest extends AbstractUnitTest { .build()); doThrow(HttpServerErrorException.InternalServerError.class) .when(searchServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseDto.class)); + .exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(DatabaseBriefDto.class)); /* test */ assertThrows(SearchServiceConnectionException.class, () -> { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServicePersistenceTest.java index 182fe8e14a..b77bc30d38 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServicePersistenceTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServicePersistenceTest.java @@ -1,20 +1,20 @@ package at.tuwien.service; +import at.tuwien.api.datacite.DataCiteBody; +import at.tuwien.api.datacite.doi.DataCiteDoi; import at.tuwien.api.identifier.BibliographyTypeDto; +import at.tuwien.entities.database.Database; import at.tuwien.entities.identifier.Creator; import at.tuwien.entities.identifier.Identifier; import at.tuwien.entities.identifier.IdentifierStatusType; import at.tuwien.entities.identifier.NameIdentifierSchemeType; +import at.tuwien.exception.*; +import at.tuwien.gateway.SearchServiceGateway; import at.tuwien.repository.ContainerRepository; import at.tuwien.repository.DatabaseRepository; import at.tuwien.repository.LicenseRepository; import at.tuwien.repository.UserRepository; import at.tuwien.test.AbstractUnitTest; -import at.tuwien.api.datacite.DataCiteBody; -import at.tuwien.api.datacite.doi.DataCiteDoi; -import at.tuwien.entities.database.Database; -import at.tuwien.exception.*; -import at.tuwien.gateway.SearchServiceGateway; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -141,7 +141,7 @@ public class DataCiteIdentifierServicePersistenceTest extends AbstractUnitTest { when(restTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(dataCiteBodyParameterizedTypeReference))) .thenReturn(mock); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ dataCiteIdentifierService.save(DATABASE_1, USER_1, IDENTIFIER_1_SAVE_DTO); @@ -156,7 +156,7 @@ public class DataCiteIdentifierServicePersistenceTest extends AbstractUnitTest { .when(restTemplate) .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(dataCiteBodyParameterizedTypeReference)); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ assertThrows(MalformedException.class, () -> { @@ -173,7 +173,7 @@ public class DataCiteIdentifierServicePersistenceTest extends AbstractUnitTest { .when(restTemplate) .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(dataCiteBodyParameterizedTypeReference)); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ assertThrows(DataServiceConnectionException.class, () -> { @@ -332,7 +332,7 @@ public class DataCiteIdentifierServicePersistenceTest extends AbstractUnitTest { /* mock */ when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ dataCiteIdentifierService.delete(IDENTIFIER_1); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java index 1b6570abd8..18d037fe45 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java @@ -110,7 +110,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Database response = databaseService.modifyImage(DATABASE_1, image); @@ -164,7 +164,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Database response = databaseService.updateViewMetadata(DATABASE_1); @@ -222,7 +222,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Database response = databaseService.updateViewMetadata(DATABASE_1); @@ -240,7 +240,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Database response = databaseService.updateViewMetadata(DATABASE_1); @@ -258,7 +258,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Database response = databaseService.updateTableMetadata(DATABASE_1); @@ -276,7 +276,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Database response = databaseService.updateTableMetadata(DATABASE_1); @@ -294,7 +294,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Database response = databaseService.updateTableMetadata(DATABASE_1); @@ -517,7 +517,7 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest { /* mock */ when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServicePersistenceTest.java index 0c87dcdd69..40fc28fe4d 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServicePersistenceTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServicePersistenceTest.java @@ -176,7 +176,7 @@ public class IdentifierServicePersistenceTest extends AbstractUnitTest { when(dataServiceGateway.findQuery(IDENTIFIER_5_DATABASE_ID, IDENTIFIER_5_QUERY_ID)) .thenReturn(QUERY_2_DTO); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_2_DTO); + .thenReturn(DATABASE_2_BRIEF_DTO); /* test */ identifierService.save(DATABASE_2, USER_2, IDENTIFIER_5_SAVE_DTO); @@ -286,7 +286,7 @@ public class IdentifierServicePersistenceTest extends AbstractUnitTest { /* mock */ when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ identifierService.delete(IDENTIFIER_1); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServicePersistenceTest.java index 3126f9e9f4..5250f7e106 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServicePersistenceTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServicePersistenceTest.java @@ -112,7 +112,7 @@ public class TableServicePersistenceTest extends AbstractUnitTest { .when(dataServiceGateway) .createTable(DATABASE_1_ID, request); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Table response = tableService.createTable(DATABASE_1, request, USER_1_PRINCIPAL); 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 d975e808e3..5fb8e9ad7e 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 @@ -135,7 +135,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ tableService.updateStatistics(TABLE_8); @@ -224,7 +224,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final TableColumn response = tableService.update(TABLE_1_COLUMNS.get(0), request); @@ -256,7 +256,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final TableColumn response = tableService.update(TABLE_1_COLUMNS.get(0), request); @@ -279,7 +279,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Table response = tableService.createTable(DATABASE_1, TABLE_3_CREATE_DTO, USER_1_PRINCIPAL); @@ -316,7 +316,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Table response = tableService.createTable(DATABASE_1, request, USER_1_PRINCIPAL); @@ -369,7 +369,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ assertThrows(MalformedException.class, () -> { @@ -392,7 +392,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { .when(dataServiceGateway) .createTable(DATABASE_1_ID, TABLE_3_CREATE_DTO); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final Table response = tableService.createTable(DATABASE_1, TABLE_3_CREATE_DTO, USER_1_PRINCIPAL); @@ -413,7 +413,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { .when(dataServiceGateway) .createTable(DATABASE_1_ID, TABLE_5_CREATE_DTO); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ assertThrows(DataServiceException.class, () -> { @@ -511,7 +511,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { .when(dataServiceGateway) .deleteTable(DATABASE_1_ID, TABLE_1_ID); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ tableService.deleteTable(TABLE_1); @@ -527,7 +527,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { .when(dataServiceGateway) .deleteTable(DATABASE_1_ID, TABLE_4_ID); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ tableService.deleteTable(TABLE_4); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServicePersistenceTest.java index 8ca002472a..57a84965dc 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServicePersistenceTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServicePersistenceTest.java @@ -89,7 +89,7 @@ public class ViewServicePersistenceTest extends AbstractUnitTest { .when(dataServiceGateway) .deleteView(DATABASE_1_ID, VIEW_1_ID); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ viewService.delete(VIEW_1); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceUnitTest.java index cd9fe03c65..c63d207e6e 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceUnitTest.java @@ -62,7 +62,7 @@ public class ViewServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ final View response = viewService.create(DATABASE_1, USER_1, request); @@ -117,7 +117,7 @@ public class ViewServiceUnitTest extends AbstractUnitTest { when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_BRIEF_DTO); /* test */ viewService.delete(VIEW_1); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/SearchServiceGateway.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/SearchServiceGateway.java index f5e2f49c02..6632a08194 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/SearchServiceGateway.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/SearchServiceGateway.java @@ -1,12 +1,12 @@ package at.tuwien.gateway; -import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.DatabaseBriefDto; import at.tuwien.entities.database.Database; import at.tuwien.exception.*; public interface SearchServiceGateway { - DatabaseDto update(Database database) throws SearchServiceConnectionException, SearchServiceException, DatabaseNotFoundException; + DatabaseBriefDto update(Database database) throws SearchServiceConnectionException, SearchServiceException, DatabaseNotFoundException; void delete(Long databaseId) throws SearchServiceConnectionException, SearchServiceException, DatabaseNotFoundException; } 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 0f14b8d348..503cad47ec 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 @@ -1,6 +1,6 @@ package at.tuwien.gateway.impl; -import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.DatabaseBriefDto; import at.tuwien.config.GatewayConfig; import at.tuwien.entities.database.Database; import at.tuwien.exception.DatabaseNotFoundException; @@ -35,8 +35,8 @@ public class SearchServiceGatewayImpl implements SearchServiceGateway { } @Override - public DatabaseDto update(Database database) throws SearchServiceConnectionException, SearchServiceException, DatabaseNotFoundException { - final ResponseEntity<DatabaseDto> response; + public DatabaseBriefDto update(Database database) throws SearchServiceConnectionException, SearchServiceException, DatabaseNotFoundException { + final ResponseEntity<DatabaseBriefDto> response; final HttpHeaders headers = new HttpHeaders(); headers.set("Accept", "application/json"); headers.set("Content-Type", "application/json"); @@ -44,7 +44,7 @@ public class SearchServiceGatewayImpl implements SearchServiceGateway { log.trace("update database at endpoint {} with path {}", gatewayConfig.getSearchEndpoint(), path); try { response = restTemplate.exchange(path, HttpMethod.PUT, new HttpEntity<>( - metadataMapper.databaseToPrivilegedDatabaseDto(database), headers), DatabaseDto.class); + metadataMapper.databaseToDatabaseDto(database), headers), DatabaseBriefDto.class); } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable | HttpServerErrorException.InternalServerError e) { log.error("Failed to update database: {}", e.getMessage()); 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 91936adaf8..232b4cd280 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 @@ -12,7 +12,7 @@ public abstract class AbstractUnitTest extends BaseTest { public void genesis() { IMAGE_1_DTO.setOperators(IMAGE_1_OPERATORS_DTO); - CONTAINER_1_PRIVILEGED_DTO.setImage(IMAGE_1_DTO); + CONTAINER_1_DTO.setImage(IMAGE_1_DTO); IMAGE_1.setOperators(new LinkedList<>(IMAGE_1_OPERATORS)); CONTAINER_1.setDatabases(new LinkedList<>(List.of(DATABASE_1, DATABASE_2, DATABASE_3))); CONTAINER_4.setDatabases(new LinkedList<>(List.of(DATABASE_4))); @@ -36,13 +36,12 @@ public abstract class AbstractUnitTest extends BaseTest { DATABASE_1.setIsSchemaPublic(DATABASE_1_SCHEMA_PUBLIC); DATABASE_1_USER_1_READ_ACCESS.setType(AccessType.READ); 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))); + DATABASE_1_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); UNIT_1.setId(UNIT_1_ID); TABLE_1.setColumns(new LinkedList<>(TABLE_1_COLUMNS)); TABLE_1.setConstraints(TABLE_1_CONSTRAINTS); - TABLE_1_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_1_COLUMNS_DTO)); - TABLE_1_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); + TABLE_1_DTO.setColumns(new LinkedList<>(TABLE_1_COLUMNS_DTO)); VIEW_1_DTO.setIdentifiers(VIEW_1_DTO_IDENTIFIERS); DATABASE_1.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_1, IDENTIFIER_2, IDENTIFIER_3, IDENTIFIER_4))); IDENTIFIER_1.setDatabase(DATABASE_1); @@ -51,18 +50,17 @@ public abstract class AbstractUnitTest extends BaseTest { IDENTIFIER_4.setDatabase(DATABASE_1); DATABASE_1.setTables(new LinkedList<>(List.of(TABLE_1, TABLE_2, TABLE_3, TABLE_4))); DATABASE_1.setViews(new LinkedList<>(List.of(VIEW_1, VIEW_2, VIEW_3))); - DATABASE_1_PRIVILEGED_DTO.setContainer(CONTAINER_1_PRIVILEGED_DTO); - DATABASE_1_PRIVILEGED_DTO.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO))); - DATABASE_1_PRIVILEGED_DTO.setTables(new LinkedList<>(List.of(TABLE_1_DTO, TABLE_2_DTO, TABLE_3_DTO, TABLE_4_DTO))); - DATABASE_1_PRIVILEGED_DTO.setViews(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO))); + DATABASE_1_DTO.setContainer(CONTAINER_1_DTO); + DATABASE_1_DTO.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO))); + DATABASE_1_DTO.setTables(new LinkedList<>(List.of(TABLE_1_DTO, TABLE_2_DTO, TABLE_3_DTO, TABLE_4_DTO))); + DATABASE_1_DTO.setViews(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO))); TABLE_1_DTO.setColumns(new LinkedList<>(TABLE_1_COLUMNS_DTO)); TABLE_1_DTO.setConstraints(TABLE_1_CONSTRAINTS_DTO); TABLE_2.setDatabase(DATABASE_1); TABLE_2.setColumns(new LinkedList<>(TABLE_2_COLUMNS)); TABLE_2_CONSTRAINTS.getForeignKeys().get(0).getReferences().get(0).setForeignKey(TABLE_2_CONSTRAINTS.getForeignKeys().get(0)); TABLE_2.setConstraints(TABLE_2_CONSTRAINTS); - TABLE_2_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_2_COLUMNS_DTO)); - TABLE_2_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); + TABLE_2_DTO.setColumns(new LinkedList<>(TABLE_2_COLUMNS_DTO)); TABLE_2_DTO.setColumns(new LinkedList<>(TABLE_2_COLUMNS_DTO)); TABLE_2_DTO.setConstraints(TABLE_2_CONSTRAINTS_DTO); TABLE_3.setDatabase(DATABASE_1); @@ -78,13 +76,10 @@ public abstract class AbstractUnitTest extends BaseTest { VIEW_1.setDatabase(DATABASE_1); VIEW_1.setColumns(new LinkedList<>(VIEW_1_COLUMNS)); VIEW_1.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_3))); - VIEW_1_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); VIEW_2.setDatabase(DATABASE_1); VIEW_2.setColumns(new LinkedList<>(VIEW_2_COLUMNS)); - VIEW_2_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); VIEW_3.setDatabase(DATABASE_1); VIEW_3.setColumns(new LinkedList<>(VIEW_3_COLUMNS)); - VIEW_3_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); IDENTIFIER_1.setDatabase(DATABASE_1); IDENTIFIER_2.setDatabase(DATABASE_1); IDENTIFIER_3.setDatabase(DATABASE_1); @@ -92,20 +87,19 @@ public abstract class AbstractUnitTest extends BaseTest { /* 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_PRIVILEGED_DTO.setAccesses(new LinkedList<>(List.of(DATABASE_2_USER_2_WRITE_ALL_ACCESS_DTO, DATABASE_2_USER_3_READ_ACCESS_DTO))); + DATABASE_2_DTO.setAccesses(new LinkedList<>(List.of(DATABASE_2_USER_2_WRITE_ALL_ACCESS_DTO, DATABASE_2_USER_3_READ_ACCESS_DTO))); DATABASE_2.setTables(new LinkedList<>(List.of(TABLE_5, TABLE_6, TABLE_7))); VIEW_4.setColumns(new LinkedList<>(VIEW_4_COLUMNS)); DATABASE_2.setViews(new LinkedList<>(List.of(VIEW_4))); DATABASE_2.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_5))); - DATABASE_2_PRIVILEGED_DTO.setTables(new LinkedList<>(List.of(TABLE_5_DTO, TABLE_6_DTO, TABLE_7_DTO))); - DATABASE_2_PRIVILEGED_DTO.setViews(new LinkedList<>(List.of(VIEW_4_DTO))); - DATABASE_2_PRIVILEGED_DTO.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_5_DTO))); + DATABASE_2_DTO.setTables(new LinkedList<>(List.of(TABLE_5_DTO, TABLE_6_DTO, TABLE_7_DTO))); + DATABASE_2_DTO.setViews(new LinkedList<>(List.of(VIEW_4_DTO))); + DATABASE_2_DTO.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_5_DTO))); TABLE_5.setDatabase(DATABASE_2); TABLE_5.setColumns(new LinkedList<>(TABLE_5_COLUMNS)); TABLE_5.setConstraints(TABLE_5_CONSTRAINTS); - TABLE_5_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_5_COLUMNS_DTO)); - TABLE_5_PRIVILEGED_DTO.setConstraints(TABLE_5_CONSTRAINTS_DTO); - TABLE_5_PRIVILEGED_DTO.setDatabase(DATABASE_2_PRIVILEGED_DTO); + TABLE_5_DTO.setColumns(new LinkedList<>(TABLE_5_COLUMNS_DTO)); + TABLE_5_DTO.setConstraints(TABLE_5_CONSTRAINTS_DTO); TABLE_5_DTO.setColumns(TABLE_5_COLUMNS_DTO); TABLE_5_DTO.setConstraints(TABLE_5_CONSTRAINTS_DTO); TABLE_6.setDatabase(DATABASE_2); @@ -128,27 +122,31 @@ public abstract class AbstractUnitTest extends BaseTest { DATABASE_3.setTables(new LinkedList<>(List.of(TABLE_8))); DATABASE_3.setViews(new LinkedList<>(List.of(VIEW_5))); DATABASE_3.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_6))); + DATABASE_3_DTO.setTables(new LinkedList<>(List.of(TABLE_8_DTO))); + DATABASE_3_DTO.setViews(new LinkedList<>(List.of(VIEW_5_DTO))); + DATABASE_3_DTO.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_6_DTO))); TABLE_8.setDatabase(DATABASE_3); TABLE_8.setColumns(new LinkedList<>(TABLE_8_COLUMNS)); TABLE_8.setConstraints(TABLE_8_CONSTRAINTS); TABLE_8_DTO.setColumns(new LinkedList<>(TABLE_8_COLUMNS_DTO)); TABLE_8_DTO.setConstraints(TABLE_8_CONSTRAINTS_DTO); - TABLE_8_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_8_COLUMNS_DTO)); - TABLE_8_PRIVILEGED_DTO.setConstraints(TABLE_8_CONSTRAINTS_DTO); - TABLE_8_PRIVILEGED_DTO.setDatabase(DATABASE_3_PRIVILEGED_DTO); + TABLE_8_DTO.setColumns(new LinkedList<>(TABLE_8_COLUMNS_DTO)); + TABLE_8_DTO.setConstraints(TABLE_8_CONSTRAINTS_DTO); VIEW_5.setDatabase(DATABASE_3); VIEW_5.setColumns(VIEW_5_COLUMNS); VIEW_5_DTO.setColumns(VIEW_5_COLUMNS_DTO); 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))); + DATABASE_4_DTO.setTables(new LinkedList<>(List.of(TABLE_9_DTO))); + DATABASE_4_DTO.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_7_DTO))); TABLE_9.setDatabase(DATABASE_4); TABLE_9.setColumns(TABLE_9_COLUMNS); TABLE_9.setConstraints(TABLE_9_CONSTRAINTS); TABLE_9_DTO.setColumns(TABLE_9_COLUMNS_DTO); TABLE_9_DTO.setConstraints(TABLE_9_CONSTRAINTS_DTO); - 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.setStatus(IdentifierStatusType.DRAFT); 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 368b1d182c..12dbd35efc 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 @@ -11,11 +11,8 @@ import at.tuwien.api.auth.SignupRequestDto; import at.tuwien.api.container.ContainerBriefDto; import at.tuwien.api.container.ContainerDto; import at.tuwien.api.container.image.*; -import at.tuwien.api.container.internal.PrivilegedContainerDto; import at.tuwien.api.database.*; import at.tuwien.api.database.internal.CreateDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedDatabaseDto; -import at.tuwien.api.database.internal.PrivilegedViewDto; import at.tuwien.api.database.query.QueryBriefDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.table.TableBriefDto; @@ -29,7 +26,6 @@ import at.tuwien.api.database.table.constraints.ConstraintsDto; import at.tuwien.api.database.table.constraints.foreign.*; import at.tuwien.api.database.table.constraints.primary.PrimaryKeyDto; import at.tuwien.api.database.table.constraints.unique.UniqueDto; -import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.api.datacite.DataCiteBody; import at.tuwien.api.datacite.DataCiteData; import at.tuwien.api.datacite.doi.DataCiteDoi; @@ -53,7 +49,6 @@ import at.tuwien.api.semantics.*; import at.tuwien.api.user.UserAttributesDto; import at.tuwien.api.user.UserDto; import at.tuwien.api.user.*; -import at.tuwien.api.user.internal.PrivilegedUserDto; import at.tuwien.api.user.internal.UpdateUserPasswordDto; import at.tuwien.entities.container.Container; import at.tuwien.entities.container.image.ContainerImage; @@ -553,7 +548,7 @@ public abstract class BaseTest { .build()) .build(); - public final static PrivilegedUserDto USER_1_PRIVILEGED_DTO = PrivilegedUserDto.builder() + public final static UserDto USER_1_PRIVILEGED_DTO = UserDto.builder() .id(USER_1_ID) .username(USER_1_USERNAME) .password(USER_1_PASSWORD) @@ -741,7 +736,7 @@ public abstract class BaseTest { .tags(new String[]{}) .build(); - public final static PrivilegedUserDto USER_2_PRIVILEGED_DTO = PrivilegedUserDto.builder() + public final static UserDto USER_2_PRIVILEGED_DTO = UserDto.builder() .id(USER_2_ID) .username(USER_2_USERNAME) .password(USER_2_PASSWORD) @@ -843,7 +838,7 @@ public abstract class BaseTest { .tags(new String[]{}) .build(); - public final static PrivilegedUserDto USER_3_PRIVILEGED_DTO = PrivilegedUserDto.builder() + public final static UserDto USER_3_PRIVILEGED_DTO = UserDto.builder() .id(USER_3_ID) .username(USER_3_USERNAME) .password(USER_3_PASSWORD) @@ -925,7 +920,7 @@ public abstract class BaseTest { public final static Principal USER_4_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_4_DETAILS, USER_4_PASSWORD, USER_4_DETAILS.getAuthorities()); - public final static PrivilegedUserDto USER_4_PRIVILEGED_DTO = PrivilegedUserDto.builder() + public final static UserDto USER_4_PRIVILEGED_DTO = UserDto.builder() .id(USER_4_ID) .username(USER_4_USERNAME) .password(USER_4_PASSWORD) @@ -973,7 +968,7 @@ public abstract class BaseTest { .attributes(USER_5_ATTRIBUTES_DTO) .build(); - public final static PrivilegedUserDto USER_5_PRIVILEGED_DTO = PrivilegedUserDto.builder() + public final static UserDto USER_5_PRIVILEGED_DTO = UserDto.builder() .id(USER_5_ID) .username(USER_5_USERNAME) .firstname(USER_5_FIRSTNAME) @@ -1187,7 +1182,7 @@ public abstract class BaseTest { .image(IMAGE_1_BRIEF_DTO) .build(); - public final static PrivilegedContainerDto CONTAINER_1_PRIVILEGED_DTO = PrivilegedContainerDto.builder() + public final static ContainerDto CONTAINER_1_PRIVILEGED_DTO = ContainerDto.builder() .id(CONTAINER_1_ID) .name(CONTAINER_1_NAME) .internalName(CONTAINER_1_INTERNALNAME) @@ -1243,7 +1238,7 @@ public abstract class BaseTest { .quota(CONTAINER_2_QUOTA) .build(); - public final static PrivilegedContainerDto CONTAINER_2_PRIVILEGED_DTO = PrivilegedContainerDto.builder() + public final static ContainerDto CONTAINER_2_PRIVILEGED_DTO = ContainerDto.builder() .id(CONTAINER_2_ID) .name(CONTAINER_2_NAME) .internalName(CONTAINER_2_INTERNALNAME) @@ -1385,11 +1380,21 @@ public abstract class BaseTest { .id(DATABASE_3_ID) .isPublic(DATABASE_3_PUBLIC) .name(DATABASE_3_NAME) - .container(CONTAINER_1_BRIEF_DTO) .internalName(DATABASE_3_INTERNALNAME) + .owner(USER_3_BRIEF_DTO) + .container(CONTAINER_1_DTO) .exchangeName(DATABASE_3_EXCHANGE) - .tables(new LinkedList<>()) /* TABLE_3, TABLE_3, TABLE_3 */ - .views(new LinkedList<>()) + .tables(new LinkedList<>()) /* TABLE_8_DTO */ + .views(new LinkedList<>()) /* VIEW_5_DTO */ + .identifiers(new LinkedList<>()) /* IDENTIFIER_6_DTO */ + .build(); + + public final static DatabaseBriefDto DATABASE_3_BRIEF_DTO = DatabaseBriefDto.builder() + .id(DATABASE_3_ID) + .isPublic(DATABASE_3_PUBLIC) + .name(DATABASE_3_NAME) + .internalName(DATABASE_3_INTERNALNAME) + .ownerId(USER_3_ID) .identifiers(new LinkedList<>()) .build(); @@ -1411,6 +1416,17 @@ public abstract class BaseTest { public final static UUID DATABASE_4_OWNER = USER_4_ID; public final static UUID DATABASE_4_CREATOR = USER_4_ID; + public final static DatabaseBriefDto DATABASE_4_BRIEF_DTO = DatabaseBriefDto.builder() + .id(DATABASE_4_ID) + .isPublic(DATABASE_4_PUBLIC) + .isSchemaPublic(DATABASE_4_SCHEMA_PUBLIC) + .name(DATABASE_4_NAME) + .description(DATABASE_4_DESCRIPTION) + .internalName(DATABASE_4_INTERNALNAME) + .ownerId(USER_4_ID) + .identifiers(new LinkedList<>()) + .build(); + public final static DatabaseDto DATABASE_4_DTO = DatabaseDto.builder() .id(DATABASE_4_ID) .isPublic(DATABASE_4_PUBLIC) @@ -1420,9 +1436,9 @@ public abstract class BaseTest { .internalName(DATABASE_4_INTERNALNAME) .exchangeName(DATABASE_4_EXCHANGE) .owner(USER_4_BRIEF_DTO) - .tables(new LinkedList<>()) + .tables(new LinkedList<>()) /* TABLE_9_DTO */ .views(new LinkedList<>()) - .identifiers(new LinkedList<>()) + .identifiers(new LinkedList<>()) /* IDENTIFIER_7_DTO */ .build(); public final static TableCreateDto TABLE_0_CREATE_DTO = TableCreateDto.builder() @@ -1612,10 +1628,9 @@ public abstract class BaseTest { 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) */; - public final static PrivilegedTableDto TABLE_1_PRIVILEGED_DTO = PrivilegedTableDto.builder() + public final static TableDto TABLE_1_PRIVILEGED_DTO = TableDto.builder() .id(TABLE_1_ID) .tdbid(DATABASE_1_ID) - .database(null) /* DATABASE_1_PRIVILEGED_DTO */ .internalName(TABLE_1_INTERNAL_NAME) .isVersioned(TABLE_1_VERSIONED) .isPublic(TABLE_1_SCHEMA_PUBLIC) @@ -1826,10 +1841,9 @@ public abstract class BaseTest { .maxDataLength(TABLE_2_MAX_DATA_LENGTH) .build(); - public final static PrivilegedTableDto TABLE_2_PRIVILEGED_DTO = PrivilegedTableDto.builder() + public final static TableDto TABLE_2_PRIVILEGED_DTO = TableDto.builder() .id(TABLE_2_ID) .tdbid(DATABASE_1_ID) - .database(null) /* DATABASE_1_PRIVILEGED_DTO */ .internalName(TABLE_2_INTERNALNAME) .isVersioned(TABLE_2_VERSIONED) .isPublic(TABLE_2_IS_PUBLIC) @@ -2036,10 +2050,9 @@ public abstract class BaseTest { .owner(USER_1_BRIEF_DTO) .build(); - public final static PrivilegedTableDto TABLE_5_PRIVILEGED_DTO = PrivilegedTableDto.builder() + public final static TableDto TABLE_5_PRIVILEGED_DTO = TableDto.builder() .id(TABLE_5_ID) .tdbid(DATABASE_2_ID) - .database(null) /* DATABASE_2_PRIVILEGED_DTO */ .internalName(TABLE_5_INTERNALNAME) .isVersioned(TABLE_5_VERSIONED) .isPublic(TABLE_5_IS_PUBLIC) @@ -2407,7 +2420,7 @@ public abstract class BaseTest { .ownedBy(USER_1_ID) .build(); - public final static PrivilegedTableDto TABLE_8_PRIVILEGED_DTO = PrivilegedTableDto.builder() + public final static TableDto TABLE_8_PRIVILEGED_DTO = TableDto.builder() .id(TABLE_8_ID) .tdbid(TABLE_8_DATABASE_ID) .internalName(TABLE_8_INTERNAL_NAME) @@ -2483,7 +2496,7 @@ public abstract class BaseTest { .ownedBy(USER_1_ID) .build(); - public final static PrivilegedTableDto TABLE_9_PRIVILEGED_DTO = PrivilegedTableDto.builder() + public final static TableDto TABLE_9_PRIVILEGED_DTO = TableDto.builder() .id(TABLE_9_ID) .tdbid(TABLE_9_DATABASE_ID) .internalName(TABLE_9_INTERNAL_NAME) @@ -2979,7 +2992,7 @@ public abstract class BaseTest { .isPersisted(QUERY_3_PERSISTED) .resultNumber(2L) .build(); - + public final static Long QUERY_7_ID = 7L; public final static String QUERY_7_STATEMENT = "SELECT id, date, a.location, lat, lng FROM weather_aus a JOIN weather_location l on a.location = l.location WHERE date = '2008-12-01'"; public final static String QUERY_7_QUERY_HASH = "df7da3801dfb5c191ff6711d79ce6455f3c09ec8323ce1ff7208ab85387263f5"; @@ -5117,10 +5130,9 @@ public abstract class BaseTest { .columns(VIEW_1_COLUMNS_DTO) .build(); - public final static PrivilegedViewDto VIEW_1_PRIVILEGED_DTO = PrivilegedViewDto.builder() + public final static ViewDto VIEW_1_PRIVILEGED_DTO = ViewDto.builder() .id(VIEW_1_ID) .isInitialView(VIEW_1_INITIAL_VIEW) - .database(null) /* DATABASE_1_PRIVILEGED_DTO */ .name(VIEW_1_NAME) .internalName(VIEW_1_INTERNAL_NAME) .vdbid(VIEW_1_DATABASE_ID) @@ -5279,10 +5291,9 @@ public abstract class BaseTest { .owner(USER_1_BRIEF_DTO) .build(); - public final static PrivilegedViewDto VIEW_2_PRIVILEGED_DTO = PrivilegedViewDto.builder() + public final static ViewDto VIEW_2_PRIVILEGED_DTO = ViewDto.builder() .id(VIEW_2_ID) .isInitialView(VIEW_2_INITIAL_VIEW) - .database(null) /* DATABASE_1_PRIVILEGED_DTO */ .name(VIEW_2_NAME) .internalName(VIEW_2_INTERNAL_NAME) .vdbid(VIEW_2_DATABASE_ID) @@ -5380,10 +5391,9 @@ public abstract class BaseTest { .owner(USER_1) .build(); - public final static PrivilegedViewDto VIEW_3_PRIVILEGED_DTO = PrivilegedViewDto.builder() + public final static ViewDto VIEW_3_PRIVILEGED_DTO = ViewDto.builder() .id(VIEW_3_ID) .isInitialView(VIEW_3_INITIAL_VIEW) - .database(null) /* DATABASE_1_PRIVILEGED_DTO */ .name(VIEW_3_NAME) .internalName(VIEW_3_INTERNAL_NAME) .vdbid(VIEW_3_DATABASE_ID) @@ -7518,27 +7528,20 @@ public abstract class BaseTest { .id(DATABASE_1_ID) .isPublic(DATABASE_1_PUBLIC) .name(DATABASE_1_NAME) - .container(CONTAINER_1_BRIEF_DTO) + .container(CONTAINER_1_DTO) .internalName(DATABASE_1_INTERNALNAME) .exchangeName(DATABASE_1_EXCHANGE) - .identifiers(new LinkedList<>(List.of(IDENTIFIER_1_BRIEF_DTO, IDENTIFIER_2_BRIEF_DTO, IDENTIFIER_3_BRIEF_DTO, IDENTIFIER_4_BRIEF_DTO))) - .tables(new LinkedList<>(List.of(TABLE_1_BRIEF_DTO, TABLE_2_BRIEF_DTO, TABLE_3_BRIEF_DTO, TABLE_4_BRIEF_DTO))) - .views(new LinkedList<>(List.of(VIEW_1_BRIEF_DTO, VIEW_2_BRIEF_DTO, VIEW_3_BRIEF_DTO))) + .identifiers(new LinkedList<>(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO))) + .tables(new LinkedList<>(List.of(TABLE_1_DTO, TABLE_2_DTO, TABLE_3_DTO, TABLE_4_DTO))) + .views(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO))) .build(); - public final static PrivilegedDatabaseDto DATABASE_1_PRIVILEGED_DTO = PrivilegedDatabaseDto.builder() + public final static DatabaseBriefDto DATABASE_1_BRIEF_DTO = DatabaseBriefDto.builder() .id(DATABASE_1_ID) .isPublic(DATABASE_1_PUBLIC) - .isSchemaPublic(DATABASE_1_SCHEMA_PUBLIC) .name(DATABASE_1_NAME) - .container(CONTAINER_1_PRIVILEGED_DTO) .internalName(DATABASE_1_INTERNALNAME) - .exchangeName(DATABASE_1_EXCHANGE) - .identifiers(new LinkedList<>(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO))) - .tables(new LinkedList<>(List.of(TABLE_1_DTO, TABLE_2_DTO, TABLE_3_DTO, TABLE_4_DTO))) - .views(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO))) - .owner(USER_1_BRIEF_DTO) - .lastRetrieved(Instant.now()) + .identifiers(new LinkedList<>(List.of(IDENTIFIER_1_BRIEF_DTO, IDENTIFIER_2_BRIEF_DTO, IDENTIFIER_3_BRIEF_DTO, IDENTIFIER_4_BRIEF_DTO))) .build(); public final static DatabaseAccess DATABASE_1_USER_1_READ_ACCESS = DatabaseAccess.builder() @@ -7672,7 +7675,7 @@ public abstract class BaseTest { .identifiers(new LinkedList<>()) .build(); - public final static PrivilegedDatabaseDto DATABASE_2_PRIVILEGED_DTO = PrivilegedDatabaseDto.builder() + public final static DatabaseDto DATABASE_2_DTO = DatabaseDto.builder() .id(DATABASE_2_ID) .isPublic(DATABASE_2_PUBLIC) .isSchemaPublic(DATABASE_2_SCHEMA_PUBLIC) @@ -7687,18 +7690,13 @@ public abstract class BaseTest { .lastRetrieved(Instant.now()) .build(); - public final static DatabaseDto DATABASE_2_DTO = DatabaseDto.builder() + public final static DatabaseBriefDto DATABASE_2_BRIEF_DTO = DatabaseBriefDto.builder() .id(DATABASE_2_ID) .isPublic(DATABASE_2_PUBLIC) .name(DATABASE_2_NAME) - .container(CONTAINER_1_BRIEF_DTO) .internalName(DATABASE_2_INTERNALNAME) - .exchangeName(DATABASE_2_EXCHANGE) .identifiers(new LinkedList<>(List.of(IDENTIFIER_5_BRIEF_DTO))) - .tables(new LinkedList<>(List.of(TABLE_5_BRIEF_DTO, TABLE_6_BRIEF_DTO, TABLE_7_BRIEF_DTO))) - .views(new LinkedList<>(List.of(VIEW_4_BRIEF_DTO))) - .identifiers(new LinkedList<>()) - .owner(USER_2_BRIEF_DTO) + .ownerId(USER_2_ID) .build(); public final static DatabaseAccess DATABASE_2_USER_1_READ_ACCESS = DatabaseAccess.builder() @@ -7924,21 +7922,6 @@ public abstract class BaseTest { .user(USER_3_BRIEF_DTO) .build(); - public final static PrivilegedDatabaseDto DATABASE_3_PRIVILEGED_DTO = PrivilegedDatabaseDto.builder() - .id(DATABASE_3_ID) - .isPublic(DATABASE_3_PUBLIC) - .isSchemaPublic(DATABASE_3_SCHEMA_PUBLIC) - .name(DATABASE_3_NAME) - .container(CONTAINER_1_PRIVILEGED_DTO) - .internalName(DATABASE_3_INTERNALNAME) - .exchangeName(DATABASE_3_EXCHANGE) - .identifiers(new LinkedList<>(List.of(IDENTIFIER_6_DTO))) - .tables(new LinkedList<>(List.of(TABLE_8_DTO))) - .views(new LinkedList<>(List.of(VIEW_5_DTO))) - .owner(USER_3_BRIEF_DTO) - .lastRetrieved(Instant.now()) - .build(); - public final static Identifier IDENTIFIER_7 = Identifier.builder() .id(IDENTIFIER_7_ID) .descriptions(new LinkedList<>()) @@ -7985,21 +7968,6 @@ public abstract class BaseTest { .identifiers(new LinkedList<>()) .build(); - public final static PrivilegedDatabaseDto DATABASE_4_PRIVILEGED_DTO = PrivilegedDatabaseDto.builder() - .id(DATABASE_4_ID) - .isPublic(DATABASE_4_PUBLIC) - .isSchemaPublic(DATABASE_4_SCHEMA_PUBLIC) - .name(DATABASE_4_NAME) - .container(CONTAINER_1_PRIVILEGED_DTO) - .internalName(DATABASE_4_INTERNALNAME) - .exchangeName(DATABASE_4_EXCHANGE) - .identifiers(new LinkedList<>(List.of(IDENTIFIER_7_DTO))) - .tables(new LinkedList<>(List.of(TABLE_9_DTO))) - .views(new LinkedList<>(List.of())) - .owner(USER_3_BRIEF_DTO) - .lastRetrieved(Instant.now()) - .build(); - public final static DatabaseAccess DATABASE_4_USER_1_READ_ACCESS = DatabaseAccess.builder() .type(AccessType.READ) .hdbid(DATABASE_4_ID) diff --git a/dbrepo-search-service/Pipfile.lock b/dbrepo-search-service/Pipfile.lock index c789904077..8362cd2df3 100644 --- a/dbrepo-search-service/Pipfile.lock +++ b/dbrepo-search-service/Pipfile.lock @@ -360,7 +360,7 @@ }, "dbrepo": { "hashes": [ - "sha256:501b53c7e4b32774809f9685a18288da5b938fc1512e94d8b248f531ee8667fc" + "sha256:19c6bbcf9461e20681f0fb342087c618a91123d2d04d4df2f4fd1da80aa77b76" ], "path": "./lib/dbrepo-1.6.2.tar.gz" }, diff --git a/dbrepo-search-service/init/Pipfile.lock b/dbrepo-search-service/init/Pipfile.lock index 64f5fc6cc6..e72262e85d 100644 --- a/dbrepo-search-service/init/Pipfile.lock +++ b/dbrepo-search-service/init/Pipfile.lock @@ -254,10 +254,9 @@ }, "dbrepo": { "hashes": [ - "sha256:501b53c7e4b32774809f9685a18288da5b938fc1512e94d8b248f531ee8667fc" + "sha256:19c6bbcf9461e20681f0fb342087c618a91123d2d04d4df2f4fd1da80aa77b76" ], - "path": "./lib/dbrepo-1.6.2.tar.gz", - "version": "==1.6.2" + "path": "./lib/dbrepo-1.6.2.tar.gz" }, "docker": { "hashes": [ @@ -279,6 +278,7 @@ "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==2.3.3" }, "frozenlist": { @@ -643,6 +643,7 @@ "sha256:6598df0bc7a003294edd0ba88a331e0793acbb8c910c43edf398791e3b2eccda" ], "index": "pypi", + "markers": "python_version >= '3.8' and python_version < '4'", "version": "==2.8.0" }, "packaging": { @@ -933,6 +934,7 @@ "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==8.3.4" }, "python-dateutil": { @@ -940,7 +942,7 @@ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "version": "==2.9.0.post0" }, "python-dotenv": { @@ -949,6 +951,7 @@ "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==1.0.1" }, "pytz": { @@ -964,6 +967,7 @@ "sha256:f3dcb4c106a8cd9e060d92f43d593d09ebc3d07adc244f4c7315856a12e383ee" ], "index": "pypi", + "markers": "python_full_version >= '3.8.1' and python_full_version < '4.0.0'", "version": "==7.1.3" }, "requests": { @@ -979,7 +983,7 @@ "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "version": "==1.17.0" }, "testcontainers-core": { @@ -994,6 +998,7 @@ "sha256:0bdf270b5b7f53915832f7c31dd2bd3ffdc20b534ea6b32231cc7003049bd0e1" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==0.0.1rc1" }, "tinydb": { @@ -1285,6 +1290,7 @@ "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f" ], "index": "pypi", + "markers": "python_version >= '3.9'", "version": "==7.6.10" }, "iniconfig": { @@ -1317,6 +1323,7 @@ "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==8.3.4" } } diff --git a/dbrepo-search-service/init/lib/dbrepo-1.6.2.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.6.2.tar.gz index 58081673e955d89fccf70c9161037a725b647f71..02ed2aec31c2b1881165a12d45060ed4a311192d 100644 GIT binary patch delta 39303 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 R=>>ny-)xbb%dmiz0RTX?Dir_# delta 39288 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 LHtuT-3s@NdgDHfU diff --git a/dbrepo-search-service/lib/dbrepo-1.6.2.tar.gz b/dbrepo-search-service/lib/dbrepo-1.6.2.tar.gz index 58081673e955d89fccf70c9161037a725b647f71..02ed2aec31c2b1881165a12d45060ed4a311192d 100644 GIT binary patch delta 39303 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 R=>>ny-)xbb%dmiz0RTX?Dir_# delta 39288 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 LHtuT-3s@NdgDHfU diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue index 3d718d5739..f953071136 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/settings.vue @@ -24,7 +24,7 @@ v-model="modify.description" rows="2" :rules="[ - v => !max(v, 180) || ($t('validation.max-length') + 180), + v => max(v, 180) || ($t('validation.max-length') + 180), ]" clearable counter="180" diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py index fa7eb063fc..a8518ec79e 100644 --- a/lib/python/dbrepo/api/dto.py +++ b/lib/python/dbrepo/api/dto.py @@ -511,6 +511,7 @@ class CreateTableColumn(BaseModel): name: str type: ColumnType null_allowed: bool + description: Optional[str] = None concept_uri: Optional[str] = None unit_uri: Optional[str] = None index_length: Optional[int] = None @@ -622,6 +623,21 @@ class Identifier(BaseModel): publication_month: Optional[int] = None +class IdentifierBrief(BaseModel): + id: int + database_id: int + type: IdentifierType + created_by: str + status: IdentifierStatusType + publication_year: int + publisher: str + titles: List[IdentifierTitle] + doi: Optional[str] = None + query_id: Optional[int] = None + table_id: Optional[int] = None + view_id: Optional[int] = None + + class View(BaseModel): id: int database_id: int @@ -961,7 +977,7 @@ class ColumnMinimal(BaseModel): database_id: int -class Database(BaseModel): +class DatabaseBrief(BaseModel): id: int name: str owner: UserBrief @@ -971,26 +987,36 @@ class Database(BaseModel): is_public: bool is_schema_public: bool container: ContainerBrief - identifiers: Optional[List[Identifier]] = field(default_factory=list) - subsets: Optional[List[Identifier]] = field(default_factory=list) + identifiers: Optional[List[IdentifierBrief]] = field(default_factory=list) + subsets: Optional[List[IdentifierBrief]] = field(default_factory=list) + preview_image: Optional[str] = None description: Optional[str] = None - tables: Optional[List[Table]] = field(default_factory=list) - views: Optional[List[View]] = field(default_factory=list) + tables: Optional[List[TableBrief]] = field(default_factory=list) + views: Optional[List[ViewBrief]] = field(default_factory=list) image: Optional[str] = None accesses: Optional[List[DatabaseAccess]] = field(default_factory=list) - exchange_type: Optional[str] = None + exchange_name: Optional[str] = None -class DatabaseBrief(BaseModel): +class Database(BaseModel): id: int name: str + owner: UserBrief + contact: UserBrief + exchange_name: str internal_name: str - description: Optional[str] = None is_public: bool is_schema_public: bool + container: ContainerBrief identifiers: Optional[List[Identifier]] = field(default_factory=list) - contact: UserBrief - owner_id: str + subsets: Optional[List[Identifier]] = field(default_factory=list) + preview_image: Optional[str] = None + description: Optional[str] = None + tables: Optional[List[Table]] = field(default_factory=list) + views: Optional[List[View]] = field(default_factory=list) + image: Optional[str] = None + accesses: Optional[List[DatabaseAccess]] = field(default_factory=list) + exchange_name: Optional[str] = None class Unique(BaseModel): -- GitLab From a73c6ee36dc0d005588a42979f690e7fb576d554 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Wed, 22 Jan 2025 10:57:36 +0100 Subject: [PATCH 03/19] Added Swagger version lint Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .gitlab-ci.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 01e1f79c16..0e2c8535aa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,7 +49,6 @@ lint-docker-compose: before_script: - 'apk --no-cache add bash wget' - 'wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY} -O /usr/bin/yq && chmod +x /usr/bin/yq' - - 'ls -la .scripts' script: - "bash .scripts/check-compose.sh" - "yq compare -P docker-compose.yml .docker/docker-compose.yml 'volumes.*'" @@ -117,6 +116,19 @@ lint-metadata-schema: script: - diff dbrepo-metadata-db/1_setup-schema.sql helm/dbrepo/files/01-setup-schema.sql +lint-swagger-version: + image: docker.io/alpine:${ALPINE_VERSION} + stage: lint + variables: + VERSION: 3.3.0 + BINARY: yq_linux_amd64 + before_script: + - 'apk --no-cache add bash wget' + - 'wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY} -O /usr/bin/yq && chmod +x /usr/bin/yq' + script: + - cat ./.docs/.swagger/api.base.yaml | yq .externalDocs.url | grep "${DOC_VERSION}" + - cat ./.docs/.swagger/api.base.yaml | yq .info.version | grep "${APP_VERSION}" + build-metadata-service: image: maven:3-openjdk-${JAVA_VERSION} stage: build -- GitLab From df05d432a05db89fcf1c48e55a6cd703f3010070 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Wed, 22 Jan 2025 10:58:18 +0100 Subject: [PATCH 04/19] Fixed the docker compose versions Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .docker/docker-compose.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml index cdeb4e3624..b6a7478e82 100644 --- a/.docker/docker-compose.yml +++ b/.docker/docker-compose.yml @@ -109,7 +109,7 @@ services: dbrepo-auth-service-init: init: true restart: "no" - image: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.1 + image: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.2 environment: AUTH_SERVICE_ADMIN: ${AUTH_SERVICE_ADMIN:-admin} AUTH_SERVICE_ADMIN_PASSWORD: ${AUTH_SERVICE_ADMIN_PASSWORD:-admin} @@ -130,7 +130,7 @@ services: restart: "no" container_name: dbrepo-metadata-service hostname: metadata-service - image: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.1 + image: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.2 volumes: - "${SHARED_VOLUME:-/tmp}:/tmp" environment: @@ -193,7 +193,7 @@ services: restart: "no" container_name: dbrepo-analyse-service hostname: analyse-service - image: registry.datalab.tuwien.ac.at/dbrepo/analyse-service:1.6.1 + image: registry.datalab.tuwien.ac.at/dbrepo/analyse-service:1.6.2 environment: AUTH_SERVICE_CLIENT: ${AUTH_SERVICE_CLIENT:-dbrepo-client} AUTH_SERVICE_CLIENT_SECRET: ${AUTH_SERVICE_CLIENT:-MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG} @@ -248,7 +248,7 @@ services: restart: "no" container_name: dbrepo-search-db hostname: search-db - image: registry.datalab.tuwien.ac.at/dbrepo/search-db:1.6.1 + image: registry.datalab.tuwien.ac.at/dbrepo/search-db:1.6.2 healthcheck: test: curl -sSL localhost:9200/_plugins/_security/health | jq .status | grep UP interval: 10s @@ -272,7 +272,7 @@ services: restart: "no" container_name: dbrepo-search-service hostname: search-service - image: registry.datalab.tuwien.ac.at/dbrepo/search-service:1.6.1 + image: registry.datalab.tuwien.ac.at/dbrepo/search-service:1.6.2 environment: AUTH_SERVICE_CLIENT: ${AUTH_SERVICE_CLIENT:-dbrepo-client} AUTH_SERVICE_CLIENT_SECRET: ${AUTH_SERVICE_CLIENT_SECRET:-MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG} @@ -296,7 +296,7 @@ services: restart: "no" container_name: dbrepo-ui hostname: ui - image: registry.datalab.tuwien.ac.at/dbrepo/ui:1.6.1 + image: registry.datalab.tuwien.ac.at/dbrepo/ui:1.6.2 environment: NUXT_PUBLIC_API_CLIENT: "${BASE_URL:-http://localhost}" NUXT_PUBLIC_API_SERVER: "${BASE_URL:-http://localhost}" @@ -365,7 +365,7 @@ services: init: true container_name: dbrepo-search-service-init hostname: search-service-init - image: registry.datalab.tuwien.ac.at/dbrepo/search-service-init:1.6.1 + image: registry.datalab.tuwien.ac.at/dbrepo/search-service-init:1.6.2 environment: LOG_LEVEL: ${LOG_LEVEL:-info} METADATA_SERVICE_ENDPOINT: ${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080} @@ -422,7 +422,7 @@ services: restart: "no" container_name: dbrepo-dashboard-service hostname: dashboard-service - image: registry.datalab.tuwien.ac.at/dbrepo/dashboard-service:1.6.1 + image: registry.datalab.tuwien.ac.at/dbrepo/dashboard-service:1.6.2 ports: - "3000:3000" volumes: @@ -449,7 +449,7 @@ services: init: true container_name: dbrepo-storage-service-init hostname: storage-service-init - image: registry.datalab.tuwien.ac.at/dbrepo/storage-service-init:1.6.1 + image: registry.datalab.tuwien.ac.at/dbrepo/storage-service-init:1.6.2 environment: S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-seaweedfsadmin} S3_BUCKET: "${S3_BUCKET:-dbrepo}" @@ -494,7 +494,7 @@ services: restart: "no" container_name: dbrepo-data-service hostname: data-service - image: registry.datalab.tuwien.ac.at/dbrepo/data-service:1.6.1 + image: registry.datalab.tuwien.ac.at/dbrepo/data-service:1.6.2 volumes: - "${SHARED_VOLUME:-/tmp}:/tmp" environment: -- GitLab From 724bd150330579304462e4bb4fa570bc4194c326 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Wed, 22 Jan 2025 11:08:17 +0100 Subject: [PATCH 05/19] Updated yq commands Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0e2c8535aa..dfc73f621d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -126,8 +126,8 @@ lint-swagger-version: - 'apk --no-cache add bash wget' - 'wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY} -O /usr/bin/yq && chmod +x /usr/bin/yq' script: - - cat ./.docs/.swagger/api.base.yaml | yq .externalDocs.url | grep "${DOC_VERSION}" - - cat ./.docs/.swagger/api.base.yaml | yq .info.version | grep "${APP_VERSION}" + - yq r ./.docs/.swagger/api.base.yaml 'externalDocs.url' | grep "${DOC_VERSION}" + - yq r ./.docs/.swagger/api.base.yaml 'info.version' | grep "${DOC_VERSION}" build-metadata-service: image: maven:3-openjdk-${JAVA_VERSION} -- GitLab From 58c934876d173c4b2bc14cc505851777b24a32ef Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Wed, 22 Jan 2025 13:06:11 +0100 Subject: [PATCH 06/19] Updated libs Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- dbrepo-analyse-service/Pipfile.lock | 20 ++++----- .../lib/dbrepo-1.6.2-py3-none-any.whl | Bin 0 -> 30864 bytes .../lib/dbrepo-1.6.2.tar.gz | Bin 40094 -> 40094 bytes .../impl/MetadataServiceGatewayImpl.java | 15 ++++--- .../at/tuwien/service/impl/DataConnector.java | 14 +++--- .../impl/DatabaseServiceMariaDbImpl.java | 12 +---- .../service/impl/TableServiceMariaDbImpl.java | 10 ++--- .../at/tuwien/endpoints/DatabaseEndpoint.java | 1 + dbrepo-search-service/Pipfile.lock | 8 ++-- dbrepo-search-service/init/Pipfile.lock | 8 ++-- .../init/lib/dbrepo-1.6.2-py3-none-any.whl | Bin 0 -> 30864 bytes .../init/lib/dbrepo-1.6.2.tar.gz | Bin 40094 -> 40094 bytes .../lib/dbrepo-1.6.2-py3-none-any.whl | Bin 0 -> 30864 bytes dbrepo-search-service/lib/dbrepo-1.6.2.tar.gz | Bin 40094 -> 40094 bytes dbrepo-ui/components/dialogs/EditTuple.vue | 42 ++++++------------ make/build.mk | 12 ++--- 16 files changed, 60 insertions(+), 82 deletions(-) create mode 100644 dbrepo-analyse-service/lib/dbrepo-1.6.2-py3-none-any.whl create mode 100644 dbrepo-search-service/init/lib/dbrepo-1.6.2-py3-none-any.whl create mode 100644 dbrepo-search-service/lib/dbrepo-1.6.2-py3-none-any.whl diff --git a/dbrepo-analyse-service/Pipfile.lock b/dbrepo-analyse-service/Pipfile.lock index ec9b5f13d4..f177d904cb 100644 --- a/dbrepo-analyse-service/Pipfile.lock +++ b/dbrepo-analyse-service/Pipfile.lock @@ -175,20 +175,20 @@ }, "boto3": { "hashes": [ - "sha256:76cfc9a705be46e8d22607efacc8d688c064f923d785a01c00b28e9a96425d1a", - "sha256:fde1c29996b77274a60b7bc9f741525afa6267bb1716eb644a764fb7c124a0d2" + "sha256:53a5307f6a3526ee2f8590e3c45efa504a3ea4532c1bfe4926c0c19bf188d141", + "sha256:f9843a5d06f501d66ada06f5a5417f671823af2cf319e36ceefa1bafaaaaa953" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.36.2" + "version": "==1.36.3" }, "botocore": { "hashes": [ - "sha256:a1fe6610983f0214b0c7655fe6990b6a731746baf305b182976fc7b568fc3cb0", - "sha256:bc3b7e3b573a48af2bd7116b80fe24f9a335b0b67314dcb2697a327d009abf29" + "sha256:536ab828e6f90dbb000e3702ac45fd76642113ae2db1b7b1373ad24104e89255", + "sha256:775b835e979da5c96548ed1a0b798101a145aec3cd46541d62e27dda5a94d7f8" ], "markers": "python_version >= '3.8'", - "version": "==1.36.2" + "version": "==1.36.3" }, "certifi": { "hashes": [ @@ -412,7 +412,7 @@ }, "dbrepo": { "hashes": [ - "sha256:19c6bbcf9461e20681f0fb342087c618a91123d2d04d4df2f4fd1da80aa77b76" + "sha256:a41ca60353510cbecf8fb647cf2483acb100258743794a16bc8ad6f8e9ea4481" ], "path": "./lib/dbrepo-1.6.2.tar.gz" }, @@ -1601,11 +1601,11 @@ }, "tzdata": { "hashes": [ - "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", - "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd" + "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", + "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639" ], "markers": "python_version >= '2'", - "version": "==2024.2" + "version": "==2025.1" }, "urllib3": { "hashes": [ diff --git a/dbrepo-analyse-service/lib/dbrepo-1.6.2-py3-none-any.whl b/dbrepo-analyse-service/lib/dbrepo-1.6.2-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..24256263e2fb3156ac0eea01079116e4b40e36fd GIT binary patch literal 30864 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 literal 0 HcmV?d00001 diff --git a/dbrepo-analyse-service/lib/dbrepo-1.6.2.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.6.2.tar.gz index 02ed2aec31c2b1881165a12d45060ed4a311192d..2ae1ea50b1610050f5bd5746f7e9596b1c483c9d 100644 GIT binary patch literal 40094 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 Hz{&ssdmpy^ literal 40094 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 Mwn)xpSis5v08ewvp#T5? diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java index 57d6ffab7c..4dcfaf13a2 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java @@ -100,7 +100,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { log.error("Failed to find database with id {}: service responded unsuccessful: {}", id, response.getStatusCode()); throw new MetadataServiceException("Failed to find database: service responded unsuccessful: " + response.getStatusCode()); } - final List<String> expectedHeaders = List.of("X-Username", "X-Password", "X-Host", "X-Port"); + final List<String> expectedHeaders = List.of("X-Username", "X-Password", "X-Host", "X-Port", "X-Type"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { log.error("Failed to find all database headers"); log.debug("expected headers: {}", expectedHeaders); @@ -112,10 +112,11 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { throw new MetadataServiceException("Failed to find database with id " + id + ": body is empty"); } final DatabaseDto database = response.getBody(); - database.getContainer().setUsername(response.getHeaders().get("X-Username").get(0)); - database.getContainer().setPassword(response.getHeaders().get("X-Password").get(0)); - database.getContainer().setHost(response.getHeaders().get("X-Host").get(0)); - database.getContainer().setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0))); + database.setJdbcMethod(response.getHeaders().get("X-Type").get(0)); + database.setUsername(response.getHeaders().get("X-Username").get(0)); + database.setPassword(response.getHeaders().get("X-Password").get(0)); + database.setHost(response.getHeaders().get("X-Host").get(0)); + database.setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0))); database.setLastRetrieved(Instant.now()); return database; } @@ -139,7 +140,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { log.error("Failed to find table with id {}: service responded unsuccessful: {}", id, response.getStatusCode()); throw new MetadataServiceException("Failed to find table: service responded unsuccessful: " + response.getStatusCode()); } - final List<String> expectedHeaders = List.of("X-Type", "X-Host", "X-Port", "X-Username", "X-Password", "X-Database", "X-Table"); + final List<String> expectedHeaders = List.of("X-Type", "X-Host", "X-Port", "X-Username", "X-Password", "X-Database", "X-Table", "X-Type"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { log.error("Failed to find all table headers"); log.debug("expected headers: {}", expectedHeaders); @@ -181,7 +182,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { log.error("Failed to find view with id {}: service responded unsuccessful: {}", id, response.getStatusCode()); throw new MetadataServiceException("Failed to find view: service responded unsuccessful: " + response.getStatusCode()); } - final List<String> expectedHeaders = List.of("X-Type", "X-Host", "X-Port", "X-Username", "X-Password", "X-Database", "X-View"); + final List<String> expectedHeaders = List.of("X-Type", "X-Host", "X-Port", "X-Username", "X-Password", "X-Database", "X-View", "X-Type"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { log.error("Failed to find all view headers"); log.debug("expected headers: {}", expectedHeaders); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java index 9fbee30fa6..1044869ad8 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java @@ -12,7 +12,7 @@ public abstract class DataConnector<T extends CacheableDto> { public ComboPooledDataSource getDataSource(T entity) { final long start = System.currentTimeMillis(); final ComboPooledDataSource dataSource = new ComboPooledDataSource(); - dataSource.setJdbcUrl(getJdbcUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPassword(), + dataSource.setJdbcUrl(getJdbcUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPort(), entity.getDatabase())); dataSource.setUser(entity.getUsername()); dataSource.setPassword(entity.getPassword()); @@ -27,7 +27,7 @@ public abstract class DataConnector<T extends CacheableDto> { public ComboPooledDataSource getDataSource(T entity, String databaseName) { final long start = System.currentTimeMillis(); final ComboPooledDataSource dataSource = new ComboPooledDataSource(); - dataSource.setJdbcUrl(getJdbcUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPassword(), databaseName)); + dataSource.setJdbcUrl(getJdbcUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPort(), databaseName)); dataSource.setUser(entity.getUsername()); dataSource.setPassword(entity.getPassword()); dataSource.setInitialPoolSize(5); @@ -38,24 +38,24 @@ public abstract class DataConnector<T extends CacheableDto> { return dataSource; } - public String getSparkUrl(String jdbcMethod, String host, String password, String databaseName) { - final StringBuilder sb = new StringBuilder(getJdbcUrl(jdbcMethod, host, password, databaseName)) + public String getSparkUrl(String jdbcMethod, String host, Integer port, String databaseName) { + final StringBuilder sb = new StringBuilder(getJdbcUrl(jdbcMethod, host, port, databaseName)) .append("?sessionVariables=sql_mode='ANSI_QUOTES'"); log.trace("mapped container to spark url: {}", sb.toString()); return sb.toString(); } public String getSparkUrl(T entity) { - return getSparkUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPassword(), entity.getDatabase()); + return getSparkUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPort(), entity.getDatabase()); } - public String getJdbcUrl(String jdbcMethod, String host, String password, String databaseName) { + public String getJdbcUrl(String jdbcMethod, String host, Integer port, String databaseName) { final StringBuilder stringBuilder = new StringBuilder("jdbc:") .append(jdbcMethod) .append("://") .append(host) .append(":") - .append(password); + .append(port); if (databaseName != null) { stringBuilder.append("/") .append(databaseName); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java index 2b5af71d1c..cfe43d8f8f 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java @@ -13,13 +13,10 @@ import at.tuwien.mapper.DataMapper; import at.tuwien.mapper.MariaDbMapper; import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.DatabaseService; -import at.tuwien.service.TableService; -import at.tuwien.service.ViewService; import com.google.common.hash.Hashing; import com.mchange.v2.c3p0.ComboPooledDataSource; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets; @@ -36,19 +33,14 @@ public class DatabaseServiceMariaDbImpl extends DataConnector<DatabaseDto> imple private final DataMapper dataMapper; private final QueryConfig queryConfig; - private final ViewService viewService; - private final TableService tableService; private final MariaDbMapper mariaDbMapper; private final MetadataMapper metadataMapper; @Autowired - public DatabaseServiceMariaDbImpl(DataMapper dataMapper, QueryConfig queryConfig, ViewService viewService, - TableService tableService, MariaDbMapper mariaDbMapper, - @Qualifier("metadataMapper") MetadataMapper metadataMapper) { + public DatabaseServiceMariaDbImpl(DataMapper dataMapper, QueryConfig queryConfig, MariaDbMapper mariaDbMapper, + MetadataMapper metadataMapper) { this.dataMapper = dataMapper; this.queryConfig = queryConfig; - this.viewService = viewService; - this.tableService = tableService; this.mariaDbMapper = mariaDbMapper; this.metadataMapper = metadataMapper; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java index 6586c8ba42..68fb59cb5e 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java @@ -374,10 +374,10 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements throws QueryMalformedException, TableNotFoundException { try { final Properties properties = new Properties(); - properties.setProperty("user", database.getContainer().getUsername()); - properties.setProperty("password", database.getContainer().getPassword()); + properties.setProperty("user", database.getUsername()); + properties.setProperty("password", database.getPassword()); return sparkSession.read() - .jdbc(getSparkUrl(database.getJdbcMethod(), database.getHost(), database.getPassword(), + .jdbc(getSparkUrl(database.getJdbcMethod(), database.getHost(), database.getPort(), database.getInternalName()), tableOrView, properties); } catch (Exception e) { if (e instanceof ExtendedAnalysisException exception) { @@ -386,8 +386,8 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements throw new TableNotFoundException("Failed to find named reference: " + exception.getSimpleMessage()) /* remove throwable on purpose, clutters the output */; } } - log.error("Failed to find get data from query statement: {}", e.getMessage()); - throw new QueryMalformedException("Failed to find get data from query statement: " + e.getMessage(), e); + log.error("Malformed query: {}", e.getMessage()); + throw new QueryMalformedException("Malformed query: " + e.getMessage(), e); } } 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 7e03202480..194f79d255 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 @@ -551,6 +551,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { headers.set("X-Password", database.getContainer().getPrivilegedPassword()); headers.set("X-Host", database.getContainer().getHost()); headers.set("X-Port", "" + database.getContainer().getPort()); + headers.set("X-Type", database.getContainer().getImage().getJdbcMethod()); headers.set("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port"); } return ResponseEntity.status(HttpStatus.OK) diff --git a/dbrepo-search-service/Pipfile.lock b/dbrepo-search-service/Pipfile.lock index 8362cd2df3..d75a0069a1 100644 --- a/dbrepo-search-service/Pipfile.lock +++ b/dbrepo-search-service/Pipfile.lock @@ -360,7 +360,7 @@ }, "dbrepo": { "hashes": [ - "sha256:19c6bbcf9461e20681f0fb342087c618a91123d2d04d4df2f4fd1da80aa77b76" + "sha256:a41ca60353510cbecf8fb647cf2483acb100258743794a16bc8ad6f8e9ea4481" ], "path": "./lib/dbrepo-1.6.2.tar.gz" }, @@ -1574,11 +1574,11 @@ }, "tzdata": { "hashes": [ - "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", - "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd" + "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", + "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639" ], "markers": "python_version >= '2'", - "version": "==2024.2" + "version": "==2025.1" }, "urllib3": { "hashes": [ diff --git a/dbrepo-search-service/init/Pipfile.lock b/dbrepo-search-service/init/Pipfile.lock index e72262e85d..e4a2e7d718 100644 --- a/dbrepo-search-service/init/Pipfile.lock +++ b/dbrepo-search-service/init/Pipfile.lock @@ -254,7 +254,7 @@ }, "dbrepo": { "hashes": [ - "sha256:19c6bbcf9461e20681f0fb342087c618a91123d2d04d4df2f4fd1da80aa77b76" + "sha256:a41ca60353510cbecf8fb647cf2483acb100258743794a16bc8ad6f8e9ea4481" ], "path": "./lib/dbrepo-1.6.2.tar.gz" }, @@ -1027,11 +1027,11 @@ }, "tzdata": { "hashes": [ - "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", - "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd" + "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", + "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639" ], "markers": "python_version >= '2'", - "version": "==2024.2" + "version": "==2025.1" }, "urllib3": { "hashes": [ diff --git a/dbrepo-search-service/init/lib/dbrepo-1.6.2-py3-none-any.whl b/dbrepo-search-service/init/lib/dbrepo-1.6.2-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..24256263e2fb3156ac0eea01079116e4b40e36fd GIT binary patch literal 30864 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 literal 0 HcmV?d00001 diff --git a/dbrepo-search-service/init/lib/dbrepo-1.6.2.tar.gz b/dbrepo-search-service/init/lib/dbrepo-1.6.2.tar.gz index 02ed2aec31c2b1881165a12d45060ed4a311192d..2ae1ea50b1610050f5bd5746f7e9596b1c483c9d 100644 GIT binary patch literal 40094 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 Hz{&ssdmpy^ literal 40094 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 Mwn)xpSis5v08ewvp#T5? diff --git a/dbrepo-search-service/lib/dbrepo-1.6.2-py3-none-any.whl b/dbrepo-search-service/lib/dbrepo-1.6.2-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..24256263e2fb3156ac0eea01079116e4b40e36fd GIT binary patch literal 30864 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 literal 0 HcmV?d00001 diff --git a/dbrepo-search-service/lib/dbrepo-1.6.2.tar.gz b/dbrepo-search-service/lib/dbrepo-1.6.2.tar.gz index 02ed2aec31c2b1881165a12d45060ed4a311192d..2ae1ea50b1610050f5bd5746f7e9596b1c483c9d 100644 GIT binary patch literal 40094 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 Hz{&ssdmpy^ literal 40094 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 Mwn)xpSis5v08ewvp#T5? diff --git a/dbrepo-ui/components/dialogs/EditTuple.vue b/dbrepo-ui/components/dialogs/EditTuple.vue index 3290d230d1..75e824ed43 100644 --- a/dbrepo-ui/components/dialogs/EditTuple.vue +++ b/dbrepo-ui/components/dialogs/EditTuple.vue @@ -3,7 +3,7 @@ <v-form ref="form" v-model="valid" - @submit.prevent="submit"> + @submit.prevent="validate"> <v-card :title="title" :subtitle="this.$t('toolbars.table.data.subtitle')" @@ -17,12 +17,10 @@ <v-text-field v-if="isNumber(column)" v-model.number="tuple[column.internal_name]" - :disabled="!edit" persistent-hint :variant="inputVariant" :label="column.internal_name" :hint="hint(column)" - :rules="rules(column)" :required="required(column)" type="number"> <template @@ -48,11 +46,9 @@ <v-text-field v-if="isTextField(column)" v-model="tuple[column.internal_name]" - :disabled="disabled(column)" :clearable="!required(column)" :counter="maxLength(column) !== null" :maxlength="maxLength(column)" - :rules="rules(column)" :required="required(column)" persistent-hint :variant="inputVariant" @@ -82,10 +78,8 @@ <v-text-field v-if="isFloatingPoint(column)" v-model="tuple[column.internal_name]" - :disabled="disabled(column)" step=".1" :clearable="!required(column)" - :rules="rules(column)" :required="required(column)" persistent-hint :variant="inputVariant" @@ -115,10 +109,8 @@ <v-textarea v-if="isTextArea(column)" v-model="tuple[column.internal_name]" - :disabled="disabled(column)" rows="3" :clearable="!required(column)" - :rules="rules(column)" :required="required(column)" persistent-hint :variant="inputVariant" @@ -155,7 +147,6 @@ :variant="inputVariant" :label="column.internal_name" :hint="hint(column)" - :rules="rules(column)" :required="required(column)" :clearable="!required(column)" :items="isSet(column) ? column.sets : column.enums"> @@ -186,7 +177,6 @@ :variant="inputVariant" :label="column.internal_name" :hint="hint(column)" - :rules="rules(column)" :required="required(column)" :items="bools" :clearable="!required(column)"> @@ -322,10 +312,10 @@ export default { cacheStore: useCacheStore() } }, - mounted() { + mounted () { this.fetchContainer() - this.$refs.form.validate() this.oldTuple = Object.assign({}, this.tuple) + this.validate() }, computed: { database () { @@ -358,8 +348,17 @@ export default { return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal } }, + watch: { + tuple: { + handler () { + this.validate() + }, + deep: true + } + }, methods: { - submit () { + validate () { + console.debug('validate form') this.$refs.form.validate() }, cancel () { @@ -425,18 +424,6 @@ export default { isTimeField (column) { return ['date', 'datetime', 'timestamp', 'time', 'year'].includes(column.type) }, - rules (column) { - if (column.is_null_allowed) { - return [] - } - const rules = [] - rules.push(v => v !== null || this.$t('validation.required')) - if (column.type === 'decimal' || column.type === 'double') { - rules.push(v => !(!v || v.split('.')[0].length > column.size) || `${this.$t('pages.table.subpages.data.float.max')} ${column.size} ${this.$t('pages.table.subpages.data.float.before')}`) - rules.push(v => !(!v || (column.d && v.split('.')[1].length > column.d)) || `${this.$t('pages.table.subpages.data.float.max')} ${column.d} ${this.$t('pages.table.subpages.data.float.after')}`) - } - return rules - }, maxLength (column) { if (!this.isTextField(column) || column.size === null) { return null @@ -446,9 +433,6 @@ export default { required (column) { return column.is_null_allowed === false }, - disabled (column) { - return (this.edit && column.is_primary_key) || !this.edit - }, updateTuple () { const constraints = {} this.primaryKeyColumns diff --git a/make/build.mk b/make/build.mk index 7311ed2716..bc6dfc56a7 100644 --- a/make/build.mk +++ b/make/build.mk @@ -20,16 +20,16 @@ build-ui: ## Build the UI. .PHONY: build-lib build-lib: ## Build the Python Library. - rm -f ./dbrepo-analyse-service/lib/dbrepo-${APP_VERSION}.tar.gz - rm -f ./dbrepo-search-service/lib/dbrepo-${APP_VERSION}.tar.gz - rm -f ./dbrepo-search-service/init/lib/dbrepo-${APP_VERSION}.tar.gz + rm -f ./dbrepo-analyse-service/Pipfile.lock ./dbrepo-analyse-service/lib/dbrepo-${APP_VERSION}* + rm -f ./dbrepo-search-service/Pipfile.lock ./dbrepo-search-service/lib/dbrepo-${APP_VERSION}* + rm -f ./dbrepo-search-service/init/Pipfile.lock ./dbrepo-search-service/init/lib/dbrepo-${APP_VERSION}* python3 -m build --sdist ./lib/python python3 -m build --wheel ./lib/python - cp ./lib/python/dist/dbrepo-${APP_VERSION}.tar.gz ./dbrepo-analyse-service/lib/dbrepo-${APP_VERSION}.tar.gz + cp -r ./lib/python/dist/dbrepo-${APP_VERSION}* ./dbrepo-analyse-service/lib (cd ./dbrepo-analyse-service && PIPENV_IGNORE_VIRTUALENVS=1 pipenv lock) - cp ./lib/python/dist/dbrepo-${APP_VERSION}.tar.gz ./dbrepo-search-service/lib/dbrepo-${APP_VERSION}.tar.gz + cp -r ./lib/python/dist/dbrepo-${APP_VERSION}* ./dbrepo-search-service/lib (cd ./dbrepo-search-service && PIPENV_IGNORE_VIRTUALENVS=1 pipenv lock) - cp ./lib/python/dist/dbrepo-${APP_VERSION}.tar.gz ./dbrepo-search-service/init/lib/dbrepo-${APP_VERSION}.tar.gz + cp -r ./lib/python/dist/dbrepo-${APP_VERSION}* ./dbrepo-search-service/init/lib (cd ./dbrepo-search-service/init && PIPENV_IGNORE_VIRTUALENVS=1 pipenv lock) .PHONY: build-helm -- GitLab From f6a6eb3fb4d99e0a5fca91aa5e5a06c284929dfe Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Wed, 22 Jan 2025 15:19:09 +0100 Subject: [PATCH 07/19] Bumped yq and added schema validation Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .docs/.openapi/api-analyse.yaml | 332 + .docs/.openapi/api-data.yaml | 1792 +++++ .docs/.openapi/api-metadata.yaml | 7043 +++++++++++++++++ .docs/.openapi/api-search.yaml | 387 + .docs/{.swagger => .openapi}/api.base.yaml | 0 .docs/{.swagger => .openapi}/api.yaml | 810 +- .docs/{.swagger => .openapi}/custom.css | 0 .../openapi-generate.sh} | 4 +- .../{.swagger => .openapi}/openapi-merge.json | 0 .docs/{.swagger => .openapi}/swagger-ui.html | 0 .gitlab-ci.yml | 20 +- .../api/database/table/columns/ColumnDto.java | 13 +- .../columns/concepts/ConceptBriefDto.java | 5 + .../table/columns/concepts/UnitBriefDto.java | 5 + lib/python/Pipfile | 1 + lib/python/Pipfile.lock | 233 +- lib/python/tests/conftest.py | 37 - lib/python/tests/test_dtos.py | 44 + lib/python/tests/test_system_database.py | 34 - lib/python/tests/test_system_user.py | 42 - lib/python/tests/test_unit_analyse.py | 1 - lib/python/tests/test_unit_database.py | 16 +- make/gen.mk | 8 +- yq | 7043 +++++++++++++++++ 24 files changed, 17272 insertions(+), 598 deletions(-) create mode 100644 .docs/.openapi/api-analyse.yaml create mode 100644 .docs/.openapi/api-data.yaml create mode 100644 .docs/.openapi/api-metadata.yaml create mode 100644 .docs/.openapi/api-search.yaml rename .docs/{.swagger => .openapi}/api.base.yaml (100%) rename .docs/{.swagger => .openapi}/api.yaml (97%) rename .docs/{.swagger => .openapi}/custom.css (100%) rename .docs/{.swagger/swagger-generate.sh => .openapi/openapi-generate.sh} (82%) rename .docs/{.swagger => .openapi}/openapi-merge.json (100%) rename .docs/{.swagger => .openapi}/swagger-ui.html (100%) delete mode 100644 lib/python/tests/conftest.py create mode 100644 lib/python/tests/test_dtos.py delete mode 100644 lib/python/tests/test_system_database.py delete mode 100644 lib/python/tests/test_system_user.py create mode 100644 yq diff --git a/.docs/.openapi/api-analyse.yaml b/.docs/.openapi/api-analyse.yaml new file mode 100644 index 0000000000..25e15521d5 --- /dev/null +++ b/.docs/.openapi/api-analyse.yaml @@ -0,0 +1,332 @@ +{ + "components": { + "schemas": { + "AnalysisDto": { + "properties": { + "columns": { + "items": { + "properties": { + "column_name": { + "$ref": "#/components/schemas/ColumnAnalysisDto" + } + } + }, + "type": "array" + }, + "line_termination": { + "example": "\r\n", + "type": "string" + }, + "separator": { + "example": ",", + "type": "string" + } + }, + "type": "object" + }, + "ColumnAnalysisDto": { + "properties": { + "d": { + "example": 4, + "type": "integer" + }, + "dfid": { + "example": null, + "type": "integer" + }, + "enums": { + "example": null, + "properties": { + "type": "string" + }, + "type": "array" + }, + "null_allowed": { + "type": "boolean" + }, + "sets": { + "example": null, + "properties": { + "type": "string" + }, + "type": "array" + }, + "size": { + "example": 10, + "type": "integer" + }, + "type": { + "example": "decimal", + "type": "string" + } + }, + "type": "object" + }, + "ErrorDto": { + "properties": { + "message": { + "example": "Message", + "type": "string" + }, + "success": { + "example": false, + "type": "boolean" + } + }, + "type": "object" + }, + "KeysDto": { + "properties": { + "keys": { + "items": { + "properties": { + "column_name": { + "format": "int64", + "type": "integer" + } + } + }, + "type": "array" + } + }, + "required": [ + "keys" + ], + "type": "object" + } + }, + "securitySchemes": { + "basicAuth": { + "in": "header", + "scheme": "basic", + "type": "http" + }, + "bearerAuth": { + "bearerFormat": "JWT", + "in": "header", + "scheme": "bearer", + "type": "http" + } + } + }, + "externalDocs": { + "description": "Sourcecode Documentation", + "url": "https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.5/" + }, + "info": { + "contact": { + "email": "andreas.rauber@tuwien.ac.at", + "name": "Prof. Andreas Rauber" + }, + "description": "Service that analyses data structures", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0" + }, + "title": "Database Repository Analyse Service API", + "version": "1.5" + }, + "openapi": "3.0.0", + "paths": { + "/api/analyse/datatypes": { + "get": { + "consumes": [ + "application/json" + ], + "description": "Determines MySQL 8 datatypes of a given dataset. Requires role `table-semantic-analyse`.", + "operationId": "analyse_datatypes", + "parameters": [ + { + "example": "filename_s3_key", + "in": "query", + "name": "filename", + "required": true, + "schema": { + "type": "string" + } + }, + { + "example": ",", + "in": "query", + "name": "separator", + "required": true, + "schema": { + "type": "string" + } + }, + { + "example": "false", + "in": "query", + "name": "enum", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "example": "2.5", + "in": "query", + "name": "enum_tol", + "required": false, + "schema": { + "type": "float" + } + } + ], + "produces": [ + "application/json" + ], + "responses": { + "202": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AnalysisDto" + } + } + }, + "description": "Determined data types successfully" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorDto" + } + } + }, + "description": "Failed to determine data types" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorDto" + } + } + }, + "description": "Failed to find file in Storage Service" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorDto" + } + } + }, + "description": "Unexpected system error" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "basicAuth": [] + } + ], + "summary": "Determine datatypes", + "tags": [ + "analyse-endpoint" + ] + } + }, + "/api/analyse/keys": { + "get": { + "consumes": [ + "application/json" + ], + "description": "Determines primary keys of a given dataset. Requires role `table-semantic-analyse`.", + "operationId": "analyse_keys", + "parameters": [ + { + "example": "filename_s3_key", + "in": "query", + "name": "filename", + "required": true, + "schema": { + "type": "string" + } + }, + { + "example": ",", + "in": "query", + "name": "separator", + "required": true, + "schema": { + "type": "string" + } + } + ], + "produces": [ + "application/json" + ], + "responses": { + "202": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeysDto" + } + } + }, + "description": "Determined keys successfully" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorDto" + } + } + }, + "description": "Failed to determine keys" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorDto" + } + } + }, + "description": "Failed to find file in Storage Service or is empty" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorDto" + } + } + }, + "description": "Unexpected system error" + } + }, + "security": [ + { + "bearerAuth": [] + }, + { + "basicAuth": [] + } + ], + "summary": "Determine keys", + "tags": [ + "analyse-endpoint" + ] + } + } + }, + "servers": [ + { + "description": "Generated server url", + "url": "http://localhost:5000" + }, + { + "description": "Sandbox", + "url": "https://test.dbrepo.tuwien.ac.at" + } + ] +} diff --git a/.docs/.openapi/api-data.yaml b/.docs/.openapi/api-data.yaml new file mode 100644 index 0000000000..7be3a4039a --- /dev/null +++ b/.docs/.openapi/api-data.yaml @@ -0,0 +1,1792 @@ +openapi: 3.0.1 +info: + title: Database Repository Data Service API + description: Service that manages the data + contact: + name: Prof. Andreas Rauber + email: andreas.rauber@tuwien.ac.at + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + version: 1.6.2 +externalDocs: + description: Sourcecode Documentation + url: https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.6.2/system-services-metadata/ +servers: +- url: http://localhost + description: Development instance +- url: https://test.dbrepo.tuwien.ac.at + description: Staging instance +paths: + /api/database/{databaseId}/view/{viewId}/data: + get: + tags: + - view-endpoint + summary: Get view data + description: "Gets data from a view of a database. For private databases, the\ + \ user needs at least *READ* access to the associated database. Requires role\ + \ `view-database-view-data`." + operationId: getData + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: viewId + in: path + required: true + schema: + type: integer + format: int64 + - name: page + in: query + required: false + schema: + type: integer + format: int64 + - name: size + in: query + required: false + schema: + type: integer + format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time + responses: + "400": + description: Request pagination is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to retrieve view data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Retrieved view data + headers: + Access-Control-Expose-Headers: + description: Expose `X-Count` custom header + required: true + style: simple + X-Count: + description: Number of rows + required: true + style: simple + content: + application/json: + schema: + type: string + "409": + description: View schema could not be mapped + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find view in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + head: + tags: + - view-endpoint + summary: Get view data + description: "Gets data from a view of a database. For private databases, the\ + \ user needs at least *READ* access to the associated database. Requires role\ + \ `view-database-view-data`." + operationId: getData_1 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: viewId + in: path + required: true + schema: + type: integer + format: int64 + - name: page + in: query + required: false + schema: + type: integer + format: int64 + - name: size + in: query + required: false + schema: + type: integer + format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time + responses: + "400": + description: Request pagination is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to retrieve view data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Retrieved view data + headers: + Access-Control-Expose-Headers: + description: Expose `X-Count` custom header + required: true + style: simple + X-Count: + description: Number of rows + required: true + style: simple + content: + application/json: + schema: + type: string + "409": + description: View schema could not be mapped + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find view in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + /api/database/{databaseId}/table/{tableId}/data: + get: + tags: + - table-endpoint + summary: Get table data + description: "Gets data from a table with id. For a table in a private database,\ + \ the user needs to have at least *READ* access to the associated database.\ + \ Requests with HTTP method **GET** return the full dataset, requests with\ + \ HTTP method **HEAD** only the number of tuples in the `X-Count` header." + operationId: getData_2 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time + - name: page + in: query + required: false + schema: + type: integer + format: int64 + - name: size + in: query + required: false + schema: + type: integer + format: int64 + responses: + "404": + description: Failed to find table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Request pagination or table data select query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Get table data + headers: + Access-Control-Expose-Headers: + description: Expose `X-Count` custom header + required: true + style: simple + X-Count: + description: Number of rows + required: true + style: simple + content: + application/json: + schema: + type: string + "403": + description: Not allowed to get table data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + put: + tags: + - table-endpoint + summary: Update tuple + description: "Updates a data tuple into a table, then the table statistics are\ + \ updated. The user needs to have at least *WRITE_OWN* access to the associated\ + \ database. Requires role `insert-table-data`." + operationId: updateRawTuple + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: Authorization + in: header + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TupleUpdateDto" + required: true + responses: + "404": + description: Failed to find table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Request pagination or table data select query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated table data + "403": + description: Update table data not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + post: + tags: + - table-endpoint + summary: Insert tuple + description: "Inserts a data tuple into a table, then the table statistics are\ + \ updated. The user needs to have at least *WRITE_OWN* access to the associated\ + \ database. Requires role `insert-table-data`." + operationId: insertRawTuple + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: Authorization + in: header + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TupleDto" + required: true + responses: + "503": + description: Failed to establish connection with the metadata service or + storage service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Request pagination or table data select query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Create table data not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created table data + "404": + description: Failed to find table in metadata database or blob in storage + service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + delete: + tags: + - table-endpoint + summary: Delete tuple + description: "Deletes a data tuple into a table, then the table statistics are\ + \ updated. The user needs to have at least *WRITE_OWN* access to the associated\ + \ database. Requires role `delete-table-data`." + operationId: deleteRawTuple + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: Authorization + in: header + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TupleDeleteDto" + required: true + responses: + "404": + description: Failed to find table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Request pagination or table data select query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Delete table data not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted table data + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + head: + tags: + - table-endpoint + summary: Get table data + description: "Gets data from a table with id. For a table in a private database,\ + \ the user needs to have at least *READ* access to the associated database.\ + \ Requests with HTTP method **GET** return the full dataset, requests with\ + \ HTTP method **HEAD** only the number of tuples in the `X-Count` header." + operationId: getData_3 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time + - name: page + in: query + required: false + schema: + type: integer + format: int64 + - name: size + in: query + required: false + schema: + type: integer + format: int64 + responses: + "404": + description: Failed to find table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Request pagination or table data select query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Get table data + headers: + Access-Control-Expose-Headers: + description: Expose `X-Count` custom header + required: true + style: simple + X-Count: + description: Number of rows + required: true + style: simple + content: + application/json: + schema: + type: string + "403": + description: Not allowed to get table data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + /api/database/{databaseId}/subset/{subsetId}/data: + get: + tags: + - subset-endpoint + summary: Get subset data + description: "Gets data of subset with id. For private databases, the user needs\ + \ at least *READ* access to the associated database. Requests with HTTP method\ + \ **GET** return the subset dataset, requests with HTTP method **HEAD** only\ + \ the number of rows in the subset dataset in the `X-Count` header" + operationId: getData_4 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: subsetId + in: path + required: true + schema: + type: integer + format: int64 + - name: page + in: query + required: false + schema: + type: integer + format: int64 + - name: size + in: query + required: false + schema: + type: integer + format: int64 + responses: + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to retrieve subset data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Invalid pagination + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Retrieved subset data + headers: + Access-Control-Expose-Headers: + description: Reverse proxy exposing of custom headers + required: true + style: simple + X-Count: + description: Number of rows + style: simple + X-Id: + description: The subset id + required: true + style: simple + X-Headers: + description: The list of headers separated by comma + style: simple + content: + application/json: + schema: + type: string + "503": + description: Failed to communicate with database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + head: + tags: + - subset-endpoint + summary: Get subset data + description: "Gets data of subset with id. For private databases, the user needs\ + \ at least *READ* access to the associated database. Requests with HTTP method\ + \ **GET** return the subset dataset, requests with HTTP method **HEAD** only\ + \ the number of rows in the subset dataset in the `X-Count` header" + operationId: getData_5 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: subsetId + in: path + required: true + schema: + type: integer + format: int64 + - name: page + in: query + required: false + schema: + type: integer + format: int64 + - name: size + in: query + required: false + schema: + type: integer + format: int64 + responses: + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to retrieve subset data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Invalid pagination + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Retrieved subset data + headers: + Access-Control-Expose-Headers: + description: Reverse proxy exposing of custom headers + required: true + style: simple + X-Count: + description: Number of rows + style: simple + X-Id: + description: The subset id + required: true + style: simple + X-Headers: + description: The list of headers separated by comma + style: simple + content: + application/json: + schema: + type: string + "503": + description: Failed to communicate with database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/subset/{queryId}: + put: + tags: + - subset-endpoint + summary: Persist subset + description: Persists a subset with id. Requires role `persist-query`. + operationId: persist + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: queryId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/QueryPersistDto" + required: true + responses: + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Persisted subset + content: + application/json: + schema: + $ref: "#/components/schemas/QueryDto" + "403": + description: Not allowed to persist subset + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Malformed select query + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "417": + description: Failed to persist subset + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to communicate with database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}/data/import: + post: + tags: + - table-endpoint + summary: Import dataset + description: Imports a dataset in a table. Then update the table statistics. + The user needs to have at least *WRITE_OWN* access to the associated database + when importing into a owned table. Otherwise *WRITE_ALL* access in needed. + Requires role `insert-table-data`. + operationId: importDataset + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: Authorization + in: header + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ImportDto" + required: true + responses: + "202": + description: Imported dataset successfully + "403": + description: Import table dataset not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Dataset and/or query are malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + /api/database/{databaseId}/subset: + get: + tags: + - subset-endpoint + summary: Find subsets + description: "Finds subsets in the query store. When the database schema is\ + \ marked as hidden, the user needs to be authorized, have at least read-access\ + \ to the database. The result can be optionally filtered by setting `persisted`.\ + \ When set to *true*, only persisted queries are returned, otherwise only\ + \ non-persisted queries are returned." + operationId: list + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: persisted + in: query + required: false + schema: + type: boolean + responses: + "403": + description: Not allowed to find subsets + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found subsets + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/QueryDto" + "503": + description: Failed to communicate with database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + post: + tags: + - subset-endpoint + summary: Create subset + description: Creates a subset in the query store of the data database. Can also + be used without authentication if (and only if) the database is marked as + public (i.e. when `is_public` = `is_schema_public` is set to `true`). Otherwise + at least read access is required. + operationId: create + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time + - name: page + in: query + required: false + schema: + type: integer + format: int64 + - name: size + in: query + required: false + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ExecuteStatementDto" + required: true + responses: + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Malformed select query + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "501": + description: Failed to execute query as it contains non-supported keywords + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "417": + description: Failed to insert query into query store of data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to communicate with database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created subset + content: + application/json: + schema: + type: string + "403": + description: Not allowed to find subset + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + /api/database/{databaseId}/view/{viewId}/export: + get: + tags: + - view-endpoint + summary: Get view data + description: "Gets data from view with id as downloadable file. For tables in\ + \ private databases, the user needs to have at least *READ* access to the\ + \ associated database." + operationId: exportDataset + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: viewId + in: path + required: true + schema: + type: integer + format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time + responses: + "403": + description: Export view data not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Request pagination or view data select query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Exported view data + content: + application/json: + schema: + type: string + format: binary + "404": + description: Failed to find view in metadata database or export dataset + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + /api/database/{databaseId}/table/{tableId}/history: + get: + tags: + - table-endpoint + summary: Get history + description: "Gets the insert/delete operations history performed. For tables\ + \ in private databases, the user needs to have at least *READ* access to the\ + \ associated database." + operationId: getHistory + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: size + in: query + required: false + schema: + type: integer + format: int64 + responses: + "400": + description: "Invalid pagination size request, must be > 0" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Find table history not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found table history + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TableHistoryDto" + "404": + description: Failed to find table history in data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + /api/database/{databaseId}/table/{tableId}/export: + get: + tags: + - table-endpoint + summary: Get table data + description: "Gets data from table with id as downloadable file. For tables\ + \ in private databases, the user needs to have at least *READ* access to the\ + \ associated database." + operationId: exportDataset_1 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time + responses: + "403": + description: Export table data not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Exported table data + content: + application/json: + schema: + type: string + format: binary + "400": + description: Request pagination or table data select query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] + /api/database/{databaseId}/subset/{subsetId}: + get: + tags: + - subset-endpoint + summary: Find subset + description: "Finds a subset in the data database. When the database schema\ + \ is marked as hidden, the user needs to be authorized, have at least read-access\ + \ to the database. Requests with HTTP header `Accept=application/json` return\ + \ the metadata, requests with HTTP header `Accept=text/csv` return the data\ + \ as downloadable file." + operationId: findById + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: subsetId + in: path + required: true + schema: + type: integer + format: int64 + - name: Accept + in: header + required: true + schema: + type: string + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time + responses: + "200": + description: Found subset + content: + application/json: + schema: + $ref: "#/components/schemas/QueryDto" + text/csv: {} + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Malformed select query + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to communicate with database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to find subset + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "406": + description: Failed to find acceptable representation + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - basicAuth: [] + - bearerAuth: [] +components: + schemas: + ApiErrorDto: + required: + - code + - message + - status + type: object + properties: + status: + type: string + example: NOT_FOUND + enum: + - 100 CONTINUE + - 101 SWITCHING_PROTOCOLS + - 102 PROCESSING + - 103 EARLY_HINTS + - 103 CHECKPOINT + - 200 OK + - 201 CREATED + - 202 ACCEPTED + - 203 NON_AUTHORITATIVE_INFORMATION + - 204 NO_CONTENT + - 205 RESET_CONTENT + - 206 PARTIAL_CONTENT + - 207 MULTI_STATUS + - 208 ALREADY_REPORTED + - 226 IM_USED + - 300 MULTIPLE_CHOICES + - 301 MOVED_PERMANENTLY + - 302 FOUND + - 302 MOVED_TEMPORARILY + - 303 SEE_OTHER + - 304 NOT_MODIFIED + - 305 USE_PROXY + - 307 TEMPORARY_REDIRECT + - 308 PERMANENT_REDIRECT + - 400 BAD_REQUEST + - 401 UNAUTHORIZED + - 402 PAYMENT_REQUIRED + - 403 FORBIDDEN + - 404 NOT_FOUND + - 405 METHOD_NOT_ALLOWED + - 406 NOT_ACCEPTABLE + - 407 PROXY_AUTHENTICATION_REQUIRED + - 408 REQUEST_TIMEOUT + - 409 CONFLICT + - 410 GONE + - 411 LENGTH_REQUIRED + - 412 PRECONDITION_FAILED + - 413 PAYLOAD_TOO_LARGE + - 413 REQUEST_ENTITY_TOO_LARGE + - 414 URI_TOO_LONG + - 414 REQUEST_URI_TOO_LONG + - 415 UNSUPPORTED_MEDIA_TYPE + - 416 REQUESTED_RANGE_NOT_SATISFIABLE + - 417 EXPECTATION_FAILED + - 418 I_AM_A_TEAPOT + - 419 INSUFFICIENT_SPACE_ON_RESOURCE + - 420 METHOD_FAILURE + - 421 DESTINATION_LOCKED + - 422 UNPROCESSABLE_ENTITY + - 423 LOCKED + - 424 FAILED_DEPENDENCY + - 425 TOO_EARLY + - 426 UPGRADE_REQUIRED + - 428 PRECONDITION_REQUIRED + - 429 TOO_MANY_REQUESTS + - 431 REQUEST_HEADER_FIELDS_TOO_LARGE + - 451 UNAVAILABLE_FOR_LEGAL_REASONS + - 500 INTERNAL_SERVER_ERROR + - 501 NOT_IMPLEMENTED + - 502 BAD_GATEWAY + - 503 SERVICE_UNAVAILABLE + - 504 GATEWAY_TIMEOUT + - 505 HTTP_VERSION_NOT_SUPPORTED + - 506 VARIANT_ALSO_NEGOTIATES + - 507 INSUFFICIENT_STORAGE + - 508 LOOP_DETECTED + - 509 BANDWIDTH_LIMIT_EXCEEDED + - 510 NOT_EXTENDED + - 511 NETWORK_AUTHENTICATION_REQUIRED + message: + type: string + example: Error message + code: + type: string + example: error.service.code + TupleUpdateDto: + required: + - data + - keys + type: object + properties: + data: + type: object + additionalProperties: + type: object + keys: + type: object + additionalProperties: + type: object + QueryPersistDto: + required: + - persist + type: object + properties: + persist: + type: boolean + example: true + IdentifierBriefDto: + required: + - created_by + - database_id + - id + - publication_year + - publisher + - titles + - type + type: object + properties: + id: + type: integer + format: int64 + type: + type: string + enum: + - database + - subset + - table + - view + titles: + type: array + items: + $ref: "#/components/schemas/IdentifierTitleDto" + doi: + type: string + example: 10.1038/nphys1170 + publisher: + type: string + example: TU Wien + status: + type: string + enum: + - draft + - published + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + example: 1 + table_id: + type: integer + format: int64 + example: 1 + view_id: + type: integer + format: int64 + example: 1 + publication_year: + type: integer + format: int32 + example: 2022 + created_by: + type: string + format: uuid + IdentifierTitleDto: + required: + - id + type: object + properties: + id: + type: integer + format: int64 + title: + type: string + example: Airquality Demonstrator + language: + type: string + example: en + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + type: + type: string + enum: + - AlternativeTitle + - Subtitle + - TranslatedTitle + - Other + QueryDto: + required: + - database_id + - execution + - id + - identifiers + - is_persisted + - owner + - query + - query_hash + - query_normalized + type: object + properties: + id: + type: integer + format: int64 + owner: + $ref: "#/components/schemas/UserBriefDto" + execution: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + query: + type: string + example: SELECT `id` FROM `air_quality` + type: + type: string + example: query + enum: + - query + - view + identifiers: + type: array + items: + $ref: "#/components/schemas/IdentifierBriefDto" + database_id: + type: integer + format: int64 + query_normalized: + type: string + example: SELECT `id` FROM `air_quality` + query_hash: + type: string + example: 17e682f060b5f8e47ea04c5c4855908b0a5ad612022260fe50e11ecb0cc0ab76 + is_persisted: + type: boolean + example: true + result_hash: + type: string + example: 17e682f060b5f8e47ea04c5c4855908b0a5ad612022260fe50e11ecb0cc0ab76 + result_number: + type: integer + format: int64 + example: 1 + UserBriefDto: + required: + - id + - username + type: object + properties: + id: + type: string + format: uuid + example: 1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4 + username: + type: string + description: Only contains lowercase characters + example: jcarberry + name: + type: string + example: Josiah Carberry + orcid: + type: string + example: 0000-0002-1825-0097 + qualified_name: + type: string + example: Josiah Carberry — @jcarberry + given_name: + type: string + example: Josiah + family_name: + type: string + example: Carberry + TupleDto: + required: + - data + type: object + properties: + data: + type: object + additionalProperties: + type: object + ImportDto: + required: + - header + - location + - separator + type: object + properties: + location: + type: string + example: file.csv + header: + type: boolean + description: "If true, the first line contains the column names, otherwise\ + \ it contains only data" + separator: + type: string + example: "," + quote: + type: string + example: '"' + line_termination: + type: string + example: \r\n + ExecuteStatementDto: + required: + - statement + type: object + properties: + statement: + type: string + example: SELECT `id` FROM `air_quality` + TableHistoryDto: + required: + - event + - timestamp + - total + type: object + properties: + timestamp: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + event: + type: string + total: + type: integer + format: int64 + example: 1 + TupleDeleteDto: + required: + - keys + type: object + properties: + keys: + type: object + additionalProperties: + type: object + securitySchemes: + basicAuth: + type: http + scheme: basic + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT diff --git a/.docs/.openapi/api-metadata.yaml b/.docs/.openapi/api-metadata.yaml new file mode 100644 index 0000000000..5578822fb4 --- /dev/null +++ b/.docs/.openapi/api-metadata.yaml @@ -0,0 +1,7043 @@ +openapi: 3.0.1 +info: + title: Database Repository Metadata Service API + description: Service that manages the metadata + contact: + name: Prof. Andreas Rauber + email: andreas.rauber@tuwien.ac.at + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + version: 1.6.2 +externalDocs: + description: Sourcecode Documentation + url: https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.6.2/system-services-metadata/ +servers: +- url: http://localhost + description: Development instance +- url: https://test.dbrepo.tuwien.ac.at + description: Staging instance +paths: + /api/database: + get: + tags: + - database-endpoint + summary: List databases + description: "Lists all databases in the metadata database. Requests with HTTP\ + \ method **GET** return the list of databases, requests with HTTP method **HEAD**\ + \ only the number in the `X-Count` header." + operationId: list + parameters: + - name: internal_name + in: query + required: false + schema: + type: string + responses: + "200": + description: List of databases + headers: + Access-Control-Expose-Headers: + description: Expose `X-Count` custom header + required: true + style: simple + X-Count: + description: Number of databases + required: true + style: simple + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/DatabaseBriefDto" + post: + tags: + - database-endpoint + summary: Create database + description: Creates a database in the container with id. Requires roles `create-database`. + operationId: create_5 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseCreateDto" + required: true + responses: + "409": + description: Query store could not be created + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Database create query is malformed or image is not supported + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "423": + description: Database quota exceeded + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Database create permission is missing or grant permissions + at broker service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to fin container/user/database in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created a new database + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + security: + - bearerAuth: [] + - basicAuth: [] + head: + tags: + - database-endpoint + summary: List databases + description: "Lists all databases in the metadata database. Requests with HTTP\ + \ method **GET** return the list of databases, requests with HTTP method **HEAD**\ + \ only the number in the `X-Count` header." + operationId: list_1 + parameters: + - name: internal_name + in: query + required: false + schema: + type: string + responses: + "200": + description: List of databases + headers: + Access-Control-Expose-Headers: + description: Expose `X-Count` custom header + required: true + style: simple + X-Count: + description: Number of databases + required: true + style: simple + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/DatabaseBriefDto" + /api/database/{databaseId}/access/{userId}: + get: + tags: + - access-endpoint + summary: Find/Check access + description: "Finds or checks access of a user with given id to a database with\ + \ given id. Requests with HTTP method **GET** return the access object, requests\ + \ with HTTP method **HEAD** only the status. When the user has at least *READ*\ + \ access, the status 200 is returned, 403 otherwise. Requires role `check-database-access`\ + \ or `check-foreign-database-access`." + operationId: find + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: userId + in: path + required: true + schema: + type: string + format: uuid + responses: + "403": + description: No access to this database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found database access + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseAccessDto" + security: + - bearerAuth: [] + - basicAuth: [] + put: + tags: + - access-endpoint + summary: Modify access + description: Modifies access of a user with given id to database with given + id. Requires role `update-database-access`. + operationId: update_5 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: userId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateDatabaseAccessDto" + required: true + responses: + "404": + description: Database or user not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Modify access not permitted when no access is granted in the + first place + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Access could not be updated in the data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Modified access + "502": + description: Access could not be updated due to connection error in the + data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Modify access query or database connection is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + post: + tags: + - access-endpoint + summary: Give access + description: Give a user with given id access to some database with given id. + Requires role `create-database-access`. + operationId: create_8 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: userId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateDatabaseAccessDto" + required: true + responses: + "404": + description: Database or user not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Access could not be created in the data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Failed giving access + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Granting access succeeded + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseAccessDto" + "400": + description: Granting access query or database connection is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Access could not be created due to connection error + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - access-endpoint + summary: Delete access + description: Delete access of a user with id to a database with id. Requires + role `delete-database-access`. + operationId: revoke + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: userId + in: path + required: true + schema: + type: string + format: uuid + responses: + "403": + description: Revoke of access not permitted as no access was found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted access + "502": + description: Access could not be created due to connection error + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "User, database with access was not found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Access could not be revoked in the data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Modify access query or database connection is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + head: + tags: + - access-endpoint + summary: Find/Check access + description: "Finds or checks access of a user with given id to a database with\ + \ given id. Requests with HTTP method **GET** return the access object, requests\ + \ with HTTP method **HEAD** only the status. When the user has at least *READ*\ + \ access, the status 200 is returned, 403 otherwise. Requires role `check-database-access`\ + \ or `check-foreign-database-access`." + operationId: find_1 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: userId + in: path + required: true + schema: + type: string + format: uuid + responses: + "403": + description: No access to this database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found database access + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseAccessDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/user/{userId}: + get: + tags: + - user-endpoint + summary: Get user + description: Gets own user information from the metadata database. Requires + authentication. Foreign user information can only be obtained if additional + role `find-foreign-user` is present. Finding information about internal users + results in a 404 error. + operationId: find_2 + parameters: + - name: userId + in: path + required: true + schema: + type: string + format: uuid + responses: + "404": + description: User was not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found user + content: + application/json: + schema: + $ref: "#/components/schemas/UserDto" + "403": + description: Find user is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + put: + tags: + - user-endpoint + summary: Update user + description: Updates user with id. Requires role `modify-user-information`. + operationId: modify + parameters: + - name: userId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UserUpdateDto" + required: true + responses: + "202": + description: Modified user information + content: + application/json: + schema: + $ref: "#/components/schemas/UserDto" + "404": + description: Failed to find database/user in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Modify user query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to modify user metadata + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/user/{userId}/password: + put: + tags: + - user-endpoint + summary: Update user password + description: Updates password of user with id. Requires authentication. + operationId: password + parameters: + - name: userId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UserPasswordDto" + required: true + responses: + "400": + description: Invalid password payload + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Modified user password + "403": + description: Not allowed to change foreign user password + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to get user in auth service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database/user in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to auth service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/user/token: + put: + tags: + - user-endpoint + summary: Refresh token + description: Refreshes user token by refresh token. + operationId: refreshToken + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RefreshTokenRequestDto" + required: true + responses: + "202": + description: Refreshed user token + content: + application/json: + schema: + $ref: "#/components/schemas/TokenDto" + "403": + description: Not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Invalid refresh token + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to auth service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + post: + tags: + - user-endpoint + summary: Create token + description: Creates a user token via the Auth Service. + operationId: getToken + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/LoginRequestDto" + required: true + responses: + "400": + description: Invalid login request + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to get token + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find user in auth database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to get user in auth service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Obtained user token + content: + application/json: + schema: + $ref: "#/components/schemas/TokenDto" + "428": + description: Account is not fully setup in auth service (requires password + change?) + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to auth service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + /api/ontology/{ontologyId}: + get: + tags: + - ontology-endpoint + summary: Find ontology + description: Finds an ontology with id in the metadata database. + operationId: find_3 + parameters: + - name: ontologyId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Find one ontology + content: + application/json: + schema: + $ref: "#/components/schemas/OntologyDto" + put: + tags: + - ontology-endpoint + summary: Update ontology + description: Updates an ontology with id. Requires role `update-ontology`. + operationId: update + parameters: + - name: ontologyId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/OntologyModifyDto" + required: true + responses: + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated ontology successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OntologyDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - ontology-endpoint + summary: Delete ontology + description: Deletes an ontology with given id. Requires role `delete-ontology`. + operationId: delete + parameters: + - name: ontologyId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "202": + description: Deleted ontology successfully + content: + application/json: {} + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/message/{messageId}: + put: + tags: + - message-endpoint + summary: Update message + description: Updates a message with id. Requires role `update-maintenance-message`. + operationId: update_1 + parameters: + - name: messageId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/BannerMessageUpdateDto" + required: true + responses: + "202": + description: Updated message + content: + application/json: + schema: + $ref: "#/components/schemas/BannerMessageBriefDto" + "404": + description: Could not find message + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - message-endpoint + summary: Delete message + description: Deletes a message with id. Requires role `delete-maintenance-message`. + operationId: delete_1 + parameters: + - name: messageId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "404": + description: Could not find message + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted message + content: + application/json: {} + security: + - bearerAuth: [] + - basicAuth: [] + /api/image/{imageId}: + get: + tags: + - image-endpoint + summary: Find image + description: Finds a container image in the metadata database. + operationId: findById + parameters: + - name: imageId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "404": + description: Image could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found image + content: + application/json: + schema: + $ref: "#/components/schemas/ImageDto" + put: + tags: + - image-endpoint + summary: Update image + description: Updates container image in the metadata database. Requires role + `modify-image`. + operationId: update_2 + parameters: + - name: imageId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ImageChangeDto" + required: true + responses: + "404": + description: Image could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated image successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ImageDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - image-endpoint + summary: Delete image + description: Deletes a container image in the metadata database. Requires role + `delete-image`. + operationId: delete_2 + parameters: + - name: imageId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "202": + description: Deleted image successfully + "404": + description: Image could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/identifier/{identifierId}: + get: + tags: + - identifier-endpoint + summary: Find identifier + description: Finds an identifier with id. The response format depends on the + HTTP `Accept` header set on the request. + operationId: find_6 + parameters: + - name: identifierId + in: path + required: true + schema: + type: integer + format: int64 + - name: Accept + in: header + required: true + schema: + type: string + responses: + "200": + description: Found identifier successfully + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + application/ld+json: + schema: + $ref: "#/components/schemas/LdDatasetDto" + text/csv: {} + text/xml: {} + text/bibliography: {} + text/bibliography; style=apa: {} + text/bibliography; style=ieee: {} + text/bibliography; style=bibtex: {} + "502": + description: Connection to data service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: "Identifier could not be exported, the requested style is not\ + \ known" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "409": + description: Exported resource was not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "410": + description: Failed to retrieve from S3 endpoint + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Identifier could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "406": + description: Failed to find acceptable representation + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to find in data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + put: + tags: + - identifier-endpoint + summary: Save identifier + description: Saves an identifier with id as a draft identifier. Identifiers + can only be created for objects the user has at least *READ* access in the + associated database (requires role `create-identifier`) or for any object + in any database (requires role `create-foreign-identifier`). + operationId: save + parameters: + - name: identifierId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierSaveDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Failed to find database, table or view" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Identifier form contains invalid request data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Saved identifier + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + "403": + description: Insufficient access rights or authorities + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - identifier-endpoint + summary: Delete identifier + description: Deletes an identifier with id. Requires role `delete-identifier`. + operationId: delete_3 + parameters: + - name: identifierId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to delete in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Identifier or database could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted identifier + "403": + description: Deleting identifier not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/identifier/{identifierId}/publish: + put: + tags: + - identifier-endpoint + summary: Publish identifier + description: Publishes an identifier with id. A published identifier cannot + be changed anymore. Requires role `publish-identifier`. + operationId: publish + parameters: + - name: identifierId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Failed to find database, table or view" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Published identifier + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + "400": + description: Identifier form contains invalid request data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Insufficient access rights or authorities + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/visibility: + put: + tags: + - database-endpoint + summary: Update database visibility + description: Updates the database with id on the visibility. Only the database + owner can perform this operation. Requires role `modify-database-visibility`. + operationId: visibility + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseModifyVisibilityDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Visibility modified successfully + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "400": + description: The visibility payload is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Visibility modification is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/view/{viewId}: + get: + tags: + - view-endpoint + summary: Get view + description: Gets a view with id in the metadata database. + operationId: find_7 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: viewId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "403": + description: Find view is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, view or user could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Find view successfully + headers: + X-Username: + description: The authentication username + style: simple + Access-Control-Expose-Headers: + description: Expose custom headers + style: simple + X-Type: + description: The JDBC connection type + style: simple + X-View: + description: The view internal name + style: simple + X-Database: + description: The database internal name + style: simple + X-Password: + description: The authentication password + style: simple + X-Host: + description: The database hostname + style: simple + X-Port: + description: The database port number + style: simple + content: + application/json: + schema: + $ref: "#/components/schemas/ViewDto" + security: + - bearerAuth: [] + - basicAuth: [] + put: + tags: + - view-endpoint + summary: Update view + description: Updates a view with id. This can only be performed by the view + owner or database owner. Requires role `create-database-view`. + operationId: update_3 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: viewId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ViewUpdateDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database or View could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Update not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Update view query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Update view successfully + content: + '*/*': + schema: + $ref: "#/components/schemas/ViewDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - view-endpoint + summary: Delete view + description: Deletes a view with id. Requires role `delete-database-view`. + operationId: delete_4 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: viewId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "423": + description: Delete view resulted in an invalid query statement + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, view or user could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Delete view successfully + content: + '*/*': + schema: + type: object + "400": + description: Delete view query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Deletion not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}: + get: + tags: + - table-endpoint + summary: Find table + description: "Finds a table with id. When a table is hidden (i.e. when `is_public`\ + \ is `false`), then the user needs to have at least read access and the role\ + \ `find-table`. When the `system` role is present, the endpoint responds with\ + \ additional connection metadata in the header." + operationId: findById_2 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: Find table successfully + headers: + X-Username: + description: The authentication username + style: simple + X-Table: + description: The table internal name + style: simple + Access-Control-Expose-Headers: + description: Expose custom headers + style: simple + X-Type: + description: The JDBC connection type + style: simple + X-Database: + description: The database internal name + style: simple + X-Password: + description: The authentication password + style: simple + X-Host: + description: The database hostname + style: simple + X-Port: + description: The database port number + style: simple + content: + application/json: + schema: + $ref: "#/components/schemas/TableDto" + "503": + description: Failed to obtain queue information from broker service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Access to the database is forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Failed to establish connection with broker service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Table, database or container could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + put: + tags: + - table-endpoint + summary: Update table + description: Updates a table in the database with id. Requires role `update-table`. + operationId: update_4 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TableUpdateDto" + required: true + responses: + "403": + description: Update table visibility not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Table could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated the table + content: + application/json: + schema: + $ref: "#/components/schemas/TableBriefDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Update table visibility payload is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - table-endpoint + summary: Delete table + description: Deletes a table with id. Only the owner of a table can perform + this action (requires role `delete-table`) or anyone can delete a table (requires + role `delete-foreign-table`). + operationId: delete_5 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Access to the database is forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Delete table successfully + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Delete table query resulted in an invalid query statement + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Table, database or container could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}/statistic: + put: + tags: + - table-endpoint + summary: Update statistics + description: "Updates basic statistical properties (min, max, mean, median,\ + \ std.dev) for numerical columns in a table with id. This action can only\ + \ be performed by the table owner. Requires role `update-table-statistic`." + operationId: updateStatistic + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Failed to map column statistic to known columns + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database/table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not the owner + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated table statistics successfully + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}/column/{columnId}: + put: + tags: + - table-endpoint + summary: Update semantics + description: Updates column semantics of a table column with id. Only the table + owner with at least *READ* access to the associated database can update the + column semantics (requires role `modify-table-column-semantics`) or foreign + table columns if role `modify-foreign-table-column-semantics`. + operationId: updateColumn + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: columnId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ColumnSemanticsUpdateDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Access to the database is forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find user/table/database/ontology in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated column semantics successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ColumnDto" + "400": + description: Update semantic concept query is malformed or update unit of + measurement query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/owner: + put: + tags: + - database-endpoint + summary: Update database owner + description: Updates the database with id on the owner. Only the database owner + can perform this operation. Requires role `modify-database-owner`. + operationId: transfer + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseTransferDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Transfer of ownership was successful + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "403": + description: Transfer of ownership is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Owner payload is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/metadata/view: + put: + tags: + - database-endpoint + summary: Update database view schemas + description: Updates the database with id with generated metadata from view + that are not yet known to the database. Only the database owner can perform + this operation. Requires role `find-database`. + operationId: refreshViewMetadata + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Refreshed database views metadata + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "403": + description: Refresh view metadata is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/metadata/table: + put: + tags: + - database-endpoint + summary: Update database table schemas + description: Updates the database with id with generated metadata from tables + that are not yet known to the database. Only the database owner can perform + this operation. Requires role `find-database`. + operationId: refreshTableMetadata + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "403": + description: Not allowed to refresh table metadata + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to fin user/database in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Refreshed database tables metadata + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "400": + description: Failed to parse payload at search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/image: + get: + tags: + - database-endpoint + summary: Get database preview image + description: Gets the database with id on the preview image. + operationId: findPreviewImage + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: View of image was successful + content: + '*/*': + schema: + type: array + items: + type: string + format: byte + security: + - bearerAuth: [] + - basicAuth: [] + put: + tags: + - database-endpoint + summary: Update database preview image + description: Updates the database with id on the preview image. Only the database + owner can perform this operation. Requires role `modify-database-image`. + operationId: modifyImage + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseModifyImageDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Modify of image was successful + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "410": + description: File was not found in the Storage Service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Modify of image is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/user: + get: + tags: + - user-endpoint + summary: List users + description: "Lists users known to the metadata database. Internal users are\ + \ omitted from the result list. If the optional query parameter `username`\ + \ is present, the result list can be filtered by matching this exact username." + operationId: findAll + parameters: + - name: username + in: query + required: false + schema: + type: string + responses: + "200": + description: List users + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/UserBriefDto" + post: + tags: + - user-endpoint + summary: Create user + description: Creates a user in the auth service and metadata database. Requires + that no credentials are sent in the request. + operationId: create + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/SignupRequestDto" + required: true + responses: + "403": + description: Internal authentication to the auth service is invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Parameters are not well-formed (likely email) + content: + application/json: {} + "409": + description: User with username already exists + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "417": + description: User with e-mail already exists + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to create in auth service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created user + content: + application/json: + schema: + $ref: "#/components/schemas/UserDto" + "502": + description: Failed to create in auth service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Default role not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + /api/ontology: + get: + tags: + - ontology-endpoint + summary: List ontologies + description: Lists all ontologies known to the metadata database. + operationId: findAll_2 + responses: + "200": + description: List ontologies + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/OntologyBriefDto" + post: + tags: + - ontology-endpoint + summary: Create ontology + description: Creates an ontology in the metadata database. Requires role `create-ontology`. + operationId: create_1 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/OntologyCreateDto" + required: true + responses: + "201": + description: Registered ontology successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OntologyDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/message: + get: + tags: + - message-endpoint + summary: List messages + description: "Lists messages known to the metadata database. Messages can be\ + \ filtered be filtered with the optional `active` parameter. If set to *true*,\ + \ only active messages (that is, messages whose end time has not been reached)\ + \ will be returned. Otherwise only inactive messages are returned. If not\ + \ set, active and inactive messages are returned." + operationId: list_2 + parameters: + - name: active + in: query + required: false + schema: + type: boolean + responses: + "200": + description: List messages + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/BannerMessageDto" + post: + tags: + - message-endpoint + summary: Create message + description: Creates a message in the metadata database. Requires role `create-maintenance-message`. + operationId: create_2 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/BannerMessageCreateDto" + required: true + responses: + "201": + description: Created message + content: + application/json: + schema: + $ref: "#/components/schemas/BannerMessageBriefDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/image: + get: + tags: + - image-endpoint + summary: List images + description: Lists all container images known to the metadata database. + operationId: findAll_3 + responses: + "200": + description: List images + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ImageBriefDto" + post: + tags: + - image-endpoint + summary: Create image + description: Creates a container image in the metadata database. Requires role + `create-image`. + operationId: create_3 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ImageCreateDto" + required: true + responses: + "409": + description: Image already exists + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Image specification is invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created image + content: + application/json: + schema: + $ref: "#/components/schemas/ImageDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/identifier: + get: + tags: + - identifier-endpoint + summary: List identifiers + description: Lists all identifiers known to the metadata database + operationId: findAll_4 + parameters: + - name: dbid + in: query + required: false + schema: + type: integer + format: int64 + - name: qid + in: query + required: false + schema: + type: integer + format: int64 + - name: vid + in: query + required: false + schema: + type: integer + format: int64 + - name: tid + in: query + required: false + schema: + type: integer + format: int64 + - name: Accept + in: header + required: true + schema: + type: string + responses: + "200": + description: Found identifiers successfully + content: + application/json: + schema: + type: array + items: + type: string + application/ld+json: + schema: + type: array + items: + $ref: "#/components/schemas/LdDatasetDto" + "406": + description: "Identifier could not be exported, the requested style is not\ + \ known" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + post: + tags: + - identifier-endpoint + summary: Create identifier + description: Create an identifier with id to create a draft identifier. Identifiers + can only be created for objects the user has at least *READ* access in the + associated database (requires role `create-identifier`) or for any object + in any database (requires role `create-foreign-identifier`). + operationId: create_4 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierCreateDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Drafted identifier + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + "404": + description: "Failed to find database, table or view" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Identifier form contains invalid request data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Insufficient access rights or authorities + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/view: + get: + tags: + - view-endpoint + summary: List views + description: Lists views known to the metadata database. + operationId: findAll_5 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: Find views successfully + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ViewBriefDto" + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + post: + tags: + - view-endpoint + summary: Create view + description: Creates a view. This can only be performed by the database owner. + Requires role `create-database-view`. + operationId: create_6 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ViewCreateDto" + required: true + responses: + "423": + description: Create view resulted in an invalid query statement + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Create view successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ViewBriefDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Credentials missing + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database/user in metadata database. + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Create view query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table: + get: + tags: + - table-endpoint + summary: List tables + description: "Lists all tables known to the metadata database. When a database\ + \ has a hidden schema (i.e. when `is_schema_public` is `false`), then the\ + \ user needs to have at least read access and the role `list-tables`." + operationId: list_4 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "403": + description: List tables not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: List tables + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TableBriefDto" + security: + - bearerAuth: [] + - basicAuth: [] + post: + tags: + - table-endpoint + summary: Create table + description: Creates a table in the database with id. Requires role `create-table`. + operationId: create_7 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TableCreateDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Create table not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created a new table + content: + application/json: + schema: + $ref: "#/components/schemas/TableBriefDto" + "409": + description: Create table conflicts with existing table name + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, container or user could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Create table query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/container: + get: + tags: + - container-endpoint + summary: List containers + description: List all containers in the metadata database. + operationId: findAll_6 + parameters: + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + responses: + "200": + description: List containers + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ContainerBriefDto" + post: + tags: + - container-endpoint + summary: Create container + description: Creates a container in the metadata database. Requires role `create-container`. + operationId: create_9 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ContainerCreateDto" + required: true + responses: + "400": + description: Container payload malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Container image or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "409": + description: Container name already exists + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created a new container + content: + application/json: + schema: + $ref: "#/components/schemas/ContainerDto" + "403": + description: "Create container not permitted, need authority `create-container`" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/unit: + get: + tags: + - unit-endpoint + summary: List units + description: Lists units known to the metadata database. + operationId: findAll_1 + responses: + "200": + description: Find all semantic units + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/UnitDto" + /api/ontology/{ontologyId}/entity: + get: + tags: + - ontology-endpoint + summary: Find entities + description: Finds semantic entities by label or uri in an ontology with id. + Requires role `execute-semantic-query`. + operationId: find_4 + parameters: + - name: ontologyId + in: path + required: true + schema: + type: integer + format: int64 + - name: label + in: query + required: false + schema: + type: string + - name: uri + in: query + required: false + schema: + type: string + responses: + "400": + description: Filter params are invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found entities + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/EntityDto" + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "422": + description: Ontology does not have rdf or sparql endpoint + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "417": + description: Generated query or uri is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/oai: + get: + tags: + - metadata-endpoint + summary: Get record + operationId: identify_1_1_1_1 + parameters: + - name: verb + in: query + - name: parameters + in: query + required: true + schema: + $ref: "#/components/schemas/OaiListIdentifiersParameters" + responses: + "200": + description: List containers + content: + text/xml: {} + /api/message/message/{messageId}: + get: + tags: + - message-endpoint + summary: Find message + description: Finds a message with id in the metadata database. + operationId: find_5 + parameters: + - name: messageId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: Get messages + content: + application/json: + schema: + $ref: "#/components/schemas/BannerMessageDto" + "404": + description: Could not find message + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + /api/license: + get: + tags: + - license-endpoint + summary: List licenses + description: Lists licenses known to the metadata database. + operationId: list_3 + responses: + "200": + description: List of licenses + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/LicenseDto" + /api/identifier/retrieve: + get: + tags: + - identifier-endpoint + summary: Retrieve PID metadata + description: "Retrieves Persistent Identifier (PID) metadata from external endpoints.\ + \ Supported PIDs are: ORCID, ROR, DOI." + operationId: retrieve + parameters: + - name: url + in: query + required: true + schema: + type: string + responses: + "200": + description: Retrieved metadata from identifier + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + "404": + description: Failed to find metadata for identifier + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + /api/database/{databaseId}: + get: + tags: + - database-endpoint + summary: Find database + description: Finds a database with id. + operationId: findById_1 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "503": + description: Failed to find queue information in broker service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to the broker service could not be established + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Database found successfully + headers: + X-Username: + description: The authentication username + style: simple + Access-Control-Expose-Headers: + description: Expose custom headers + style: simple + X-Password: + description: The authentication password + style: simple + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "403": + description: Not allowed to view database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, user or exchange could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}/suggest: + get: + tags: + - table-endpoint + summary: Suggest semantics + description: Suggests semantic concepts for a table. This action can only be + performed by the table owner. Requires role `table-semantic-analyse`. + operationId: analyseTable + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "417": + description: Generated query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "422": + description: Ontology does not have rdf or sparql endpoint + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Failed to parse statistic in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not the table owner. + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database/table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Suggested table semantics successfully + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/EntityDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}/column/{columnId}/suggest: + get: + tags: + - table-endpoint + summary: Suggest semantics + description: Suggests column semantics. Requires role `table-semantic-analyse`. + operationId: analyseTableColumn + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: columnId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "400": + description: Generated query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "422": + description: Ontology does not have rdf or sparql endpoint + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Suggested table column semantics successfully + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TableColumnEntityDto" + "404": + description: Failed to find database/table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/container/{containerId}: + get: + tags: + - container-endpoint + summary: Find container + description: Finds a container in the metadata database. + operationId: findById_3 + parameters: + - name: containerId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: Found container + content: + application/json: + schema: + $ref: "#/components/schemas/ContainerDto" + "404": + description: Container image could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + delete: + tags: + - container-endpoint + summary: Delete container + description: Deletes a container in the metadata database. Requires role `delete-container`. + operationId: delete_6 + parameters: + - name: containerId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "404": + description: Container not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted container + "403": + description: "Create container not permitted, need authority `delete-container`" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/concept: + get: + tags: + - concept-endpoint + summary: List concepts + description: List all semantic concepts known to the metadata database + operationId: findAll_7 + responses: + "200": + description: List concepts + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ConceptDto" +components: + schemas: + DatabaseBriefDto: + required: + - contact + - id + - internal_name + - is_public + - is_schema_public + - name + - owner_id + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + description: + type: string + example: Air Quality + identifiers: + type: array + items: + $ref: "#/components/schemas/IdentifierBriefDto" + contact: + $ref: "#/components/schemas/UserBriefDto" + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + owner_id: + type: string + format: uuid + preview_image: + type: string + IdentifierBriefDto: + required: + - created_by + - database_id + - id + - publication_year + - publisher + - titles + - type + type: object + properties: + id: + type: integer + format: int64 + type: + type: string + enum: + - database + - subset + - table + - view + titles: + type: array + items: + $ref: "#/components/schemas/IdentifierTitleDto" + doi: + type: string + example: 10.1038/nphys1170 + publisher: + type: string + example: TU Wien + status: + type: string + enum: + - draft + - published + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + example: 1 + table_id: + type: integer + format: int64 + example: 1 + view_id: + type: integer + format: int64 + example: 1 + publication_year: + type: integer + format: int32 + example: 2022 + created_by: + type: string + format: uuid + IdentifierTitleDto: + required: + - id + type: object + properties: + id: + type: integer + format: int64 + title: + type: string + example: Airquality Demonstrator + language: + type: string + example: en + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + type: + type: string + enum: + - AlternativeTitle + - Subtitle + - TranslatedTitle + - Other + UserBriefDto: + required: + - id + - username + type: object + properties: + id: + type: string + format: uuid + example: 1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4 + username: + type: string + description: Only contains lowercase characters + example: jcarberry + name: + type: string + example: Josiah Carberry + orcid: + type: string + example: 0000-0002-1825-0097 + qualified_name: + type: string + example: Josiah Carberry — @jcarberry + given_name: + type: string + example: Josiah + family_name: + type: string + example: Carberry + ApiErrorDto: + required: + - code + - message + - status + type: object + properties: + status: + type: string + example: NOT_FOUND + enum: + - 100 CONTINUE + - 101 SWITCHING_PROTOCOLS + - 102 PROCESSING + - 103 EARLY_HINTS + - 103 CHECKPOINT + - 200 OK + - 201 CREATED + - 202 ACCEPTED + - 203 NON_AUTHORITATIVE_INFORMATION + - 204 NO_CONTENT + - 205 RESET_CONTENT + - 206 PARTIAL_CONTENT + - 207 MULTI_STATUS + - 208 ALREADY_REPORTED + - 226 IM_USED + - 300 MULTIPLE_CHOICES + - 301 MOVED_PERMANENTLY + - 302 FOUND + - 302 MOVED_TEMPORARILY + - 303 SEE_OTHER + - 304 NOT_MODIFIED + - 305 USE_PROXY + - 307 TEMPORARY_REDIRECT + - 308 PERMANENT_REDIRECT + - 400 BAD_REQUEST + - 401 UNAUTHORIZED + - 402 PAYMENT_REQUIRED + - 403 FORBIDDEN + - 404 NOT_FOUND + - 405 METHOD_NOT_ALLOWED + - 406 NOT_ACCEPTABLE + - 407 PROXY_AUTHENTICATION_REQUIRED + - 408 REQUEST_TIMEOUT + - 409 CONFLICT + - 410 GONE + - 411 LENGTH_REQUIRED + - 412 PRECONDITION_FAILED + - 413 PAYLOAD_TOO_LARGE + - 413 REQUEST_ENTITY_TOO_LARGE + - 414 URI_TOO_LONG + - 414 REQUEST_URI_TOO_LONG + - 415 UNSUPPORTED_MEDIA_TYPE + - 416 REQUESTED_RANGE_NOT_SATISFIABLE + - 417 EXPECTATION_FAILED + - 418 I_AM_A_TEAPOT + - 419 INSUFFICIENT_SPACE_ON_RESOURCE + - 420 METHOD_FAILURE + - 421 DESTINATION_LOCKED + - 422 UNPROCESSABLE_ENTITY + - 423 LOCKED + - 424 FAILED_DEPENDENCY + - 425 TOO_EARLY + - 426 UPGRADE_REQUIRED + - 428 PRECONDITION_REQUIRED + - 429 TOO_MANY_REQUESTS + - 431 REQUEST_HEADER_FIELDS_TOO_LARGE + - 451 UNAVAILABLE_FOR_LEGAL_REASONS + - 500 INTERNAL_SERVER_ERROR + - 501 NOT_IMPLEMENTED + - 502 BAD_GATEWAY + - 503 SERVICE_UNAVAILABLE + - 504 GATEWAY_TIMEOUT + - 505 HTTP_VERSION_NOT_SUPPORTED + - 506 VARIANT_ALSO_NEGOTIATES + - 507 INSUFFICIENT_STORAGE + - 508 LOOP_DETECTED + - 509 BANDWIDTH_LIMIT_EXCEEDED + - 510 NOT_EXTENDED + - 511 NETWORK_AUTHENTICATION_REQUIRED + message: + type: string + example: Error message + code: + type: string + example: error.service.code + DatabaseAccessDto: + required: + - type + - user + type: object + properties: + user: + $ref: "#/components/schemas/UserBriefDto" + type: + type: string + enum: + - read + - write_own + - write_all + UserUpdateDto: + required: + - language + - theme + type: object + properties: + firstname: + type: string + example: Josiah + lastname: + type: string + example: Carberry + affiliation: + type: string + example: Brown University + orcid: + type: string + example: 0000-0002-1825-0097 + theme: + type: string + example: dark + language: + type: string + example: en + UserAttributesDto: + required: + - language + - theme + type: object + properties: + theme: + type: string + example: light + orcid: + type: string + example: https://orcid.org/0000-0002-1825-0097 + affiliation: + type: string + example: Brown University + language: + type: string + example: en + UserDto: + required: + - attributes + - id + type: object + properties: + id: + type: string + format: uuid + example: 1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4 + name: + type: string + example: Josiah Carberry + attributes: + $ref: "#/components/schemas/UserAttributesDto" + last_retrieved: + type: string + format: date-time + qualified_name: + type: string + example: Josiah Carberry — @jcarberry + given_name: + type: string + example: Josiah + family_name: + type: string + example: Carberry + UserPasswordDto: + required: + - password + type: object + properties: + password: + type: string + RefreshTokenRequestDto: + required: + - refresh_token + type: object + properties: + refresh_token: + type: string + example: refresh_token + TokenDto: + required: + - access_token + - expires_in + - id_token + - not-before-policy + - refresh_expires_in + - refresh_token + - scope + - session_state + - token_type + type: object + properties: + scope: + type: string + access_token: + type: string + expires_in: + type: integer + format: int64 + refresh_token: + type: string + refresh_expires_in: + type: integer + format: int64 + id_token: + type: string + session_state: + type: string + token_type: + type: string + not-before-policy: + type: integer + format: int64 + OntologyModifyDto: + required: + - prefix + - uri + type: object + properties: + uri: + type: string + example: Ontology URI + prefix: + type: string + example: Ontology prefix + sparql_endpoint: + type: string + example: Ontology SPARQL endpoint + rdf_path: + type: string + example: rdf/om-2.0.rdf + OntologyDto: + required: + - id + - prefix + - rdf + - sparql + - uri + type: object + properties: + id: + type: integer + format: int64 + uri: + type: string + example: http://www.wikidata.org/ + prefix: + type: string + example: wd + sparql: + type: boolean + example: true + rdf: + type: boolean + example: false + uri_pattern: + type: string + example: http://www.wikidata.org/entity/.* + sparql_endpoint: + type: string + example: https://query.wikidata.org/sparql + rdf_path: + type: string + example: rdf/om-2.0.rdf + BannerMessageUpdateDto: + required: + - message + - type + type: object + properties: + type: + type: string + enum: + - error + - warning + - info + message: + type: string + example: Maintenance starts on 8am on Monday + link: + type: string + example: https://example.com + link_text: + type: string + example: More + display_start: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + display_end: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + BannerMessageBriefDto: + required: + - message + - type + type: object + properties: + type: + type: string + enum: + - error + - warning + - info + message: + type: string + example: Maintenance starts on 8am on Monday + link: + type: string + example: https://example.com + link_text: + type: string + example: More + ImageChangeDto: + required: + - dialect + - driver_class + - jdbc_method + - registry + type: object + properties: + registry: + type: string + example: docker.io/library + defaultPort: + maximum: 65535 + minimum: 1024 + type: integer + format: int32 + example: 5432 + dialect: + type: string + example: Postgres + driver_class: + type: string + example: org.postgresql.Driver + jdbc_method: + type: string + example: postgresql + DataTypeDto: + required: + - display_name + - documentation + - is_buildable + - is_quoted + - value + type: object + properties: + value: + type: string + example: time + documentation: + type: string + example: https://mariadb.com/kb/en/time/ + display_name: + type: string + example: TIME(fsp) + size_min: + type: integer + format: int32 + example: 0 + size_max: + type: integer + format: int32 + example: 6 + size_default: + type: integer + format: int32 + example: 0 + size_required: + type: boolean + example: false + d_min: + type: integer + format: int32 + d_max: + type: integer + format: int32 + d_default: + type: integer + format: int32 + d_required: + type: boolean + data_hint: + type: string + example: "e.g. HH:MM:SS, HH:MM, HHMMSS, H:M:S" + type_hint: + type: string + example: "fsp=microsecond precision, min. 0, max. 6" + is_quoted: + type: boolean + description: frontend needs to quote this data type + example: false + is_buildable: + type: boolean + description: frontend can build this data type + example: true + ImageDto: + required: + - data_types + - default + - default_port + - dialect + - driver_class + - id + - jdbc_method + - name + - operators + - registry + - version + type: object + properties: + id: + type: integer + format: int64 + registry: + type: string + example: docker.io/library + name: + type: string + example: mariadb + version: + type: string + example: "10.5" + dialect: + type: string + example: org.hibernate.dialect.MariaDBDialect + operators: + type: array + items: + $ref: "#/components/schemas/OperatorDto" + driver_class: + type: string + example: org.mariadb.jdbc.Driver + jdbc_method: + type: string + example: mariadb + default: + type: boolean + example: false + default_port: + type: integer + format: int32 + example: 3306 + data_types: + type: array + items: + $ref: "#/components/schemas/DataTypeDto" + OperatorDto: + required: + - display_name + - documentation + - value + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + example: XOR + documentation: + type: string + example: https://mariadb.com/kb/en/xor/ + display_name: + type: string + example: XOR + CreatorSaveDto: + required: + - creator_name + - id + type: object + properties: + id: + type: integer + format: int64 + example: 1 + firstname: + type: string + example: Josiah + lastname: + type: string + example: Carberry + affiliation: + type: string + example: Wesleyan University + creator_name: + type: string + example: "Carberry, Josiah" + name_type: + type: string + example: Personal + enum: + - Personal + - Organizational + name_identifier: + type: string + example: 0000-0002-1825-0097 + name_identifier_scheme: + type: string + example: ORCID + enum: + - ORCID + - ROR + - ISNI + - GRID + affiliation_identifier: + type: string + example: https://ror.org/04d836q62 + affiliation_identifier_scheme: + type: string + example: ROR + enum: + - ROR + - GRID + - ISNI + IdentifierFunderSaveDto: + required: + - funder_name + - id + type: object + properties: + id: + type: integer + format: int64 + example: 1 + funder_name: + type: string + example: European Commission + funder_identifier: + type: string + example: http://doi.org/10.13039/501100000780 + funder_identifier_type: + type: string + example: Crossref Funder ID + enum: + - Crossref Funder ID + - ROR + - GND + - ISNI + - Other + scheme_uri: + type: string + example: http://doi.org/ + award_number: + type: string + example: "824087" + award_title: + type: string + example: EOSC-Life + IdentifierSaveDescriptionDto: + required: + - description + - id + type: object + properties: + id: + type: integer + format: int64 + example: 1 + description: + type: string + example: "Air quality reports at Stephansplatz, Vienna" + language: + type: string + example: en + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + type: + type: string + example: Abstract + enum: + - Abstract + - Methods + - SeriesInformation + - TableOfContents + - TechnicalInfo + - Other + IdentifierSaveDto: + required: + - creators + - database_id + - id + - publication_year + - publisher + - titles + - type + type: object + properties: + id: + type: integer + format: int64 + example: 1 + type: + type: string + example: database + enum: + - database + - subset + - table + - view + doi: + type: string + example: 10.1111/11111111 + titles: + type: array + items: + $ref: "#/components/schemas/IdentifierSaveTitleDto" + descriptions: + type: array + items: + $ref: "#/components/schemas/IdentifierSaveDescriptionDto" + funders: + type: array + items: + $ref: "#/components/schemas/IdentifierFunderSaveDto" + licenses: + type: array + items: + $ref: "#/components/schemas/LicenseDto" + publisher: + type: string + example: TU Wien + language: + type: string + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + creators: + type: array + items: + $ref: "#/components/schemas/CreatorSaveDto" + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + view_id: + type: integer + format: int64 + table_id: + type: integer + format: int64 + publication_day: + type: integer + format: int32 + example: 15 + publication_month: + type: integer + format: int32 + example: 12 + publication_year: + type: integer + format: int32 + example: 2022 + related_identifiers: + type: array + items: + $ref: "#/components/schemas/RelatedIdentifierSaveDto" + IdentifierSaveTitleDto: + required: + - id + - title + type: object + properties: + id: + type: integer + format: int64 + example: 1 + title: + type: string + example: Airquality Demonstrator + language: + type: string + example: en + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + type: + type: string + example: Subtitle + enum: + - AlternativeTitle + - Subtitle + - TranslatedTitle + - Other + LicenseDto: + required: + - identifier + - uri + type: object + properties: + identifier: + type: string + example: MIT + uri: + type: string + example: https://opensource.org/licenses/MIT + description: + type: string + example: "A short and simple permissive license with conditions only requiring\ + \ preservation of copyright and license notices. Licensed works, modifications,\ + \ and larger works may be distributed under different terms and without\ + \ source code." + RelatedIdentifierSaveDto: + required: + - id + - relation + - type + - value + type: object + properties: + id: + type: integer + format: int64 + example: 1 + value: + type: string + example: 10.70124/dc4zh-9ce78 + type: + type: string + example: DOI + enum: + - DOI + - URL + - URN + - ARK + - arXiv + - bibcode + - EAN13 + - EISSN + - Handle + - IGSN + - ISBN + - ISTC + - LISSN + - LSID + - PMID + - PURL + - UPC + - w3id + relation: + type: string + example: Cites + enum: + - IsCitedBy + - Cites + - IsSupplementTo + - IsSupplementedBy + - IsContinuedBy + - Continues + - IsDescribedBy + - Describes + - HasMetadata + - IsMetadataFor + - HasVersion + - IsVersionOf + - IsNewVersionOf + - IsPreviousVersionOf + - IsPartOf + - HasPart + - IsPublishedIn + - IsReferencedBy + - References + - IsDocumentedBy + - Documents + - IsCompiledBy + - Compiles + - IsVariantFormOf + - IsOriginalFormOf + - IsIdenticalTo + - IsReviewedBy + - Reviews + - IsDerivedFrom + - IsSourceOf + - IsRequiredBy + - Requires + - IsObsoletedBy + - Obsoletes + CreatorDto: + required: + - creator_name + - id + type: object + properties: + id: + type: integer + format: int64 + firstname: + type: string + example: Josiah + lastname: + type: string + example: Carberry + affiliation: + type: string + example: Brown University + creator_name: + type: string + example: "Carberry, Josiah" + name_type: + type: string + example: Personal + enum: + - Personal + - Organizational + name_identifier: + type: string + example: 0000-0002-1825-0097 + name_identifier_scheme: + type: string + example: ORCID + enum: + - ORCID + - ROR + - ISNI + - GRID + name_identifier_scheme_uri: + type: string + example: https://orcid.org/ + affiliation_identifier: + type: string + example: https://ror.org/05gq02987 + affiliation_identifier_scheme: + type: string + example: ROR + enum: + - ROR + - GRID + - ISNI + affiliation_identifier_scheme_uri: + type: string + example: https://ror.org/ + IdentifierDescriptionDto: + required: + - id + type: object + properties: + id: + type: integer + format: int64 + description: + type: string + example: "Air quality reports at Stephansplatz, Vienna" + language: + type: string + example: en + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + type: + type: string + example: Abstract + enum: + - Abstract + - Methods + - SeriesInformation + - TableOfContents + - TechnicalInfo + - Other + IdentifierDto: + required: + - creators + - database_id + - id + - owner + - publication_year + - publisher + - query + - query_hash + - query_normalized + - titles + - type + type: object + properties: + id: + type: integer + format: int64 + type: + type: string + enum: + - database + - subset + - table + - view + titles: + type: array + items: + $ref: "#/components/schemas/IdentifierTitleDto" + descriptions: + type: array + items: + $ref: "#/components/schemas/IdentifierDescriptionDto" + funders: + type: array + items: + $ref: "#/components/schemas/IdentifierFunderDto" + query: + type: string + example: "SELECT `id`, `value`, `location` FROM `air_quality` WHERE `location`\ + \ = \"09:STEF\"" + execution: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + doi: + type: string + example: 10.1038/nphys1170 + publisher: + type: string + example: TU Wien + owner: + $ref: "#/components/schemas/UserBriefDto" + language: + type: string + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + licenses: + type: array + items: + $ref: "#/components/schemas/LicenseDto" + creators: + type: array + items: + $ref: "#/components/schemas/CreatorDto" + status: + type: string + enum: + - draft + - published + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + example: 1 + table_id: + type: integer + format: int64 + example: 1 + view_id: + type: integer + format: int64 + example: 1 + query_normalized: + type: string + example: "SELECT `id`, `value`, `location` FROM `air_quality` WHERE `location`\ + \ = \"09:STEF\"" + related_identifiers: + type: array + items: + $ref: "#/components/schemas/RelatedIdentifierDto" + query_hash: + type: string + description: query hash in sha512 + result_hash: + type: string + example: 34fe82cda2c53f13f8d90cfd7a3469e3a939ff311add50dce30d9136397bf8e5 + result_number: + type: integer + format: int64 + example: 1 + publication_day: + type: integer + format: int32 + example: 15 + publication_month: + type: integer + format: int32 + example: 12 + publication_year: + type: integer + format: int32 + example: 2022 + IdentifierFunderDto: + required: + - funder_name + - id + type: object + properties: + id: + type: integer + format: int64 + funder_name: + type: string + example: European Commission + funder_identifier: + type: string + example: http://doi.org/10.13039/501100000780 + funder_identifier_type: + type: string + example: Crossref Funder ID + enum: + - Crossref Funder ID + - ROR + - GND + - ISNI + - Other + scheme_uri: + type: string + example: http://doi.org/ + award_number: + type: string + example: "824087" + award_title: + type: string + example: EOSC-Life + RelatedIdentifierDto: + required: + - id + - relation + - type + - value + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + example: 10.70124/dc4zh-9ce78 + type: + type: string + example: DOI + enum: + - DOI + - URL + - URN + - ARK + - arXiv + - bibcode + - EAN13 + - EISSN + - Handle + - IGSN + - ISBN + - ISTC + - LISSN + - LSID + - PMID + - PURL + - UPC + - w3id + relation: + type: string + example: Cites + enum: + - IsCitedBy + - Cites + - IsSupplementTo + - IsSupplementedBy + - IsContinuedBy + - Continues + - IsDescribedBy + - Describes + - HasMetadata + - IsMetadataFor + - HasVersion + - IsVersionOf + - IsNewVersionOf + - IsPreviousVersionOf + - IsPartOf + - HasPart + - IsPublishedIn + - IsReferencedBy + - References + - IsDocumentedBy + - Documents + - IsCompiledBy + - Compiles + - IsVariantFormOf + - IsOriginalFormOf + - IsIdenticalTo + - IsReviewedBy + - Reviews + - IsDerivedFrom + - IsSourceOf + - IsRequiredBy + - Requires + - IsObsoletedBy + - Obsoletes + DatabaseModifyVisibilityDto: + required: + - is_public + - is_schema_public + type: object + properties: + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + ViewUpdateDto: + required: + - is_public + - is_schema_public + type: object + properties: + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + ViewColumnDto: + required: + - auto_generated + - database_id + - id + - internal_name + - is_null_allowed + - name + - ord + - type + type: object + properties: + id: + type: integer + format: int64 + name: + maxLength: 64 + minLength: 0 + type: string + example: Date + size: + type: integer + format: int64 + example: 255 + d: + type: integer + format: int64 + example: 0 + description: + maxLength: 2048 + minLength: 0 + type: string + example: Column comment + database_id: + type: integer + format: int64 + ord: + type: integer + format: int32 + example: 0 + internal_name: + maxLength: 64 + minLength: 0 + type: string + example: mdb_date + auto_generated: + type: boolean + example: false + index_length: + type: integer + format: int64 + length: + type: integer + format: int64 + type: + type: string + example: string + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + is_null_allowed: + type: boolean + example: false + ViewDto: + required: + - columns + - database_id + - id + - internal_name + - name + - owner + - query + - query_hash + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + identifiers: + type: array + items: + $ref: "#/components/schemas/IdentifierDto" + query: + type: string + example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC + owner: + $ref: "#/components/schemas/UserBriefDto" + columns: + type: array + items: + $ref: "#/components/schemas/ViewColumnDto" + last_retrieved: + type: string + format: date-time + database_id: + type: integer + format: int64 + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + initial_view: + type: boolean + description: True if it is the default view for the database + example: true + query_hash: + type: string + example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 + TableUpdateDto: + required: + - is_public + - is_schema_public + type: object + properties: + description: + maxLength: 180 + minLength: 0 + type: string + example: Air Quality in Austria + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + TableBriefDto: + required: + - database_id + - id + - internal_name + - is_public + - is_schema_public + - is_versioned + - name + - owned_by + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + description: + type: string + example: Air Quality in Austria + database_id: + type: integer + format: int64 + internal_name: + type: string + example: air_quality + is_versioned: + type: boolean + example: true + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + owned_by: + type: string + format: uuid + ColumnSemanticsUpdateDto: + type: object + properties: + concept_uri: + type: string + unit_uri: + type: string + ColumnDto: + required: + - database_id + - id + - internal_name + - is_null_allowed + - name + - ord + - table_id + - type + type: object + properties: + id: + type: integer + format: int64 + example: 1 + name: + maxLength: 64 + minLength: 0 + type: string + example: Given Name + alias: + type: string + example: firstname + size: + type: integer + format: int64 + example: 255 + d: + type: integer + format: int64 + example: 0 + mean: + type: number + example: 45.4 + median: + type: number + example: 51 + concept: + $ref: "#/components/schemas/ConceptBriefDto" + unit: + $ref: "#/components/schemas/UnitBriefDto" + description: + maxLength: 2048 + minLength: 0 + type: string + example: Column comment + enums: + type: array + items: + type: string + sets: + type: array + items: + type: string + database_id: + type: integer + format: int64 + example: 2 + table_id: + type: integer + format: int64 + example: 3 + ord: + type: integer + format: int32 + example: 0 + internal_name: + maxLength: 64 + minLength: 0 + type: string + example: given_name + index_length: + type: integer + format: int64 + example: 255 + length: + type: integer + format: int64 + example: 255 + type: + type: string + example: varchar + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + data_length: + type: integer + format: int64 + example: 34300 + max_data_length: + type: integer + format: int64 + example: 34300 + num_rows: + type: integer + format: int64 + example: 32 + val_min: + type: number + example: 0 + val_max: + type: number + example: 100 + std_dev: + type: number + example: 5.32 + is_null_allowed: + type: boolean + example: false + ConceptBriefDto: + required: + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + example: 23 + uri: + type: string + example: http://www.wikidata.org/entity/Q202444 + name: + type: string + example: given name + description: + type: string + example: "name typically used to differentiate people from the same family,\ + \ clan, or other social group who have a common last name" + UnitBriefDto: + required: + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + example: 34 + uri: + type: string + example: http://www.wikidata.org/entity/Q1422583 + name: + type: string + example: importance + description: + type: string + example: "subjective magnitude of value, meaning, or purpose" + DatabaseTransferDto: + required: + - id + type: object + properties: + id: + type: string + format: uuid + DatabaseModifyImageDto: + type: object + properties: + key: + type: string + UpdateDatabaseAccessDto: + required: + - type + type: object + properties: + type: + type: string + enum: + - read + - write_own + - write_all + SignupRequestDto: + required: + - email + - password + - username + type: object + properties: + username: + pattern: "^[a-z0-9]{3,}$" + type: string + example: user + email: + type: string + example: user@example.com + password: + type: string + LoginRequestDto: + required: + - password + - username + type: object + properties: + username: + type: string + example: user + password: + type: string + OntologyCreateDto: + required: + - prefix + - uri + type: object + properties: + uri: + type: string + example: Ontology URI + prefix: + type: string + example: Ontology prefix + sparql_endpoint: + type: string + example: Ontology SPARQL endpoint + BannerMessageCreateDto: + required: + - message + - type + type: object + properties: + type: + type: string + enum: + - error + - warning + - info + message: + type: string + example: Maintenance starts on 8am on Monday + link: + type: string + example: https://example.com + link_text: + type: string + example: More + display_start: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + display_end: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + ImageCreateDto: + required: + - default_port + - dialect + - driver_class + - is_default + - jdbc_method + - name + - registry + - version + type: object + properties: + registry: + type: string + example: docker.io/library + name: + type: string + example: mariadb + version: + type: string + dialect: + type: string + is_default: + type: boolean + example: false + driver_class: + type: string + jdbc_method: + type: string + default_port: + maximum: 65535 + minimum: 1024 + type: integer + format: int32 + IdentifierCreateDto: + required: + - creators + - database_id + - publication_year + - publisher + - titles + - type + type: object + properties: + type: + type: string + example: database + enum: + - database + - subset + - table + - view + doi: + type: string + example: 10.1111/11111111 + titles: + type: array + items: + $ref: "#/components/schemas/IdentifierSaveTitleDto" + descriptions: + type: array + items: + $ref: "#/components/schemas/IdentifierSaveDescriptionDto" + funders: + type: array + items: + $ref: "#/components/schemas/IdentifierFunderSaveDto" + licenses: + type: array + items: + $ref: "#/components/schemas/LicenseDto" + publisher: + type: string + example: TU Wien + language: + type: string + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + creators: + type: array + items: + $ref: "#/components/schemas/CreatorSaveDto" + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + view_id: + type: integer + format: int64 + table_id: + type: integer + format: int64 + publication_day: + type: integer + format: int32 + example: 15 + publication_month: + type: integer + format: int32 + example: 12 + publication_year: + type: integer + format: int32 + example: 2022 + related_identifiers: + type: array + items: + $ref: "#/components/schemas/RelatedIdentifierSaveDto" + DatabaseCreateDto: + required: + - container_id + - is_public + - is_schema_public + - name + type: object + properties: + name: + type: string + example: Air Quality + container_id: + type: integer + format: int64 + example: 1 + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + ViewCreateDto: + required: + - is_public + - is_schema_public + - name + - query + type: object + properties: + name: + maxLength: 63 + minLength: 1 + type: string + example: Air Quality + query: + type: string + example: SELECT `id` FROM `air_quality` + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + ViewBriefDto: + required: + - database_id + - id + - internal_name + - name + - query + - query_hash + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + query: + type: string + example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC + database_id: + type: integer + format: int64 + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + initial_view: + type: boolean + description: True if it is the default view for the database + example: true + query_hash: + type: string + example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 + owned_by: + type: string + format: uuid + ColumnCreateDto: + required: + - name + - null_allowed + - type + type: object + properties: + name: + type: string + example: Date + type: + type: string + example: string + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + size: + type: integer + format: int64 + example: 255 + d: + type: integer + format: int64 + example: 0 + description: + maxLength: 2048 + minLength: 0 + type: string + example: Formatted as YYYY-MM-dd + enums: + type: array + description: "enum values, only considered when type = ENUM" + items: + type: string + description: "enum values, only considered when type = ENUM" + sets: + type: array + description: "set values, only considered when type = SET" + items: + type: string + description: "set values, only considered when type = SET" + index_length: + type: integer + format: int64 + null_allowed: + type: boolean + example: true + concept_uri: + type: string + unit_uri: + type: string + ConstraintsCreateDto: + required: + - checks + - foreign_keys + - primary_key + - uniques + type: object + properties: + uniques: + type: array + items: + type: array + items: + type: string + checks: + uniqueItems: true + type: array + items: + type: string + foreign_keys: + type: array + items: + $ref: "#/components/schemas/ForeignKeyCreateDto" + primary_key: + uniqueItems: true + type: array + items: + type: string + ForeignKeyCreateDto: + required: + - columns + - referenced_columns + - referenced_table + type: object + properties: + columns: + type: array + items: + type: string + referenced_table: + type: string + referenced_columns: + type: array + items: + type: string + on_update: + type: string + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + on_delete: + type: string + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + TableCreateDto: + required: + - columns + - constraints + - is_public + - is_schema_public + - name + type: object + properties: + name: + maxLength: 64 + minLength: 1 + type: string + example: Air Quality + description: + maxLength: 180 + minLength: 0 + type: string + example: Air Quality in Austria + columns: + type: array + items: + $ref: "#/components/schemas/ColumnCreateDto" + constraints: + $ref: "#/components/schemas/ConstraintsCreateDto" + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + ContainerCreateDto: + required: + - host + - image_id + - name + - privileged_password + - privileged_username + - quota + type: object + properties: + name: + type: string + example: Air Quality + host: + type: string + description: Hostname of container + port: + type: integer + description: Port of container + format: int32 + quota: + type: integer + format: int64 + example: 50 + image_id: + type: integer + description: Image ID + format: int64 + ui_host: + type: string + ui_port: + type: integer + format: int32 + privileged_username: + type: string + description: Username of privileged user + example: root + privileged_password: + type: string + description: Password of privileged user + ContainerDto: + required: + - count + - id + - image + - internal_name + - name + - quota + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + image: + $ref: "#/components/schemas/ImageDto" + quota: + type: integer + format: int64 + example: 50 + count: + type: integer + format: int64 + example: 10 + last_retrieved: + type: string + format: date-time + internal_name: + type: string + example: data-db + ui_host: + type: string + ui_port: + type: integer + format: int32 + ColumnBriefDto: + required: + - column_type + - database_id + - id + - internal_name + - name + - table_id + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: date + alias: + type: string + database_id: + type: integer + format: int64 + table_id: + type: integer + format: int64 + internal_name: + type: string + example: mdb_date + column_type: + type: string + example: date + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + UnitDto: + required: + - columns + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + uri: + type: string + name: + type: string + description: + type: string + columns: + type: array + items: + $ref: "#/components/schemas/ColumnBriefDto" + OntologyBriefDto: + required: + - id + - prefix + - rdf + - sparql + - uri + type: object + properties: + id: + type: integer + format: int64 + uri: + type: string + example: http://www.wikidata.org/ + prefix: + type: string + example: wd + sparql: + type: boolean + example: true + rdf: + type: boolean + example: false + uri_pattern: + type: string + example: http://www.wikidata.org/entity/.* + EntityDto: + required: + - label + - uri + type: object + properties: + uri: + type: string + example: https://www.wikidata.org/entity/Q1686799 + label: + type: string + example: Apache Jena + description: + type: string + example: open source semantic web framework for Java + OaiListIdentifiersParameters: + type: object + properties: + metadataPrefix: + type: string + from: + type: string + until: + type: string + set: + type: string + resumptionToken: + type: string + fromDate: + type: string + format: date-time + untilDate: + type: string + format: date-time + parametersString: + type: string + BannerMessageDto: + required: + - id + - message + - type + type: object + properties: + id: + type: integer + format: int64 + type: + type: string + enum: + - error + - warning + - info + message: + type: string + example: Maintenance starts on 8am on Monday + link: + type: string + example: https://example.com + link_text: + type: string + example: More + display_start: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + display_end: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + ImageBriefDto: + required: + - default + - id + - jdbc_method + - name + - version + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: mariadb + version: + type: string + example: "10.5" + jdbc_method: + type: string + example: mariadb + default: + type: boolean + example: false + LdCreatorDto: + required: + - '@type' + - name + type: object + properties: + name: + type: string + sameAs: + type: string + givenName: + type: string + familyName: + type: string + '@type': + type: string + LdDatasetDto: + required: + - '@context' + - '@type' + - citation + - creator + - description + - hasPart + - identifier + - name + - temporalCoverage + - url + - version + type: object + properties: + name: + type: string + description: + type: string + url: + type: string + identifier: + type: array + items: + type: string + license: + type: string + creator: + type: array + items: + $ref: "#/components/schemas/LdCreatorDto" + citation: + type: string + hasPart: + type: array + items: + $ref: "#/components/schemas/LdDatasetDto" + temporalCoverage: + type: string + version: + type: string + format: date-time + '@context': + type: string + '@type': + type: string + ConstraintsDto: + type: object + properties: + uniques: + type: array + items: + $ref: "#/components/schemas/UniqueDto" + checks: + uniqueItems: true + type: array + items: + type: string + foreign_keys: + type: array + items: + $ref: "#/components/schemas/ForeignKeyDto" + primary_key: + uniqueItems: true + type: array + items: + $ref: "#/components/schemas/PrimaryKeyDto" + ForeignKeyBriefDto: + type: object + properties: + id: + type: integer + format: int64 + ForeignKeyDto: + required: + - name + - referenced_table + - references + - table + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + references: + type: array + items: + $ref: "#/components/schemas/ForeignKeyReferenceDto" + table: + $ref: "#/components/schemas/TableBriefDto" + referenced_table: + $ref: "#/components/schemas/TableBriefDto" + on_update: + type: string + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + on_delete: + type: string + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + ForeignKeyReferenceDto: + required: + - column + - foreign_key + - referenced_column + type: object + properties: + id: + type: integer + format: int64 + column: + $ref: "#/components/schemas/ColumnBriefDto" + foreign_key: + $ref: "#/components/schemas/ForeignKeyBriefDto" + referenced_column: + $ref: "#/components/schemas/ColumnBriefDto" + PrimaryKeyDto: + required: + - column + - table + type: object + properties: + id: + type: integer + format: int64 + table: + $ref: "#/components/schemas/TableBriefDto" + column: + $ref: "#/components/schemas/ColumnBriefDto" + TableDto: + required: + - columns + - constraints + - database_id + - id + - internal_name + - is_public + - is_schema_public + - is_versioned + - name + - owner + - queue_name + - routing_key + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + alias: + type: string + identifiers: + type: array + items: + $ref: "#/components/schemas/IdentifierDto" + owner: + $ref: "#/components/schemas/UserBriefDto" + description: + maxLength: 2048 + minLength: 0 + type: string + example: Air Quality in Austria + columns: + type: array + items: + $ref: "#/components/schemas/ColumnDto" + constraints: + $ref: "#/components/schemas/ConstraintsDto" + last_retrieved: + type: string + format: date-time + database_id: + type: integer + format: int64 + internal_name: + type: string + example: air_quality + is_versioned: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + queue_name: + type: string + example: air_quality + queue_type: + type: string + example: quorum + routing_key: + type: string + example: dbrepo.1.2 + is_public: + type: boolean + example: true + num_rows: + type: integer + format: int64 + example: 5 + data_length: + type: integer + description: in bytes + format: int64 + example: 16384 + max_data_length: + type: integer + description: in bytes + format: int64 + example: 0 + avg_row_length: + type: integer + description: in bytes + format: int64 + example: 3276 + UniqueDto: + required: + - columns + - id + - name + - table + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + table: + $ref: "#/components/schemas/TableBriefDto" + columns: + type: array + items: + $ref: "#/components/schemas/ColumnDto" + TableColumnEntityDto: + required: + - column_id + - database_id + - table_id + - uri + type: object + properties: + uri: + type: string + example: https://www.wikidata.org/entity/Q1686799 + label: + type: string + example: Apache Jena + description: + type: string + example: open source semantic web framework for Java + database_id: + type: integer + format: int64 + example: 1 + table_id: + type: integer + format: int64 + example: 1 + column_id: + type: integer + format: int64 + example: 1 + ContainerBriefDto: + required: + - count + - hash + - id + - image + - internal_name + - name + - quota + type: object + properties: + id: + type: integer + format: int64 + hash: + type: string + example: f829dd8a884182d0da846f365dee1221fd16610a14c81b8f9f295ff162749e50 + name: + type: string + example: Air Quality + image: + $ref: "#/components/schemas/ImageBriefDto" + quota: + type: integer + format: int32 + example: 50 + count: + type: integer + format: int32 + example: 10 + internal_name: + type: string + example: air-quality + ConceptDto: + required: + - columns + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + uri: + type: string + name: + type: string + description: + type: string + columns: + type: array + items: + $ref: "#/components/schemas/ColumnBriefDto" + securitySchemes: + basicAuth: + type: http + scheme: basic + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT diff --git a/.docs/.openapi/api-search.yaml b/.docs/.openapi/api-search.yaml new file mode 100644 index 0000000000..c47cbfb649 --- /dev/null +++ b/.docs/.openapi/api-search.yaml @@ -0,0 +1,387 @@ +{ + "components": { + "schemas": { + "IndexDto": { + "properties": { + "results": { + "items": { + "type": "object" + }, + "type": "array" + }, + "type": { + "description": "Same as the requested type", + "enum": [ + "database", + "table", + "view", + "column", + "user", + "identifier", + "concept", + "unit" + ], + "type": "string" + } + }, + "required": [ + "results", + "type" + ] + }, + "IndexFieldDto": { + "properties": { + "attr_friendly_name": { + "example": "Name", + "type": "string" + }, + "attr_name": { + "example": "name", + "type": "string" + }, + "type": { + "description": "OpenSearch data types.", + "example": "string", + "type": "string" + } + }, + "required": [ + "attr_name", + "attr_friendly_name", + "type" + ], + "type": "object" + }, + "IndexFieldsDto": { + "properties": { + "results": { + "items": { + "$ref": "#/components/schemas/IndexFieldDto" + }, + "type": "array" + } + }, + "required": [ + "results" + ], + "type": "object" + }, + "SearchRequestDto": { + "properties": { + "field_value_pairs": { + "type": "object" + }, + "search_term": { + "type": "string" + } + }, + "required": [ + "search_term", + "field_value_pairs" + ], + "type": "object" + } + }, + "securitySchemes": { + "basicAuth": { + "in": "header", + "scheme": "basic", + "type": "http" + }, + "bearerAuth": { + "bearerFormat": "JWT", + "in": "header", + "scheme": "bearer", + "type": "http" + } + } + }, + "externalDocs": { + "description": "Sourcecode Documentation", + "url": "https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.5/" + }, + "info": { + "contact": { + "email": "andreas.rauber@tuwien.ac.at", + "name": "Prof. Andreas Rauber" + }, + "description": "Service that searches the search database", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0" + }, + "title": "Database Repository Search Service API", + "version": "1.5" + }, + "openapi": "3.0.0", + "paths": { + "/api/search": { + "get": { + "consumes": [ + "application/json" + ], + "description": "Performs a fuzzy search", + "operationId": "post_fuzzy_search", + "parameters": [ + { + "in": "query", + "name": "q", + "required": true, + "schema": { + "type": "string" + } + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "id": { + "type": "string" + } + }, + "type": "array" + } + } + }, + "description": "OK, contains the elements formatted as an array of JSON arrays" + }, + "415": { + "description": "Wrong accept type" + } + }, + "summary": "Performs a fuzzy search", + "tags": [ + "search-endpoint" + ] + } + }, + "/api/search/{field_type}": { + "post": { + "consumes": [ + "application/json" + ], + "description": "Performs a general search", + "operationId": "post_general_search", + "parameters": [ + { + "description": "The search type.", + "in": "path", + "name": "type", + "required": true, + "schema": { + "enum": [ + "database", + "table", + "view", + "column", + "user", + "identifier", + "concept", + "unit" + ], + "type": "string" + } + }, + { + "in": "query", + "name": "t1", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "t2", + "schema": { + "type": "integer" + } + }, + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/components/schemas/SearchRequestDto" + } + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "results": { + "items": { + "type": "object" + }, + "type": "array" + }, + "type": { + "description": "Same as the requested type", + "enum": [ + "database", + "table", + "view", + "column", + "user", + "identifier", + "concept", + "unit" + ], + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "OK, contains the elements formatted as an array of JSON arrays" + } + }, + "summary": "Performs a general search", + "tags": [ + "search-endpoint" + ] + } + }, + "/api/search/{field_type}/fields": { + "get": { + "operationId": "get_fields", + "parameters": [ + { + "description": "The search type.", + "in": "path", + "name": "type", + "required": true, + "schema": { + "enum": [ + "database", + "table", + "view", + "column", + "user", + "identifier", + "concept", + "unit" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IndexFieldsDto" + } + } + }, + "description": "List of fields" + }, + "404": { + "description": "Invalid type." + } + }, + "summary": "Get searchable fields", + "tags": [ + "search-endpoint" + ] + } + }, + "/api/search/{index}": { + "get": { + "consumes": [ + "application/json" + ], + "description": "Gets the index", + "operationId": "get_index", + "parameters": [ + { + "description": "The search type.", + "in": "path", + "name": "type", + "required": true, + "schema": { + "enum": [ + "database", + "table", + "view", + "column", + "user", + "identifier", + "concept", + "unit" + ], + "type": "string" + } + }, + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "properties": { + "field_value_pairs": { + "type": "object" + }, + "search_term": { + "example": "air quality", + "type": "string" + }, + "t1": { + "example": 0, + "type": "integer" + }, + "t2": { + "example": 100, + "type": "integer" + } + }, + "type": "object" + } + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IndexDto" + } + } + }, + "description": "OK, contains the elements formatted as an array of JSON arrays" + } + }, + "summary": "Gets the index", + "tags": [ + "search-endpoint" + ] + } + } + }, + "servers": [ + { + "description": "Generated server url", + "url": "http://localhost:4000" + }, + { + "description": "Sandbox", + "url": "https://test.dbrepo.tuwien.ac.at" + } + ] +} diff --git a/.docs/.swagger/api.base.yaml b/.docs/.openapi/api.base.yaml similarity index 100% rename from .docs/.swagger/api.base.yaml rename to .docs/.openapi/api.base.yaml diff --git a/.docs/.swagger/api.yaml b/.docs/.openapi/api.yaml similarity index 97% rename from .docs/.swagger/api.yaml rename to .docs/.openapi/api.yaml index dc7a627e74..ef55c74ae9 100644 --- a/.docs/.swagger/api.yaml +++ b/.docs/.openapi/api.yaml @@ -432,6 +432,11 @@ paths: schema: type: integer format: int64 + - name: Authorization + in: header + required: true + schema: + type: string requestBody: content: application/json: @@ -490,6 +495,11 @@ paths: schema: type: integer format: int64 + - name: Authorization + in: header + required: true + schema: + type: string requestBody: content: application/json: @@ -550,6 +560,11 @@ paths: schema: type: integer format: int64 + - name: Authorization + in: header + required: true + schema: + type: string requestBody: content: application/json: @@ -937,6 +952,11 @@ paths: schema: type: integer format: int64 + - name: Authorization + in: header + required: true + schema: + type: string requestBody: content: application/json: @@ -979,9 +999,11 @@ paths: - subset-endpoint summary: Find subsets description: >- - Finds subsets in the query store. The result can be optionally filtered - by setting `persisted`. When set to *true*, only persisted queries are - returned, otherwise only non-persisted queries are returned. + Finds subsets in the query store. When the database schema is marked as + hidden, the user needs to be authorized, have at least read-access to + the database. The result can be optionally filtered by setting + `persisted`. When set to *true*, only persisted queries are returned, + otherwise only non-persisted queries are returned. operationId: list parameters: - name: databaseId @@ -1032,8 +1054,10 @@ paths: - subset-endpoint summary: Create subset description: >- - Creates a subset in the query store of the data database. Requires role - `execute-query` for private databases. + Creates a subset in the query store of the data database. Can also be + used without authentication if (and only if) the database is marked as + public (i.e. when `is_public` = `is_schema_public` is set to `true`). + Otherwise at least read access is required. operationId: create parameters: - name: databaseId @@ -1313,9 +1337,11 @@ paths: - subset-endpoint summary: Find subset description: >- - Finds a subset in the data database. Requests with HTTP header - `Accept=application/json` return the metadata, requests with HTTP header - `Accept=text/csv` return the data as downloadable file. + Finds a subset in the data database. When the database schema is marked + as hidden, the user needs to be authorized, have at least read-access to + the database. Requests with HTTP header `Accept=application/json` + return the metadata, requests with HTTP header `Accept=text/csv` return + the data as downloadable file. operationId: findById parameters: - name: databaseId @@ -1330,6 +1356,11 @@ paths: schema: type: integer format: int64 + - name: Accept + in: header + required: true + schema: + type: string - name: timestamp in: query required: false @@ -1412,7 +1443,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/DatabaseDto' + $ref: '#/components/schemas/DatabaseBriefDto' post: tags: - database-endpoint @@ -1433,7 +1464,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DatabaseDto' + $ref: '#/components/schemas/DatabaseBriefDto' '400': description: Database create query is malformed or image is not supported content: @@ -1513,7 +1544,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/DatabaseDto' + $ref: '#/components/schemas/DatabaseBriefDto' '/api/database/{databaseId}/access/{userId}': get: tags: @@ -1568,7 +1599,7 @@ paths: description: >- Modifies access of a user with given id to database with given id. Requires role `update-database-access`. - operationId: update_6 + operationId: update_5 parameters: - name: databaseId in: path @@ -1803,7 +1834,11 @@ paths: tags: - user-endpoint summary: Get user - description: Gets user with id from the metadata database. Requires authentication. + description: >- + Gets own user information from the metadata database. Requires + authentication. Foreign user information can only be obtained if + additional role `find-foreign-user` is present. Finding information + about internal users results in a 404 error. operationId: find_2 parameters: - name: userId @@ -1979,7 +2014,7 @@ paths: tags: - user-endpoint summary: Create token - description: Creates a user token via the auth service. + description: Creates a user token via the Auth Service. operationId: getToken requestBody: content: @@ -2544,7 +2579,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DatabaseDto' + $ref: '#/components/schemas/DatabaseBriefDto' '400': description: The visibility payload is malformed content: @@ -2783,8 +2818,10 @@ paths: - table-endpoint summary: Find table description: >- - Finds a table with id. When the `system` role is present, the endpoint - responds with additional connection metadata in the header. + Finds a table with id. When a table is hidden (i.e. when `is_public` is + `false`), then the user needs to have at least read access and the role + `find-table`. When the `system` role is present, the endpoint responds + with additional connection metadata in the header. operationId: findById_2 parameters: - name: databaseId @@ -2988,8 +3025,8 @@ paths: summary: Update statistics description: >- Updates basic statistical properties (min, max, mean, median, std.dev) - for numerical columns in a table with id. Requires role - `update-table-statistic`. + for numerical columns in a table with id. This action can only be + performed by the table owner. Requires role `update-table-statistic`. operationId: updateStatistic parameters: - name: databaseId @@ -3013,6 +3050,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ApiErrorDto' + '403': + description: Not the owner + content: + application/json: + schema: + $ref: '#/components/schemas/ApiErrorDto' '404': description: Failed to find database/table in metadata database content: @@ -3044,7 +3087,7 @@ paths: with at least *READ* access to the associated database can update the column semantics (requires role `modify-table-column-semantics`) or foreign table columns if role `modify-foreign-table-column-semantics`. - operationId: update_5 + operationId: updateColumn parameters: - name: databaseId in: path @@ -3140,7 +3183,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DatabaseDto' + $ref: '#/components/schemas/DatabaseBriefDto' '400': description: Owner payload is malformed content: @@ -3197,7 +3240,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DatabaseDto' + $ref: '#/components/schemas/DatabaseBriefDto' '403': description: Refresh view metadata is not permitted content: @@ -3248,7 +3291,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DatabaseDto' + $ref: '#/components/schemas/DatabaseBriefDto' '400': description: Failed to parse payload at search service content: @@ -3342,7 +3385,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DatabaseDto' + $ref: '#/components/schemas/DatabaseBriefDto' '403': description: Modify of image is not permitted content: @@ -3350,7 +3393,7 @@ paths: schema: $ref: '#/components/schemas/ApiErrorDto' '404': - description: Database or user could not be found + description: Database could not be found content: application/json: schema: @@ -3381,7 +3424,11 @@ paths: tags: - user-endpoint summary: List users - description: Lists users known to the metadata database. + description: >- + Lists users known to the metadata database. Internal users are omitted + from the result list. If the optional query parameter `username` is + present, the result list can be filtered by matching this exact + username. operationId: findAll parameters: - name: username @@ -3819,7 +3866,10 @@ paths: tags: - table-endpoint summary: List tables - description: Lists all tables known to the metadata database. + description: >- + Lists all tables known to the metadata database. When a database has a + hidden schema (i.e. when `is_schema_public` is `false`), then the user + needs to have at least read access and the role `list-tables`. operationId: list_4 parameters: - name: databaseId @@ -4186,7 +4236,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DatabaseDto' + $ref: '#/components/schemas/DatabaseBriefDto' + '403': + description: Not allowed to view database + content: + application/json: + schema: + $ref: '#/components/schemas/ApiErrorDto' '404': description: 'Database, user or exchange could not be found' content: @@ -4214,8 +4270,8 @@ paths: - table-endpoint summary: Suggest semantics description: >- - Suggests semantic concepts for a table. Requires role - `table-semantic-analyse`. + Suggests semantic concepts for a table. This action can only be + performed by the table owner. Requires role `table-semantic-analyse`. operationId: analyseTable parameters: - name: databaseId @@ -4245,6 +4301,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ApiErrorDto' + '403': + description: Not the table owner. + content: + application/json: + schema: + $ref: '#/components/schemas/ApiErrorDto' '404': description: Failed to find database/table in metadata database content: @@ -4417,7 +4479,10 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SearchResultDto' + properties: + id: + type: string + type: array description: 'OK, contains the elements formatted as an array of JSON arrays' '415': description: Wrong accept type @@ -5168,64 +5233,15 @@ components: type: object additionalProperties: type: object - ContainerBriefDto: - required: - - count - - hash - - id - - image - - internal_name - - name - - quota - type: object - properties: - id: - type: integer - format: int64 - hash: - type: string - example: f829dd8a884182d0da846f365dee1221fd16610a14c81b8f9f295ff162749e50 - name: - type: string - example: Air Quality - image: - $ref: '#/components/schemas/ImageBriefDto' - quota: - type: integer - format: int32 - example: 50 - count: - type: integer - format: int32 - example: 10 - internal_name: - type: string - example: air-quality - DatabaseAccessDto: - required: - - type - - user - type: object - properties: - user: - $ref: '#/components/schemas/UserBriefDto' - type: - type: string - enum: - - read - - write_own - - write_all - DatabaseDto: + DatabaseBriefDto: required: - contact - - container - - exchange_name - id - internal_name - is_public - is_schema_public - name - - owner + - owner_id type: object properties: id: @@ -5237,153 +5253,40 @@ components: description: type: string example: Air Quality - tables: - type: array - items: - $ref: '#/components/schemas/TableBriefDto' - views: - type: array - items: - $ref: '#/components/schemas/ViewBriefDto' - container: - $ref: '#/components/schemas/ContainerBriefDto' - accesses: - type: array - items: - $ref: '#/components/schemas/DatabaseAccessDto' identifiers: type: array items: $ref: '#/components/schemas/IdentifierBriefDto' - subsets: - type: array - items: - $ref: '#/components/schemas/IdentifierBriefDto' contact: $ref: '#/components/schemas/UserBriefDto' - owner: - $ref: '#/components/schemas/UserBriefDto' - exchange_name: - type: string - example: dbrepo - exchange_type: - type: string - example: topic - internal_name: - type: string - example: air_quality - is_public: - type: boolean - example: true - is_schema_public: - type: boolean - example: true - preview_image: - type: string - ImageBriefDto: - required: - - default - - id - - jdbc_method - - name - - version - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - example: mariadb - version: - type: string - example: '10.5' - jdbc_method: - type: string - example: mariadb - default: - type: boolean - example: false - TableBriefDto: - required: - - database_id - - id - - internal_name - - is_public - - is_schema_public - - is_versioned - - name - - owned_by - type: object - properties: - id: - type: integer - format: int64 - name: - type: string - example: Air Quality - description: - type: string - example: Air Quality in Austria - database_id: - type: integer - format: int64 internal_name: type: string example: air_quality - is_versioned: - type: boolean - example: true is_public: type: boolean example: true is_schema_public: type: boolean example: true - owned_by: + owner_id: type: string format: uuid - ViewBriefDto: + preview_image: + type: string + DatabaseAccessDto: required: - - database_id - - id - - internal_name - - name - - query - - query_hash + - type + - user type: object properties: - id: - type: integer - format: int64 - name: - type: string - example: Air Quality - query: - type: string - example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC - database_id: - type: integer - format: int64 - internal_name: - type: string - example: air_quality - is_public: - type: boolean - example: true - is_schema_public: - type: boolean - example: true - initial_view: - type: boolean - description: True if it is the default view for the database - example: true - query_hash: - type: string - example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 - owned_by: + user: + $ref: '#/components/schemas/UserBriefDto' + type: type: string - format: uuid + enum: + - read + - write_own + - write_all UserUpdateDto: required: - language @@ -5430,22 +5333,20 @@ components: required: - attributes - id - - username type: object properties: id: type: string format: uuid example: 1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4 - username: - type: string - description: Only contains lowercase characters - example: jcarberry name: type: string example: Josiah Carberry attributes: $ref: '#/components/schemas/UserAttributesDto' + last_retrieved: + type: string + format: date-time qualified_name: type: string example: Josiah Carberry — @jcarberry @@ -5747,6 +5648,9 @@ components: - value type: object properties: + id: + type: integer + format: int64 value: type: string example: XOR @@ -6883,10 +6787,8 @@ components: - Other IdentifierDto: required: - - created_by - creators - database_id - - execution - id - owner - publication_year @@ -6936,7 +6838,7 @@ components: type: string example: TU Wien owner: - $ref: '#/components/schemas/UserDto' + $ref: '#/components/schemas/UserBriefDto' language: type: string enum: @@ -7184,9 +7086,6 @@ components: type: integer format: int32 example: 2022 - created_by: - type: string - format: uuid IdentifierFunderDto: required: - funder_name @@ -7318,140 +7217,34 @@ components: is_schema_public: type: boolean example: true - ColumnBriefDto: + ViewColumnDto: required: - - column_type + - auto_generated - database_id - id - internal_name + - is_null_allowed - name - - table_id + - ord + - type type: object properties: id: type: integer format: int64 name: + maxLength: 64 + minLength: 0 type: string - example: date - alias: - type: string - database_id: + example: Date + size: type: integer format: int64 - table_id: - type: integer - format: int64 - internal_name: - type: string - example: mdb_date - column_type: - type: string - example: date - enum: - - char - - varchar - - binary - - varbinary - - tinyblob - - tinytext - - text - - blob - - mediumtext - - mediumblob - - longtext - - longblob - - enum - - set - - serial - - bit - - tinyint - - bool - - smallint - - mediumint - - int - - bigint - - float - - double - - decimal - - date - - datetime - - timestamp - - time - - year - ConceptDto: - required: - - columns - - id - - uri - type: object - properties: - id: - type: integer - format: int64 - uri: - type: string - name: - type: string - description: - type: string - columns: - type: array - items: - $ref: '#/components/schemas/ColumnBriefDto' - UnitDto: - required: - - columns - - id - - uri - type: object - properties: - id: - type: integer - format: int64 - uri: - type: string - name: - type: string - description: - type: string - columns: - type: array - items: - $ref: '#/components/schemas/ColumnBriefDto' - ViewColumnDto: - required: - - auto_generated - - column_type - - database_id - - id - - internal_name - - is_null_allowed - - is_public - - name - - ordinal_position - type: object - properties: - id: - type: integer - format: int64 - name: - maxLength: 64 - minLength: 0 - type: string - example: Date - size: - type: integer - format: int64 - example: 255 - d: + example: 255 + d: type: integer format: int64 example: 0 - concept: - $ref: '#/components/schemas/ConceptDto' - unit: - $ref: '#/components/schemas/UnitDto' description: maxLength: 2048 minLength: 0 @@ -7460,7 +7253,7 @@ components: database_id: type: integer format: int64 - ordinal_position: + ord: type: integer format: int32 example: 0 @@ -7478,7 +7271,7 @@ components: length: type: integer format: int64 - column_type: + type: type: string example: string enum: @@ -7512,16 +7305,12 @@ components: - timestamp - time - year - is_public: - type: boolean - example: true is_null_allowed: type: boolean example: false ViewDto: required: - columns - - database - database_id - id - internal_name @@ -7534,8 +7323,6 @@ components: id: type: integer format: int64 - database: - $ref: '#/components/schemas/DatabaseDto' name: type: string example: Air Quality @@ -7552,6 +7339,9 @@ components: type: array items: $ref: '#/components/schemas/ViewColumnDto' + last_retrieved: + type: string + format: date-time database_id: type: integer format: int64 @@ -7588,6 +7378,45 @@ components: is_schema_public: type: boolean example: true + TableBriefDto: + required: + - database_id + - id + - internal_name + - is_public + - is_schema_public + - is_versioned + - name + - owned_by + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + description: + type: string + example: Air Quality in Austria + database_id: + type: integer + format: int64 + internal_name: + type: string + example: air_quality + is_versioned: + type: boolean + example: true + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + owned_by: + type: string + format: uuid ColumnSemanticsUpdateDto: type: object properties: @@ -7597,27 +7426,28 @@ components: type: string ColumnDto: required: - - column_type - database_id - id - internal_name - is_null_allowed - - is_public - name - - ordinal_position + - ord - table_id + - type type: object properties: id: type: integer format: int64 + example: 1 name: maxLength: 64 minLength: 0 type: string - example: Date + example: Given Name alias: type: string + example: firstname size: type: integer format: int64 @@ -7633,9 +7463,9 @@ components: type: number example: 51 concept: - $ref: '#/components/schemas/ConceptDto' + $ref: '#/components/schemas/ConceptBriefDto' unit: - $ref: '#/components/schemas/UnitDto' + $ref: '#/components/schemas/UnitBriefDto' description: maxLength: 2048 minLength: 0 @@ -7652,10 +7482,12 @@ components: database_id: type: integer format: int64 + example: 2 table_id: type: integer format: int64 - ordinal_position: + example: 3 + ord: type: integer format: int32 example: 0 @@ -7663,16 +7495,18 @@ components: maxLength: 64 minLength: 0 type: string - example: mdb_date + example: given_name index_length: type: integer format: int64 + example: 255 length: type: integer format: int64 - column_type: + example: 255 + type: type: string - example: string + example: varchar enum: - char - varchar @@ -7725,12 +7559,49 @@ components: std_dev: type: number example: 5.32 - is_public: - type: boolean - example: true is_null_allowed: type: boolean example: false + ConceptBriefDto: + required: + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + example: 23 + uri: + type: string + example: 'http://www.wikidata.org/entity/Q202444' + name: + type: string + example: given name + description: + type: string + example: >- + name typically used to differentiate people from the same family, + clan, or other social group who have a common last name + UnitBriefDto: + required: + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + example: 34 + uri: + type: string + example: 'http://www.wikidata.org/entity/Q1422583' + name: + type: string + example: importance + description: + type: string + example: 'subjective magnitude of value, meaning, or purpose' DatabaseTransferDto: required: - id @@ -8163,6 +8034,47 @@ components: is_schema_public: type: boolean example: true + ViewBriefDto: + required: + - database_id + - id + - internal_name + - name + - query + - query_hash + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + query: + type: string + example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC + database_id: + type: integer + format: int64 + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + initial_view: + type: boolean + description: True if it is the default view for the database + example: true + query_hash: + type: string + example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 + owned_by: + type: string + format: uuid ColumnCreateDto: required: - name @@ -8377,12 +8289,10 @@ components: ContainerDto: required: - count - - host - id - image - internal_name - name - - port - quota type: object properties: @@ -8392,11 +8302,6 @@ components: name: type: string example: Air Quality - host: - type: string - port: - type: integer - format: int32 image: $ref: '#/components/schemas/ImageDto' quota: @@ -8407,6 +8312,9 @@ components: type: integer format: int64 example: 10 + last_retrieved: + type: string + format: date-time internal_name: type: string example: data-db @@ -8415,6 +8323,87 @@ components: ui_port: type: integer format: int32 + ColumnBriefDto: + required: + - column_type + - database_id + - id + - internal_name + - name + - table_id + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: date + alias: + type: string + database_id: + type: integer + format: int64 + table_id: + type: integer + format: int64 + internal_name: + type: string + example: mdb_date + column_type: + type: string + example: date + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + UnitDto: + required: + - columns + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + uri: + type: string + name: + type: string + description: + type: string + columns: + type: array + items: + $ref: '#/components/schemas/ColumnBriefDto' OntologyBriefDto: required: - id @@ -8470,14 +8459,14 @@ components: type: string resumptionToken: type: string - parametersString: - type: string fromDate: type: string format: date-time untilDate: type: string format: date-time + parametersString: + type: string BannerMessageDto: required: - id @@ -8511,6 +8500,30 @@ components: type: string format: date-time example: '2021-03-12T15:26:21.000Z' + ImageBriefDto: + required: + - default + - id + - jdbc_method + - name + - version + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: mariadb + version: + type: string + example: '10.5' + jdbc_method: + type: string + example: mariadb + default: + type: boolean + example: false LdCreatorDto: required: - '@type' @@ -8707,6 +8720,9 @@ components: $ref: '#/components/schemas/ColumnDto' constraints: $ref: '#/components/schemas/ConstraintsDto' + last_retrieved: + type: string + format: date-time database_id: type: integer format: int64 @@ -8798,6 +8814,59 @@ components: type: integer format: int64 example: 1 + ContainerBriefDto: + required: + - count + - hash + - id + - image + - internal_name + - name + - quota + type: object + properties: + id: + type: integer + format: int64 + hash: + type: string + example: f829dd8a884182d0da846f365dee1221fd16610a14c81b8f9f295ff162749e50 + name: + type: string + example: Air Quality + image: + $ref: '#/components/schemas/ImageBriefDto' + quota: + type: integer + format: int32 + example: 50 + count: + type: integer + format: int32 + example: 10 + internal_name: + type: string + example: air-quality + ConceptDto: + required: + - columns + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + uri: + type: string + name: + type: string + description: + type: string + columns: + type: array + items: + $ref: '#/components/schemas/ColumnBriefDto' IndexDto: properties: results: @@ -8855,12 +8924,3 @@ components: - search_term - field_value_pairs type: object - SearchResultDto: - properties: - results: - items: - type: object - type: array - required: - - results - type: object diff --git a/.docs/.swagger/custom.css b/.docs/.openapi/custom.css similarity index 100% rename from .docs/.swagger/custom.css rename to .docs/.openapi/custom.css diff --git a/.docs/.swagger/swagger-generate.sh b/.docs/.openapi/openapi-generate.sh similarity index 82% rename from .docs/.swagger/swagger-generate.sh rename to .docs/.openapi/openapi-generate.sh index 884c2adfac..edd927d202 100644 --- a/.docs/.swagger/swagger-generate.sh +++ b/.docs/.openapi/openapi-generate.sh @@ -10,10 +10,10 @@ services[9099]=metadata function retrieve () { if [[ "$2" == analyse ]] || [[ "$2" == search ]]; then echo "... retrieve json api from localhost:$1" - curl -sSL "http://localhost:$1/api-$2.json" | yq -p=json > "./.docs/.swagger/api-$2.yaml" + curl -sSL "http://localhost:$1/api-$2.json" | yq -o=json - > "./.docs/.openapi/api-$2.yaml" else echo "... retrieve yaml api from localhost:$1" - curl -sSL "http://localhost:$1/v3/api-docs.yaml" > "./.docs/.swagger/api-$2.yaml" + curl -sSL "http://localhost:$1/v3/api-docs.yaml" > "./.docs/.openapi/api-$2.yaml" fi } diff --git a/.docs/.swagger/openapi-merge.json b/.docs/.openapi/openapi-merge.json similarity index 100% rename from .docs/.swagger/openapi-merge.json rename to .docs/.openapi/openapi-merge.json diff --git a/.docs/.swagger/swagger-ui.html b/.docs/.openapi/swagger-ui.html similarity index 100% rename from .docs/.swagger/swagger-ui.html rename to .docs/.openapi/swagger-ui.html diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dfc73f621d..100e56ff21 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -44,11 +44,11 @@ lint-docker-compose: image: docker.io/alpine:${ALPINE_VERSION} stage: lint variables: - VERSION: 3.3.0 + VERSION: 4.45.1 BINARY: yq_linux_amd64 before_script: - 'apk --no-cache add bash wget' - - 'wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY} -O /usr/bin/yq && chmod +x /usr/bin/yq' + - 'wget https://github.com/mikefarah/yq/releases/download/v${VERSION}/${BINARY} -O /usr/bin/yq && chmod +x /usr/bin/yq' script: - "bash .scripts/check-compose.sh" - "yq compare -P docker-compose.yml .docker/docker-compose.yml 'volumes.*'" @@ -116,18 +116,18 @@ lint-metadata-schema: script: - diff dbrepo-metadata-db/1_setup-schema.sql helm/dbrepo/files/01-setup-schema.sql -lint-swagger-version: +lint-open-api-version: image: docker.io/alpine:${ALPINE_VERSION} stage: lint variables: - VERSION: 3.3.0 + VERSION: 4.45.1 BINARY: yq_linux_amd64 before_script: - 'apk --no-cache add bash wget' - - 'wget https://github.com/mikefarah/yq/releases/download/${VERSION}/${BINARY} -O /usr/bin/yq && chmod +x /usr/bin/yq' + - 'wget https://github.com/mikefarah/yq/releases/download/v${VERSION}/${BINARY} -O /usr/bin/yq && chmod +x /usr/bin/yq' script: - - yq r ./.docs/.swagger/api.base.yaml 'externalDocs.url' | grep "${DOC_VERSION}" - - yq r ./.docs/.swagger/api.base.yaml 'info.version' | grep "${DOC_VERSION}" + - yq r ./.docs/.openapi/api.base.yaml 'externalDocs.url' | grep "${DOC_VERSION}" + - yq r ./.docs/.openapi/api.base.yaml 'info.version' | grep "${DOC_VERSION}" build-metadata-service: image: maven:3-openjdk-${JAVA_VERSION} @@ -483,9 +483,9 @@ release-docs: script: - "make gen-lib-doc gen-docs-doc package-config" - "cp -r ./lib/python/docs/build/html ./final/${DOC_VERSION}/python" # sphinx - - "cp .docs/.swagger/api.yaml ./final/${DOC_VERSION}/rest/api.yaml" # swagger - - "cp .docs/.swagger/swagger-ui.html ./final/${DOC_VERSION}/rest/index.html" # swagger - - "cp .docs/.swagger/custom.css ./final/${DOC_VERSION}/rest/custom.css" # swagger + - "cp .docs/.openapi/api.yaml ./final/${DOC_VERSION}/rest/api.yaml" # openapi + - "cp .docs/.openapi/swagger-ui.html ./final/${DOC_VERSION}/rest/index.html" # openapi + - "cp .docs/.openapi/custom.css ./final/${DOC_VERSION}/rest/custom.css" # openapi - "cp -r ./site/* ./final/${DOC_VERSION}" # mkdocs - "cp .docker/dist.tar.gz ./final/${APP_VERSION}/dist.tar.gz" # dist - "cp .docs/index.html.tpl ./final/index.html" # redirect patch docs 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 1f8d3b046e..465bd3bd1e 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 @@ -25,13 +25,16 @@ import java.util.List; public class ColumnDto { @NotNull + @Schema(example = "1") private Long id; @NotNull + @Schema(example = "2") @JsonProperty("database_id") private Long databaseId; @NotNull + @Schema(example = "3") @JsonProperty("table_id") private Long tableId; @@ -42,27 +45,29 @@ public class ColumnDto { @NotBlank @Size(max = 64) - @Schema(example = "Date") + @Schema(example = "Given Name") private String name; @NotBlank @Size(max = 64) @JsonProperty("internal_name") - @Schema(example = "mdb_date") + @Schema(example = "given_name") private String internalName; - @Schema + @Schema(example = "firstname") private String alias; @JsonProperty("index_length") + @Schema(example = "255") private Long indexLength; @JsonProperty("length") + @Schema(example = "255") private Long length; @NotNull @JsonProperty("type") - @Schema(example = "string") + @Schema(example = "varchar") private ColumnTypeDto columnType; @Schema(example = "255") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptBriefDto.java index 1e2b36dc66..3cc1231c74 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/ConceptBriefDto.java @@ -1,5 +1,6 @@ package at.tuwien.api.database.table.columns.concepts; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.*; @@ -16,13 +17,17 @@ import lombok.extern.jackson.Jacksonized; public class ConceptBriefDto { @NotNull + @Schema(example = "23") private Long id; @NotBlank + @Schema(example = "http://www.wikidata.org/entity/Q202444") private String uri; + @Schema(example = "given name") private String name; + @Schema(example = "name typically used to differentiate people from the same family, clan, or other social group who have a common last name") private String description; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitBriefDto.java index 407f3708eb..15d9ae9082 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/concepts/UnitBriefDto.java @@ -1,5 +1,6 @@ package at.tuwien.api.database.table.columns.concepts; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.*; @@ -16,12 +17,16 @@ import lombok.extern.jackson.Jacksonized; public class UnitBriefDto { @NotNull + @Schema(example = "34") private Long id; @NotBlank + @Schema(example = "http://www.wikidata.org/entity/Q1422583") private String uri; + @Schema(example = "importance") private String name; + @Schema(example = "subjective magnitude of value, meaning, or purpose") private String description; } diff --git a/lib/python/Pipfile b/lib/python/Pipfile index b54561e5f8..62a93cc02a 100644 --- a/lib/python/Pipfile +++ b/lib/python/Pipfile @@ -13,6 +13,7 @@ pandas = "*" [dev-packages] build = "*" +pyyaml = "*" setuptools = "*" twine = "*" coverage = "*" diff --git a/lib/python/Pipfile.lock b/lib/python/Pipfile.lock index 836140fd7b..52b7202c3e 100644 --- a/lib/python/Pipfile.lock +++ b/lib/python/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "4fc272b993d5091dc8d60aee49575f17cd1de3145d916dc6aa82c09f7cefe4ee" + "sha256": "01e7f752292f6f3d558a9418f7172696f9bd200d00c7eed2745b74d08ef27eb4" }, "pipfile-spec": 6, "requires": { @@ -442,64 +442,64 @@ }, "numpy": { "hashes": [ - "sha256:059e6a747ae84fce488c3ee397cee7e5f905fd1bda5fb18c66bc41807ff119b2", - "sha256:08ef779aed40dbc52729d6ffe7dd51df85796a702afbf68a4f4e41fafdc8bda5", - "sha256:164a829b6aacf79ca47ba4814b130c4020b202522a93d7bff2202bfb33b61c60", - "sha256:26c9c4382b19fcfbbed3238a14abf7ff223890ea1936b8890f058e7ba35e8d71", - "sha256:27f5cdf9f493b35f7e41e8368e7d7b4bbafaf9660cba53fb21d2cd174ec09631", - "sha256:31b89fa67a8042e96715c68e071a1200c4e172f93b0fbe01a14c0ff3ff820fc8", - "sha256:32cb94448be47c500d2c7a95f93e2f21a01f1fd05dd2beea1ccd049bb6001cd2", - "sha256:360137f8fb1b753c5cde3ac388597ad680eccbbbb3865ab65efea062c4a1fd16", - "sha256:3683a8d166f2692664262fd4900f207791d005fb088d7fdb973cc8d663626faa", - "sha256:38efc1e56b73cc9b182fe55e56e63b044dd26a72128fd2fbd502f75555d92591", - "sha256:3d03883435a19794e41f147612a77a8f56d4e52822337844fff3d4040a142964", - "sha256:3ecc47cd7f6ea0336042be87d9e7da378e5c7e9b3c8ad0f7c966f714fc10d821", - "sha256:40f9e544c1c56ba8f1cf7686a8c9b5bb249e665d40d626a23899ba6d5d9e1484", - "sha256:4250888bcb96617e00bfa28ac24850a83c9f3a16db471eca2ee1f1714df0f957", - "sha256:4511d9e6071452b944207c8ce46ad2f897307910b402ea5fa975da32e0102800", - "sha256:45681fd7128c8ad1c379f0ca0776a8b0c6583d2f69889ddac01559dfe4390918", - "sha256:48fd472630715e1c1c89bf1feab55c29098cb403cc184b4859f9c86d4fcb6a95", - "sha256:4c86e2a209199ead7ee0af65e1d9992d1dce7e1f63c4b9a616500f93820658d0", - "sha256:4dfda918a13cc4f81e9118dea249e192ab167a0bb1966272d5503e39234d694e", - "sha256:5062dc1a4e32a10dc2b8b13cedd58988261416e811c1dc4dbdea4f57eea61b0d", - "sha256:51faf345324db860b515d3f364eaa93d0e0551a88d6218a7d61286554d190d73", - "sha256:526fc406ab991a340744aad7e25251dd47a6720a685fa3331e5c59fef5282a59", - "sha256:53c09385ff0b72ba79d8715683c1168c12e0b6e84fb0372e97553d1ea91efe51", - "sha256:55ba24ebe208344aa7a00e4482f65742969a039c2acfcb910bc6fcd776eb4355", - "sha256:5b6c390bfaef8c45a260554888966618328d30e72173697e5cabe6b285fb2348", - "sha256:5c5cc0cbabe9452038ed984d05ac87910f89370b9242371bd9079cb4af61811e", - "sha256:5edb4e4caf751c1518e6a26a83501fda79bff41cc59dac48d70e6d65d4ec4440", - "sha256:61048b4a49b1c93fe13426e04e04fdf5a03f456616f6e98c7576144677598675", - "sha256:676f4eebf6b2d430300f1f4f4c2461685f8269f94c89698d832cdf9277f30b84", - "sha256:67d4cda6fa6ffa073b08c8372aa5fa767ceb10c9a0587c707505a6d426f4e046", - "sha256:694f9e921a0c8f252980e85bce61ebbd07ed2b7d4fa72d0e4246f2f8aa6642ab", - "sha256:733585f9f4b62e9b3528dd1070ec4f52b8acf64215b60a845fa13ebd73cd0712", - "sha256:7671dc19c7019103ca44e8d94917eba8534c76133523ca8406822efdd19c9308", - "sha256:780077d95eafc2ccc3ced969db22377b3864e5b9a0ea5eb347cc93b3ea900315", - "sha256:7ba9cc93a91d86365a5d270dee221fdc04fb68d7478e6bf6af650de78a8339e3", - "sha256:89b16a18e7bba224ce5114db863e7029803c179979e1af6ad6a6b11f70545008", - "sha256:9036d6365d13b6cbe8f27a0eaf73ddcc070cae584e5ff94bb45e3e9d729feab5", - "sha256:93cf4e045bae74c90ca833cba583c14b62cb4ba2cba0abd2b141ab52548247e2", - "sha256:9ad014faa93dbb52c80d8f4d3dcf855865c876c9660cb9bd7553843dd03a4b1e", - "sha256:9b1d07b53b78bf84a96898c1bc139ad7f10fda7423f5fd158fd0f47ec5e01ac7", - "sha256:a7746f235c47abc72b102d3bce9977714c2444bdfaea7888d241b4c4bb6a78bf", - "sha256:aa3017c40d513ccac9621a2364f939d39e550c542eb2a894b4c8da92b38896ab", - "sha256:b34d87e8a3090ea626003f87f9392b3929a7bbf4104a05b6667348b6bd4bf1cd", - "sha256:b541032178a718c165a49638d28272b771053f628382d5e9d1c93df23ff58dbf", - "sha256:ba5511d8f31c033a5fcbda22dd5c813630af98c70b2661f2d2c654ae3cdfcfc8", - "sha256:bc8a37ad5b22c08e2dbd27df2b3ef7e5c0864235805b1e718a235bcb200cf1cb", - "sha256:bff7d8ec20f5f42607599f9994770fa65d76edca264a87b5e4ea5629bce12268", - "sha256:c1ad395cf254c4fbb5b2132fee391f361a6e8c1adbd28f2cd8e79308a615fe9d", - "sha256:f1d09e520217618e76396377c81fba6f290d5f926f50c35f3a5f72b01a0da780", - "sha256:f3eac17d9ec51be534685ba877b6ab5edc3ab7ec95c8f163e5d7b39859524716", - "sha256:f419290bc8968a46c4933158c91a0012b7a99bb2e465d5ef5293879742f8797e", - "sha256:f62aa6ee4eb43b024b0e5a01cf65a0bb078ef8c395e8713c6e8a12a697144528", - "sha256:f74e6fdeb9a265624ec3a3918430205dff1df7e95a230779746a6af78bc615af", - "sha256:f9b57eaa3b0cd8db52049ed0330747b0364e899e8a606a624813452b8203d5f7", - "sha256:fce4f615f8ca31b2e61aa0eb5865a21e14f5629515c9151850aa936c02a1ee51" + "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f", + "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0", + "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd", + "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2", + "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4", + "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648", + "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be", + "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb", + "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160", + "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd", + "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a", + "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84", + "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e", + "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748", + "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825", + "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60", + "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957", + "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715", + "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317", + "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e", + "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283", + "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278", + "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9", + "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de", + "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369", + "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb", + "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189", + "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014", + "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323", + "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e", + "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49", + "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50", + "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d", + "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37", + "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39", + "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576", + "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a", + "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba", + "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7", + "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826", + "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467", + "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495", + "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc", + "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391", + "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0", + "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97", + "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c", + "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac", + "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369", + "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8", + "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2", + "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff", + "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a", + "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df", + "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f" ], "markers": "python_version == '3.11'", - "version": "==2.2.1" + "version": "==2.2.2" }, "paho-mqtt": { "hashes": [ @@ -655,11 +655,11 @@ }, "pydantic": { "hashes": [ - "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d", - "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06" + "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff", + "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53" ], "index": "pypi", - "version": "==2.10.4" + "version": "==2.10.5" }, "pydantic-core": { "hashes": [ @@ -824,11 +824,11 @@ }, "tzdata": { "hashes": [ - "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", - "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd" + "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", + "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639" ], "markers": "python_version >= '2'", - "version": "==2024.2" + "version": "==2025.1" }, "urllib3": { "hashes": [ @@ -1266,6 +1266,14 @@ "index": "pypi", "version": "==2024.8.6" }, + "id": { + "hashes": [ + "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", + "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658" + ], + "markers": "python_version >= '3.8'", + "version": "==1.5.0" + }, "idna": { "hashes": [ "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", @@ -1284,11 +1292,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", - "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7" + "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", + "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580" ], "markers": "python_version < '3.12'", - "version": "==8.5.0" + "version": "==8.6.1" }, "iniconfig": { "hashes": [ @@ -1431,11 +1439,11 @@ }, "more-itertools": { "hashes": [ - "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", - "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6" + "sha256:2cd7fad1009c31cc9fb6a035108509e6547547a7a738374f10bd49a09eb3ee3b", + "sha256:6eb054cb4b6db1473f6e15fcc676a08e4732548acd47c708f0e179c2c7c01e89" ], - "markers": "python_version >= '3.8'", - "version": "==10.5.0" + "markers": "python_version >= '3.9'", + "version": "==10.6.0" }, "nh3": { "hashes": [ @@ -1475,14 +1483,6 @@ "markers": "python_version >= '3.8'", "version": "==24.2" }, - "pkginfo": { - "hashes": [ - "sha256:8ad91a0445a036782b9366ef8b8c2c50291f83a553478ba8580c73d3215700cf", - "sha256:dcd589c9be4da8973eceffa247733c144812759aa67eaf4bbf97016a02f39088" - ], - "markers": "python_version >= '3.8'", - "version": "==1.12.0" - }, "pluggy": { "hashes": [ "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", @@ -1501,11 +1501,11 @@ }, "pygments": { "hashes": [ - "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", - "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a" + "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", + "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c" ], "markers": "python_version >= '3.8'", - "version": "==2.18.0" + "version": "==2.19.1" }, "pyproject-hooks": { "hashes": [ @@ -1532,6 +1532,65 @@ "index": "pypi", "version": "==0.6" }, + "pyyaml": { + "hashes": [ + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" + ], + "index": "pypi", + "version": "==6.0.2" + }, "readme-renderer": { "hashes": [ "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", @@ -1590,11 +1649,11 @@ }, "setuptools": { "hashes": [ - "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", - "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d" + "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", + "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3" ], "index": "pypi", - "version": "==75.6.0" + "version": "==75.8.0" }, "snowballstemmer": { "hashes": [ @@ -1677,11 +1736,11 @@ }, "twine": { "hashes": [ - "sha256:36158b09df5406e1c9c1fb8edb24fc2be387709443e7376689b938531582ee27", - "sha256:9c6025b203b51521d53e200f4a08b116dee7500a38591668c6a6033117bdc218" + "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384", + "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd" ], "index": "pypi", - "version": "==6.0.1" + "version": "==6.1.0" }, "urllib3": { "hashes": [ diff --git a/lib/python/tests/conftest.py b/lib/python/tests/conftest.py deleted file mode 100644 index 5270a5bfba..0000000000 --- a/lib/python/tests/conftest.py +++ /dev/null @@ -1,37 +0,0 @@ -import logging -import uuid - -import pytest - -from dbrepo.RestClient import RestClient -from dbrepo.api.dto import Database - -logging.basicConfig(level=logging.DEBUG) - - -def pytest_configure(config): - TestKeyValue.username = str(uuid.uuid4()).replace("-", "")[0:10] - TestKeyValue.password = str(uuid.uuid4()).replace("-", "")[0:10] - - -@pytest.fixture(scope='session', name='rest_client') -def user_rest_client() -> RestClient: - TestKeyValue.user_id = RestClient().create_user(username=f'{TestKeyValue.username}', - password=f'{TestKeyValue.password}', - email=f'{TestKeyValue.username}@example.com').id - return RestClient(username=TestKeyValue.username, password=TestKeyValue.password) - - -@pytest.fixture(scope='session', name='database') -def database() -> Database: - name = str(uuid.uuid4()).replace("-", "")[0:10] - return RestClient(username=TestKeyValue.username, - password=TestKeyValue.password).create_database(name=name, container_id=1, is_public=True, - is_schema_public=True) - - -class TestKeyValue: - user_id: str = None - username: str = None - password: str = None - database_name: str = None diff --git a/lib/python/tests/test_dtos.py b/lib/python/tests/test_dtos.py new file mode 100644 index 0000000000..f338c30dcc --- /dev/null +++ b/lib/python/tests/test_dtos.py @@ -0,0 +1,44 @@ +import inspect +import sys +import unittest + +from yaml import safe_load + +from dbrepo.api import dto + + +class AnalyseUnitTest(unittest.TestCase): + schemas = None + models: [()] = [] + found: int = 0 + skipped: int = 0 + + def setUp(self): + with open('../../../.docs/.openapi/api.yaml', 'r') as f: + self.schemas = safe_load(f)['components']['schemas'] + for name, obj in inspect.getmembers(sys.modules[dto.__name__]): + if not inspect.isclass(obj): + self.found += 1 + continue + if f'{name}Dto' not in self.schemas: + self.skipped += 1 + continue + self.models.append((name, obj)) + + def build_model(self, name: str, obj: any, definition: any) -> dict: + model_dict = dict() + for property in definition['properties']: + if 'example' not in definition['properties'][property]: + if '$ref' not in definition['properties'][property]: + self.fail(f'OpenAPI model {name}Dto does not have example for property: {property}') + ref = definition['properties'][property]['$ref'][len('#/components/schemas/'):-3] + # recursive call + model_dict[property] = self.build_model(ref, obj, self.schemas[f'{name}Dto']) + model_dict[property] = definition['properties'][property]['example'] + model = obj(**model_dict) + + def test_dtos_succeeds(self): + for name, obj in self.models: + self.build_model(name, obj, self.schemas[f'{name}Dto']) + + pass diff --git a/lib/python/tests/test_system_database.py b/lib/python/tests/test_system_database.py deleted file mode 100644 index 902b95435b..0000000000 --- a/lib/python/tests/test_system_database.py +++ /dev/null @@ -1,34 +0,0 @@ -import unittest -import uuid - -import pytest - - -class UserUnitTest(unittest.TestCase): - - @pytest.fixture(autouse=True) - def prepare_fixture(self, rest_client, database): - self.rest_client = rest_client - self.database = database - - @pytest.mark.usefixtures("rest_client") - def test_create_database_succeeds(self): - name = str(uuid.uuid4()).replace("-", "")[0:10] - # test - response = self.rest_client.create_database(name=name, container_id=1, - is_public=True, is_schema_public=True) - self.assertEqual(True, response.is_public) - self.assertEqual(True, response.is_schema_public) - self.assertEqual(None, response.description) - - @pytest.mark.usefixtures("rest_client", "database") - def test_update_database_visibility_succeeds(self): - # test - response = self.rest_client.update_database_visibility(database_id=self.database.id, is_public=False, - is_schema_public=False) - self.assertEqual(False, response.is_public) - self.assertEqual(False, response.is_schema_public) - - -if __name__ == "__main__": - unittest.main() diff --git a/lib/python/tests/test_system_user.py b/lib/python/tests/test_system_user.py deleted file mode 100644 index 350b341102..0000000000 --- a/lib/python/tests/test_system_user.py +++ /dev/null @@ -1,42 +0,0 @@ -import unittest -import uuid - -import pytest - -from conftest import TestKeyValue -from dbrepo.RestClient import RestClient - - -class UserUnitTest(unittest.TestCase): - - @pytest.fixture(autouse=True) - def prepare_fixture(self, rest_client): - self.rest_client = rest_client - - def test_get_users_succeeds(self): - # test - response = RestClient().get_users() - - def test_create_user_succeeds(self): - username = str(uuid.uuid4()).replace("-", "")[0:10] - password = str(uuid.uuid4()).replace("-", "")[0:10] - # test - response = RestClient().create_user(username=f'{username}', password=f'{password}', - email=f'{username}@example.com') - self.assertEqual(username, response.username) - - @pytest.mark.usefixtures("rest_client") - def test_update_user_succeeds(self): - # test - response = self.rest_client.update_user(user_id=TestKeyValue.user_id, theme='dark', language='de', - firstname='Foo', lastname='Bar', affiliation='TU Wien', - orcid='https://orcid.org/0000-0003-4216-302X') - self.assertEqual('dark', response.attributes.theme) - self.assertEqual('Foo', response.given_name) - self.assertEqual('Bar', response.family_name) - self.assertEqual('TU Wien', response.attributes.affiliation) - self.assertEqual('https://orcid.org/0000-0003-4216-302X', response.attributes.orcid) - - -if __name__ == "__main__": - unittest.main() diff --git a/lib/python/tests/test_unit_analyse.py b/lib/python/tests/test_unit_analyse.py index a26d7aa844..5ca2b8301c 100644 --- a/lib/python/tests/test_unit_analyse.py +++ b/lib/python/tests/test_unit_analyse.py @@ -1,6 +1,5 @@ import unittest import requests_mock -import dataclasses from dbrepo.RestClient import RestClient diff --git a/lib/python/tests/test_unit_database.py b/lib/python/tests/test_unit_database.py index eeeea68832..7605809e61 100644 --- a/lib/python/tests/test_unit_database.py +++ b/lib/python/tests/test_unit_database.py @@ -24,11 +24,23 @@ class DatabaseUnitTest(unittest.TestCase): DatabaseBrief( id=1, name='test', - owner_id='8638c043-5145-4be8-a3e4-4b79991b0a16', + owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'), contact=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'), internal_name='test_abcd', is_public=True, - is_schema_public=True) + is_schema_public=True, + container=ContainerBrief( + id=1, + name='MariaDB Galera 11.1.3', + internal_name='mariadb', + image=ImageBrief( + id=1, + name='mariadb', + version='11.2.2', + jdbc_method='mariadb' + ) + ) + ) ] with requests_mock.Mocker() as mock: # mock diff --git a/make/gen.mk b/make/gen.mk index ed10c7e123..308a307b95 100644 --- a/make/gen.mk +++ b/make/gen.mk @@ -1,11 +1,11 @@ ##@ Generate -.PHONY: gen-swagger-doc -gen-swagger-doc: build-images ## Generate Swagger documentation and fetch. +.PHONY: gen-openapi-doc +gen-openapi-doc: build-images ## Generate Swagger documentation and fetch. docker compose up -d - bash .docs/.swagger/swagger-generate.sh + bash .docs/.openapi/openapi-generate.sh docker compose down - openapi-merge-cli --config .docs/.swagger/openapi-merge.json + openapi-merge-cli --config .docs/.openapi/openapi-merge.json .PHONY: gen-helm-doc gen-helm-doc: build-helm ## Generate Helm documentation and schema diff --git a/yq b/yq new file mode 100644 index 0000000000..5578822fb4 --- /dev/null +++ b/yq @@ -0,0 +1,7043 @@ +openapi: 3.0.1 +info: + title: Database Repository Metadata Service API + description: Service that manages the metadata + contact: + name: Prof. Andreas Rauber + email: andreas.rauber@tuwien.ac.at + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + version: 1.6.2 +externalDocs: + description: Sourcecode Documentation + url: https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.6.2/system-services-metadata/ +servers: +- url: http://localhost + description: Development instance +- url: https://test.dbrepo.tuwien.ac.at + description: Staging instance +paths: + /api/database: + get: + tags: + - database-endpoint + summary: List databases + description: "Lists all databases in the metadata database. Requests with HTTP\ + \ method **GET** return the list of databases, requests with HTTP method **HEAD**\ + \ only the number in the `X-Count` header." + operationId: list + parameters: + - name: internal_name + in: query + required: false + schema: + type: string + responses: + "200": + description: List of databases + headers: + Access-Control-Expose-Headers: + description: Expose `X-Count` custom header + required: true + style: simple + X-Count: + description: Number of databases + required: true + style: simple + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/DatabaseBriefDto" + post: + tags: + - database-endpoint + summary: Create database + description: Creates a database in the container with id. Requires roles `create-database`. + operationId: create_5 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseCreateDto" + required: true + responses: + "409": + description: Query store could not be created + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Database create query is malformed or image is not supported + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "423": + description: Database quota exceeded + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Database create permission is missing or grant permissions + at broker service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to fin container/user/database in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created a new database + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + security: + - bearerAuth: [] + - basicAuth: [] + head: + tags: + - database-endpoint + summary: List databases + description: "Lists all databases in the metadata database. Requests with HTTP\ + \ method **GET** return the list of databases, requests with HTTP method **HEAD**\ + \ only the number in the `X-Count` header." + operationId: list_1 + parameters: + - name: internal_name + in: query + required: false + schema: + type: string + responses: + "200": + description: List of databases + headers: + Access-Control-Expose-Headers: + description: Expose `X-Count` custom header + required: true + style: simple + X-Count: + description: Number of databases + required: true + style: simple + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/DatabaseBriefDto" + /api/database/{databaseId}/access/{userId}: + get: + tags: + - access-endpoint + summary: Find/Check access + description: "Finds or checks access of a user with given id to a database with\ + \ given id. Requests with HTTP method **GET** return the access object, requests\ + \ with HTTP method **HEAD** only the status. When the user has at least *READ*\ + \ access, the status 200 is returned, 403 otherwise. Requires role `check-database-access`\ + \ or `check-foreign-database-access`." + operationId: find + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: userId + in: path + required: true + schema: + type: string + format: uuid + responses: + "403": + description: No access to this database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found database access + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseAccessDto" + security: + - bearerAuth: [] + - basicAuth: [] + put: + tags: + - access-endpoint + summary: Modify access + description: Modifies access of a user with given id to database with given + id. Requires role `update-database-access`. + operationId: update_5 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: userId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateDatabaseAccessDto" + required: true + responses: + "404": + description: Database or user not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Modify access not permitted when no access is granted in the + first place + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Access could not be updated in the data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Modified access + "502": + description: Access could not be updated due to connection error in the + data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Modify access query or database connection is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + post: + tags: + - access-endpoint + summary: Give access + description: Give a user with given id access to some database with given id. + Requires role `create-database-access`. + operationId: create_8 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: userId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateDatabaseAccessDto" + required: true + responses: + "404": + description: Database or user not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Access could not be created in the data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Failed giving access + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Granting access succeeded + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseAccessDto" + "400": + description: Granting access query or database connection is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Access could not be created due to connection error + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - access-endpoint + summary: Delete access + description: Delete access of a user with id to a database with id. Requires + role `delete-database-access`. + operationId: revoke + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: userId + in: path + required: true + schema: + type: string + format: uuid + responses: + "403": + description: Revoke of access not permitted as no access was found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted access + "502": + description: Access could not be created due to connection error + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "User, database with access was not found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Access could not be revoked in the data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Modify access query or database connection is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + head: + tags: + - access-endpoint + summary: Find/Check access + description: "Finds or checks access of a user with given id to a database with\ + \ given id. Requests with HTTP method **GET** return the access object, requests\ + \ with HTTP method **HEAD** only the status. When the user has at least *READ*\ + \ access, the status 200 is returned, 403 otherwise. Requires role `check-database-access`\ + \ or `check-foreign-database-access`." + operationId: find_1 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: userId + in: path + required: true + schema: + type: string + format: uuid + responses: + "403": + description: No access to this database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found database access + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseAccessDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/user/{userId}: + get: + tags: + - user-endpoint + summary: Get user + description: Gets own user information from the metadata database. Requires + authentication. Foreign user information can only be obtained if additional + role `find-foreign-user` is present. Finding information about internal users + results in a 404 error. + operationId: find_2 + parameters: + - name: userId + in: path + required: true + schema: + type: string + format: uuid + responses: + "404": + description: User was not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found user + content: + application/json: + schema: + $ref: "#/components/schemas/UserDto" + "403": + description: Find user is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + put: + tags: + - user-endpoint + summary: Update user + description: Updates user with id. Requires role `modify-user-information`. + operationId: modify + parameters: + - name: userId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UserUpdateDto" + required: true + responses: + "202": + description: Modified user information + content: + application/json: + schema: + $ref: "#/components/schemas/UserDto" + "404": + description: Failed to find database/user in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Modify user query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to modify user metadata + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/user/{userId}/password: + put: + tags: + - user-endpoint + summary: Update user password + description: Updates password of user with id. Requires authentication. + operationId: password + parameters: + - name: userId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UserPasswordDto" + required: true + responses: + "400": + description: Invalid password payload + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Modified user password + "403": + description: Not allowed to change foreign user password + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to get user in auth service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database/user in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to auth service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/user/token: + put: + tags: + - user-endpoint + summary: Refresh token + description: Refreshes user token by refresh token. + operationId: refreshToken + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RefreshTokenRequestDto" + required: true + responses: + "202": + description: Refreshed user token + content: + application/json: + schema: + $ref: "#/components/schemas/TokenDto" + "403": + description: Not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Invalid refresh token + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to auth service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + post: + tags: + - user-endpoint + summary: Create token + description: Creates a user token via the Auth Service. + operationId: getToken + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/LoginRequestDto" + required: true + responses: + "400": + description: Invalid login request + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to get token + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find user in auth database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to get user in auth service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Obtained user token + content: + application/json: + schema: + $ref: "#/components/schemas/TokenDto" + "428": + description: Account is not fully setup in auth service (requires password + change?) + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to auth service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + /api/ontology/{ontologyId}: + get: + tags: + - ontology-endpoint + summary: Find ontology + description: Finds an ontology with id in the metadata database. + operationId: find_3 + parameters: + - name: ontologyId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Find one ontology + content: + application/json: + schema: + $ref: "#/components/schemas/OntologyDto" + put: + tags: + - ontology-endpoint + summary: Update ontology + description: Updates an ontology with id. Requires role `update-ontology`. + operationId: update + parameters: + - name: ontologyId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/OntologyModifyDto" + required: true + responses: + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated ontology successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OntologyDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - ontology-endpoint + summary: Delete ontology + description: Deletes an ontology with given id. Requires role `delete-ontology`. + operationId: delete + parameters: + - name: ontologyId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "202": + description: Deleted ontology successfully + content: + application/json: {} + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/message/{messageId}: + put: + tags: + - message-endpoint + summary: Update message + description: Updates a message with id. Requires role `update-maintenance-message`. + operationId: update_1 + parameters: + - name: messageId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/BannerMessageUpdateDto" + required: true + responses: + "202": + description: Updated message + content: + application/json: + schema: + $ref: "#/components/schemas/BannerMessageBriefDto" + "404": + description: Could not find message + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - message-endpoint + summary: Delete message + description: Deletes a message with id. Requires role `delete-maintenance-message`. + operationId: delete_1 + parameters: + - name: messageId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "404": + description: Could not find message + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted message + content: + application/json: {} + security: + - bearerAuth: [] + - basicAuth: [] + /api/image/{imageId}: + get: + tags: + - image-endpoint + summary: Find image + description: Finds a container image in the metadata database. + operationId: findById + parameters: + - name: imageId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "404": + description: Image could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found image + content: + application/json: + schema: + $ref: "#/components/schemas/ImageDto" + put: + tags: + - image-endpoint + summary: Update image + description: Updates container image in the metadata database. Requires role + `modify-image`. + operationId: update_2 + parameters: + - name: imageId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ImageChangeDto" + required: true + responses: + "404": + description: Image could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated image successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ImageDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - image-endpoint + summary: Delete image + description: Deletes a container image in the metadata database. Requires role + `delete-image`. + operationId: delete_2 + parameters: + - name: imageId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "202": + description: Deleted image successfully + "404": + description: Image could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/identifier/{identifierId}: + get: + tags: + - identifier-endpoint + summary: Find identifier + description: Finds an identifier with id. The response format depends on the + HTTP `Accept` header set on the request. + operationId: find_6 + parameters: + - name: identifierId + in: path + required: true + schema: + type: integer + format: int64 + - name: Accept + in: header + required: true + schema: + type: string + responses: + "200": + description: Found identifier successfully + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + application/ld+json: + schema: + $ref: "#/components/schemas/LdDatasetDto" + text/csv: {} + text/xml: {} + text/bibliography: {} + text/bibliography; style=apa: {} + text/bibliography; style=ieee: {} + text/bibliography; style=bibtex: {} + "502": + description: Connection to data service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: "Identifier could not be exported, the requested style is not\ + \ known" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "409": + description: Exported resource was not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "410": + description: Failed to retrieve from S3 endpoint + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Identifier could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "406": + description: Failed to find acceptable representation + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to find in data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + put: + tags: + - identifier-endpoint + summary: Save identifier + description: Saves an identifier with id as a draft identifier. Identifiers + can only be created for objects the user has at least *READ* access in the + associated database (requires role `create-identifier`) or for any object + in any database (requires role `create-foreign-identifier`). + operationId: save + parameters: + - name: identifierId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierSaveDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Failed to find database, table or view" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Identifier form contains invalid request data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Saved identifier + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + "403": + description: Insufficient access rights or authorities + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - identifier-endpoint + summary: Delete identifier + description: Deletes an identifier with id. Requires role `delete-identifier`. + operationId: delete_3 + parameters: + - name: identifierId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to delete in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Identifier or database could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted identifier + "403": + description: Deleting identifier not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/identifier/{identifierId}/publish: + put: + tags: + - identifier-endpoint + summary: Publish identifier + description: Publishes an identifier with id. A published identifier cannot + be changed anymore. Requires role `publish-identifier`. + operationId: publish + parameters: + - name: identifierId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Failed to find database, table or view" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Published identifier + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + "400": + description: Identifier form contains invalid request data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Insufficient access rights or authorities + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/visibility: + put: + tags: + - database-endpoint + summary: Update database visibility + description: Updates the database with id on the visibility. Only the database + owner can perform this operation. Requires role `modify-database-visibility`. + operationId: visibility + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseModifyVisibilityDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Visibility modified successfully + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "400": + description: The visibility payload is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Visibility modification is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/view/{viewId}: + get: + tags: + - view-endpoint + summary: Get view + description: Gets a view with id in the metadata database. + operationId: find_7 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: viewId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "403": + description: Find view is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, view or user could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Find view successfully + headers: + X-Username: + description: The authentication username + style: simple + Access-Control-Expose-Headers: + description: Expose custom headers + style: simple + X-Type: + description: The JDBC connection type + style: simple + X-View: + description: The view internal name + style: simple + X-Database: + description: The database internal name + style: simple + X-Password: + description: The authentication password + style: simple + X-Host: + description: The database hostname + style: simple + X-Port: + description: The database port number + style: simple + content: + application/json: + schema: + $ref: "#/components/schemas/ViewDto" + security: + - bearerAuth: [] + - basicAuth: [] + put: + tags: + - view-endpoint + summary: Update view + description: Updates a view with id. This can only be performed by the view + owner or database owner. Requires role `create-database-view`. + operationId: update_3 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: viewId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ViewUpdateDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database or View could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Update not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Update view query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Update view successfully + content: + '*/*': + schema: + $ref: "#/components/schemas/ViewDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - view-endpoint + summary: Delete view + description: Deletes a view with id. Requires role `delete-database-view`. + operationId: delete_4 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: viewId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "423": + description: Delete view resulted in an invalid query statement + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, view or user could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Delete view successfully + content: + '*/*': + schema: + type: object + "400": + description: Delete view query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Deletion not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}: + get: + tags: + - table-endpoint + summary: Find table + description: "Finds a table with id. When a table is hidden (i.e. when `is_public`\ + \ is `false`), then the user needs to have at least read access and the role\ + \ `find-table`. When the `system` role is present, the endpoint responds with\ + \ additional connection metadata in the header." + operationId: findById_2 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: Find table successfully + headers: + X-Username: + description: The authentication username + style: simple + X-Table: + description: The table internal name + style: simple + Access-Control-Expose-Headers: + description: Expose custom headers + style: simple + X-Type: + description: The JDBC connection type + style: simple + X-Database: + description: The database internal name + style: simple + X-Password: + description: The authentication password + style: simple + X-Host: + description: The database hostname + style: simple + X-Port: + description: The database port number + style: simple + content: + application/json: + schema: + $ref: "#/components/schemas/TableDto" + "503": + description: Failed to obtain queue information from broker service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Access to the database is forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Failed to establish connection with broker service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Table, database or container could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + put: + tags: + - table-endpoint + summary: Update table + description: Updates a table in the database with id. Requires role `update-table`. + operationId: update_4 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TableUpdateDto" + required: true + responses: + "403": + description: Update table visibility not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Table could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated the table + content: + application/json: + schema: + $ref: "#/components/schemas/TableBriefDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Update table visibility payload is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + delete: + tags: + - table-endpoint + summary: Delete table + description: Deletes a table with id. Only the owner of a table can perform + this action (requires role `delete-table`) or anyone can delete a table (requires + role `delete-foreign-table`). + operationId: delete_5 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Access to the database is forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Delete table successfully + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Delete table query resulted in an invalid query statement + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Table, database or container could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}/statistic: + put: + tags: + - table-endpoint + summary: Update statistics + description: "Updates basic statistical properties (min, max, mean, median,\ + \ std.dev) for numerical columns in a table with id. This action can only\ + \ be performed by the table owner. Requires role `update-table-statistic`." + operationId: updateStatistic + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Failed to map column statistic to known columns + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database/table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not the owner + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated table statistics successfully + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}/column/{columnId}: + put: + tags: + - table-endpoint + summary: Update semantics + description: Updates column semantics of a table column with id. Only the table + owner with at least *READ* access to the associated database can update the + column semantics (requires role `modify-table-column-semantics`) or foreign + table columns if role `modify-foreign-table-column-semantics`. + operationId: updateColumn + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: columnId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ColumnSemanticsUpdateDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Access to the database is forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find user/table/database/ontology in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated column semantics successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ColumnDto" + "400": + description: Update semantic concept query is malformed or update unit of + measurement query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/owner: + put: + tags: + - database-endpoint + summary: Update database owner + description: Updates the database with id on the owner. Only the database owner + can perform this operation. Requires role `modify-database-owner`. + operationId: transfer + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseTransferDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Transfer of ownership was successful + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "403": + description: Transfer of ownership is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Owner payload is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/metadata/view: + put: + tags: + - database-endpoint + summary: Update database view schemas + description: Updates the database with id with generated metadata from view + that are not yet known to the database. Only the database owner can perform + this operation. Requires role `find-database`. + operationId: refreshViewMetadata + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Refreshed database views metadata + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "403": + description: Refresh view metadata is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/metadata/table: + put: + tags: + - database-endpoint + summary: Update database table schemas + description: Updates the database with id with generated metadata from tables + that are not yet known to the database. Only the database owner can perform + this operation. Requires role `find-database`. + operationId: refreshTableMetadata + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "403": + description: Not allowed to refresh table metadata + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to fin user/database in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Refreshed database tables metadata + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "400": + description: Failed to parse payload at search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/image: + get: + tags: + - database-endpoint + summary: Get database preview image + description: Gets the database with id on the preview image. + operationId: findPreviewImage + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: View of image was successful + content: + '*/*': + schema: + type: array + items: + type: string + format: byte + security: + - bearerAuth: [] + - basicAuth: [] + put: + tags: + - database-endpoint + summary: Update database preview image + description: Updates the database with id on the preview image. Only the database + owner can perform this operation. Requires role `modify-database-image`. + operationId: modifyImage + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseModifyImageDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Modify of image was successful + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "410": + description: File was not found in the Storage Service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Modify of image is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/user: + get: + tags: + - user-endpoint + summary: List users + description: "Lists users known to the metadata database. Internal users are\ + \ omitted from the result list. If the optional query parameter `username`\ + \ is present, the result list can be filtered by matching this exact username." + operationId: findAll + parameters: + - name: username + in: query + required: false + schema: + type: string + responses: + "200": + description: List users + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/UserBriefDto" + post: + tags: + - user-endpoint + summary: Create user + description: Creates a user in the auth service and metadata database. Requires + that no credentials are sent in the request. + operationId: create + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/SignupRequestDto" + required: true + responses: + "403": + description: Internal authentication to the auth service is invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Parameters are not well-formed (likely email) + content: + application/json: {} + "409": + description: User with username already exists + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "417": + description: User with e-mail already exists + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to create in auth service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created user + content: + application/json: + schema: + $ref: "#/components/schemas/UserDto" + "502": + description: Failed to create in auth service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Default role not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + /api/ontology: + get: + tags: + - ontology-endpoint + summary: List ontologies + description: Lists all ontologies known to the metadata database. + operationId: findAll_2 + responses: + "200": + description: List ontologies + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/OntologyBriefDto" + post: + tags: + - ontology-endpoint + summary: Create ontology + description: Creates an ontology in the metadata database. Requires role `create-ontology`. + operationId: create_1 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/OntologyCreateDto" + required: true + responses: + "201": + description: Registered ontology successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OntologyDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/message: + get: + tags: + - message-endpoint + summary: List messages + description: "Lists messages known to the metadata database. Messages can be\ + \ filtered be filtered with the optional `active` parameter. If set to *true*,\ + \ only active messages (that is, messages whose end time has not been reached)\ + \ will be returned. Otherwise only inactive messages are returned. If not\ + \ set, active and inactive messages are returned." + operationId: list_2 + parameters: + - name: active + in: query + required: false + schema: + type: boolean + responses: + "200": + description: List messages + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/BannerMessageDto" + post: + tags: + - message-endpoint + summary: Create message + description: Creates a message in the metadata database. Requires role `create-maintenance-message`. + operationId: create_2 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/BannerMessageCreateDto" + required: true + responses: + "201": + description: Created message + content: + application/json: + schema: + $ref: "#/components/schemas/BannerMessageBriefDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/image: + get: + tags: + - image-endpoint + summary: List images + description: Lists all container images known to the metadata database. + operationId: findAll_3 + responses: + "200": + description: List images + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ImageBriefDto" + post: + tags: + - image-endpoint + summary: Create image + description: Creates a container image in the metadata database. Requires role + `create-image`. + operationId: create_3 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ImageCreateDto" + required: true + responses: + "409": + description: Image already exists + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Image specification is invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created image + content: + application/json: + schema: + $ref: "#/components/schemas/ImageDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/identifier: + get: + tags: + - identifier-endpoint + summary: List identifiers + description: Lists all identifiers known to the metadata database + operationId: findAll_4 + parameters: + - name: dbid + in: query + required: false + schema: + type: integer + format: int64 + - name: qid + in: query + required: false + schema: + type: integer + format: int64 + - name: vid + in: query + required: false + schema: + type: integer + format: int64 + - name: tid + in: query + required: false + schema: + type: integer + format: int64 + - name: Accept + in: header + required: true + schema: + type: string + responses: + "200": + description: Found identifiers successfully + content: + application/json: + schema: + type: array + items: + type: string + application/ld+json: + schema: + type: array + items: + $ref: "#/components/schemas/LdDatasetDto" + "406": + description: "Identifier could not be exported, the requested style is not\ + \ known" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + post: + tags: + - identifier-endpoint + summary: Create identifier + description: Create an identifier with id to create a draft identifier. Identifiers + can only be created for objects the user has at least *READ* access in the + associated database (requires role `create-identifier`) or for any object + in any database (requires role `create-foreign-identifier`). + operationId: create_4 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierCreateDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Drafted identifier + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + "404": + description: "Failed to find database, table or view" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Identifier form contains invalid request data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Insufficient access rights or authorities + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/view: + get: + tags: + - view-endpoint + summary: List views + description: Lists views known to the metadata database. + operationId: findAll_5 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: Find views successfully + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ViewBriefDto" + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + post: + tags: + - view-endpoint + summary: Create view + description: Creates a view. This can only be performed by the database owner. + Requires role `create-database-view`. + operationId: create_6 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ViewCreateDto" + required: true + responses: + "423": + description: Create view resulted in an invalid query statement + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Create view successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ViewBriefDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Credentials missing + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database/user in metadata database. + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Create view query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table: + get: + tags: + - table-endpoint + summary: List tables + description: "Lists all tables known to the metadata database. When a database\ + \ has a hidden schema (i.e. when `is_schema_public` is `false`), then the\ + \ user needs to have at least read access and the role `list-tables`." + operationId: list_4 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "403": + description: List tables not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: List tables + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TableBriefDto" + security: + - bearerAuth: [] + - basicAuth: [] + post: + tags: + - table-endpoint + summary: Create table + description: Creates a table in the database with id. Requires role `create-table`. + operationId: create_7 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TableCreateDto" + required: true + responses: + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Create table not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created a new table + content: + application/json: + schema: + $ref: "#/components/schemas/TableBriefDto" + "409": + description: Create table conflicts with existing table name + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, container or user could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Create table query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/container: + get: + tags: + - container-endpoint + summary: List containers + description: List all containers in the metadata database. + operationId: findAll_6 + parameters: + - name: limit + in: query + required: false + schema: + type: integer + format: int32 + responses: + "200": + description: List containers + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ContainerBriefDto" + post: + tags: + - container-endpoint + summary: Create container + description: Creates a container in the metadata database. Requires role `create-container`. + operationId: create_9 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ContainerCreateDto" + required: true + responses: + "400": + description: Container payload malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Container image or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "409": + description: Container name already exists + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "201": + description: Created a new container + content: + application/json: + schema: + $ref: "#/components/schemas/ContainerDto" + "403": + description: "Create container not permitted, need authority `create-container`" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/unit: + get: + tags: + - unit-endpoint + summary: List units + description: Lists units known to the metadata database. + operationId: findAll_1 + responses: + "200": + description: Find all semantic units + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/UnitDto" + /api/ontology/{ontologyId}/entity: + get: + tags: + - ontology-endpoint + summary: Find entities + description: Finds semantic entities by label or uri in an ontology with id. + Requires role `execute-semantic-query`. + operationId: find_4 + parameters: + - name: ontologyId + in: path + required: true + schema: + type: integer + format: int64 + - name: label + in: query + required: false + schema: + type: string + - name: uri + in: query + required: false + schema: + type: string + responses: + "400": + description: Filter params are invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found entities + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/EntityDto" + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "422": + description: Ontology does not have rdf or sparql endpoint + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "417": + description: Generated query or uri is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/oai: + get: + tags: + - metadata-endpoint + summary: Get record + operationId: identify_1_1_1_1 + parameters: + - name: verb + in: query + - name: parameters + in: query + required: true + schema: + $ref: "#/components/schemas/OaiListIdentifiersParameters" + responses: + "200": + description: List containers + content: + text/xml: {} + /api/message/message/{messageId}: + get: + tags: + - message-endpoint + summary: Find message + description: Finds a message with id in the metadata database. + operationId: find_5 + parameters: + - name: messageId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: Get messages + content: + application/json: + schema: + $ref: "#/components/schemas/BannerMessageDto" + "404": + description: Could not find message + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + /api/license: + get: + tags: + - license-endpoint + summary: List licenses + description: Lists licenses known to the metadata database. + operationId: list_3 + responses: + "200": + description: List of licenses + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/LicenseDto" + /api/identifier/retrieve: + get: + tags: + - identifier-endpoint + summary: Retrieve PID metadata + description: "Retrieves Persistent Identifier (PID) metadata from external endpoints.\ + \ Supported PIDs are: ORCID, ROR, DOI." + operationId: retrieve + parameters: + - name: url + in: query + required: true + schema: + type: string + responses: + "200": + description: Retrieved metadata from identifier + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + "404": + description: Failed to find metadata for identifier + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + /api/database/{databaseId}: + get: + tags: + - database-endpoint + summary: Find database + description: Finds a database with id. + operationId: findById_1 + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "503": + description: Failed to find queue information in broker service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to the broker service could not be established + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Database found successfully + headers: + X-Username: + description: The authentication username + style: simple + Access-Control-Expose-Headers: + description: Expose custom headers + style: simple + X-Password: + description: The authentication password + style: simple + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "403": + description: Not allowed to view database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, user or exchange could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}/suggest: + get: + tags: + - table-endpoint + summary: Suggest semantics + description: Suggests semantic concepts for a table. This action can only be + performed by the table owner. Requires role `table-semantic-analyse`. + operationId: analyseTable + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "417": + description: Generated query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "422": + description: Ontology does not have rdf or sparql endpoint + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Failed to parse statistic in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not the table owner. + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database/table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Suggested table semantics successfully + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/EntityDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/database/{databaseId}/table/{tableId}/column/{columnId}/suggest: + get: + tags: + - table-endpoint + summary: Suggest semantics + description: Suggests column semantics. Requires role `table-semantic-analyse`. + operationId: analyseTableColumn + parameters: + - name: databaseId + in: path + required: true + schema: + type: integer + format: int64 + - name: tableId + in: path + required: true + schema: + type: integer + format: int64 + - name: columnId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "400": + description: Generated query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "422": + description: Ontology does not have rdf or sparql endpoint + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Suggested table column semantics successfully + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TableColumnEntityDto" + "404": + description: Failed to find database/table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/container/{containerId}: + get: + tags: + - container-endpoint + summary: Find container + description: Finds a container in the metadata database. + operationId: findById_3 + parameters: + - name: containerId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "200": + description: Found container + content: + application/json: + schema: + $ref: "#/components/schemas/ContainerDto" + "404": + description: Container image could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + delete: + tags: + - container-endpoint + summary: Delete container + description: Deletes a container in the metadata database. Requires role `delete-container`. + operationId: delete_6 + parameters: + - name: containerId + in: path + required: true + schema: + type: integer + format: int64 + responses: + "404": + description: Container not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted container + "403": + description: "Create container not permitted, need authority `delete-container`" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + security: + - bearerAuth: [] + - basicAuth: [] + /api/concept: + get: + tags: + - concept-endpoint + summary: List concepts + description: List all semantic concepts known to the metadata database + operationId: findAll_7 + responses: + "200": + description: List concepts + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ConceptDto" +components: + schemas: + DatabaseBriefDto: + required: + - contact + - id + - internal_name + - is_public + - is_schema_public + - name + - owner_id + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + description: + type: string + example: Air Quality + identifiers: + type: array + items: + $ref: "#/components/schemas/IdentifierBriefDto" + contact: + $ref: "#/components/schemas/UserBriefDto" + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + owner_id: + type: string + format: uuid + preview_image: + type: string + IdentifierBriefDto: + required: + - created_by + - database_id + - id + - publication_year + - publisher + - titles + - type + type: object + properties: + id: + type: integer + format: int64 + type: + type: string + enum: + - database + - subset + - table + - view + titles: + type: array + items: + $ref: "#/components/schemas/IdentifierTitleDto" + doi: + type: string + example: 10.1038/nphys1170 + publisher: + type: string + example: TU Wien + status: + type: string + enum: + - draft + - published + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + example: 1 + table_id: + type: integer + format: int64 + example: 1 + view_id: + type: integer + format: int64 + example: 1 + publication_year: + type: integer + format: int32 + example: 2022 + created_by: + type: string + format: uuid + IdentifierTitleDto: + required: + - id + type: object + properties: + id: + type: integer + format: int64 + title: + type: string + example: Airquality Demonstrator + language: + type: string + example: en + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + type: + type: string + enum: + - AlternativeTitle + - Subtitle + - TranslatedTitle + - Other + UserBriefDto: + required: + - id + - username + type: object + properties: + id: + type: string + format: uuid + example: 1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4 + username: + type: string + description: Only contains lowercase characters + example: jcarberry + name: + type: string + example: Josiah Carberry + orcid: + type: string + example: 0000-0002-1825-0097 + qualified_name: + type: string + example: Josiah Carberry — @jcarberry + given_name: + type: string + example: Josiah + family_name: + type: string + example: Carberry + ApiErrorDto: + required: + - code + - message + - status + type: object + properties: + status: + type: string + example: NOT_FOUND + enum: + - 100 CONTINUE + - 101 SWITCHING_PROTOCOLS + - 102 PROCESSING + - 103 EARLY_HINTS + - 103 CHECKPOINT + - 200 OK + - 201 CREATED + - 202 ACCEPTED + - 203 NON_AUTHORITATIVE_INFORMATION + - 204 NO_CONTENT + - 205 RESET_CONTENT + - 206 PARTIAL_CONTENT + - 207 MULTI_STATUS + - 208 ALREADY_REPORTED + - 226 IM_USED + - 300 MULTIPLE_CHOICES + - 301 MOVED_PERMANENTLY + - 302 FOUND + - 302 MOVED_TEMPORARILY + - 303 SEE_OTHER + - 304 NOT_MODIFIED + - 305 USE_PROXY + - 307 TEMPORARY_REDIRECT + - 308 PERMANENT_REDIRECT + - 400 BAD_REQUEST + - 401 UNAUTHORIZED + - 402 PAYMENT_REQUIRED + - 403 FORBIDDEN + - 404 NOT_FOUND + - 405 METHOD_NOT_ALLOWED + - 406 NOT_ACCEPTABLE + - 407 PROXY_AUTHENTICATION_REQUIRED + - 408 REQUEST_TIMEOUT + - 409 CONFLICT + - 410 GONE + - 411 LENGTH_REQUIRED + - 412 PRECONDITION_FAILED + - 413 PAYLOAD_TOO_LARGE + - 413 REQUEST_ENTITY_TOO_LARGE + - 414 URI_TOO_LONG + - 414 REQUEST_URI_TOO_LONG + - 415 UNSUPPORTED_MEDIA_TYPE + - 416 REQUESTED_RANGE_NOT_SATISFIABLE + - 417 EXPECTATION_FAILED + - 418 I_AM_A_TEAPOT + - 419 INSUFFICIENT_SPACE_ON_RESOURCE + - 420 METHOD_FAILURE + - 421 DESTINATION_LOCKED + - 422 UNPROCESSABLE_ENTITY + - 423 LOCKED + - 424 FAILED_DEPENDENCY + - 425 TOO_EARLY + - 426 UPGRADE_REQUIRED + - 428 PRECONDITION_REQUIRED + - 429 TOO_MANY_REQUESTS + - 431 REQUEST_HEADER_FIELDS_TOO_LARGE + - 451 UNAVAILABLE_FOR_LEGAL_REASONS + - 500 INTERNAL_SERVER_ERROR + - 501 NOT_IMPLEMENTED + - 502 BAD_GATEWAY + - 503 SERVICE_UNAVAILABLE + - 504 GATEWAY_TIMEOUT + - 505 HTTP_VERSION_NOT_SUPPORTED + - 506 VARIANT_ALSO_NEGOTIATES + - 507 INSUFFICIENT_STORAGE + - 508 LOOP_DETECTED + - 509 BANDWIDTH_LIMIT_EXCEEDED + - 510 NOT_EXTENDED + - 511 NETWORK_AUTHENTICATION_REQUIRED + message: + type: string + example: Error message + code: + type: string + example: error.service.code + DatabaseAccessDto: + required: + - type + - user + type: object + properties: + user: + $ref: "#/components/schemas/UserBriefDto" + type: + type: string + enum: + - read + - write_own + - write_all + UserUpdateDto: + required: + - language + - theme + type: object + properties: + firstname: + type: string + example: Josiah + lastname: + type: string + example: Carberry + affiliation: + type: string + example: Brown University + orcid: + type: string + example: 0000-0002-1825-0097 + theme: + type: string + example: dark + language: + type: string + example: en + UserAttributesDto: + required: + - language + - theme + type: object + properties: + theme: + type: string + example: light + orcid: + type: string + example: https://orcid.org/0000-0002-1825-0097 + affiliation: + type: string + example: Brown University + language: + type: string + example: en + UserDto: + required: + - attributes + - id + type: object + properties: + id: + type: string + format: uuid + example: 1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4 + name: + type: string + example: Josiah Carberry + attributes: + $ref: "#/components/schemas/UserAttributesDto" + last_retrieved: + type: string + format: date-time + qualified_name: + type: string + example: Josiah Carberry — @jcarberry + given_name: + type: string + example: Josiah + family_name: + type: string + example: Carberry + UserPasswordDto: + required: + - password + type: object + properties: + password: + type: string + RefreshTokenRequestDto: + required: + - refresh_token + type: object + properties: + refresh_token: + type: string + example: refresh_token + TokenDto: + required: + - access_token + - expires_in + - id_token + - not-before-policy + - refresh_expires_in + - refresh_token + - scope + - session_state + - token_type + type: object + properties: + scope: + type: string + access_token: + type: string + expires_in: + type: integer + format: int64 + refresh_token: + type: string + refresh_expires_in: + type: integer + format: int64 + id_token: + type: string + session_state: + type: string + token_type: + type: string + not-before-policy: + type: integer + format: int64 + OntologyModifyDto: + required: + - prefix + - uri + type: object + properties: + uri: + type: string + example: Ontology URI + prefix: + type: string + example: Ontology prefix + sparql_endpoint: + type: string + example: Ontology SPARQL endpoint + rdf_path: + type: string + example: rdf/om-2.0.rdf + OntologyDto: + required: + - id + - prefix + - rdf + - sparql + - uri + type: object + properties: + id: + type: integer + format: int64 + uri: + type: string + example: http://www.wikidata.org/ + prefix: + type: string + example: wd + sparql: + type: boolean + example: true + rdf: + type: boolean + example: false + uri_pattern: + type: string + example: http://www.wikidata.org/entity/.* + sparql_endpoint: + type: string + example: https://query.wikidata.org/sparql + rdf_path: + type: string + example: rdf/om-2.0.rdf + BannerMessageUpdateDto: + required: + - message + - type + type: object + properties: + type: + type: string + enum: + - error + - warning + - info + message: + type: string + example: Maintenance starts on 8am on Monday + link: + type: string + example: https://example.com + link_text: + type: string + example: More + display_start: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + display_end: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + BannerMessageBriefDto: + required: + - message + - type + type: object + properties: + type: + type: string + enum: + - error + - warning + - info + message: + type: string + example: Maintenance starts on 8am on Monday + link: + type: string + example: https://example.com + link_text: + type: string + example: More + ImageChangeDto: + required: + - dialect + - driver_class + - jdbc_method + - registry + type: object + properties: + registry: + type: string + example: docker.io/library + defaultPort: + maximum: 65535 + minimum: 1024 + type: integer + format: int32 + example: 5432 + dialect: + type: string + example: Postgres + driver_class: + type: string + example: org.postgresql.Driver + jdbc_method: + type: string + example: postgresql + DataTypeDto: + required: + - display_name + - documentation + - is_buildable + - is_quoted + - value + type: object + properties: + value: + type: string + example: time + documentation: + type: string + example: https://mariadb.com/kb/en/time/ + display_name: + type: string + example: TIME(fsp) + size_min: + type: integer + format: int32 + example: 0 + size_max: + type: integer + format: int32 + example: 6 + size_default: + type: integer + format: int32 + example: 0 + size_required: + type: boolean + example: false + d_min: + type: integer + format: int32 + d_max: + type: integer + format: int32 + d_default: + type: integer + format: int32 + d_required: + type: boolean + data_hint: + type: string + example: "e.g. HH:MM:SS, HH:MM, HHMMSS, H:M:S" + type_hint: + type: string + example: "fsp=microsecond precision, min. 0, max. 6" + is_quoted: + type: boolean + description: frontend needs to quote this data type + example: false + is_buildable: + type: boolean + description: frontend can build this data type + example: true + ImageDto: + required: + - data_types + - default + - default_port + - dialect + - driver_class + - id + - jdbc_method + - name + - operators + - registry + - version + type: object + properties: + id: + type: integer + format: int64 + registry: + type: string + example: docker.io/library + name: + type: string + example: mariadb + version: + type: string + example: "10.5" + dialect: + type: string + example: org.hibernate.dialect.MariaDBDialect + operators: + type: array + items: + $ref: "#/components/schemas/OperatorDto" + driver_class: + type: string + example: org.mariadb.jdbc.Driver + jdbc_method: + type: string + example: mariadb + default: + type: boolean + example: false + default_port: + type: integer + format: int32 + example: 3306 + data_types: + type: array + items: + $ref: "#/components/schemas/DataTypeDto" + OperatorDto: + required: + - display_name + - documentation + - value + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + example: XOR + documentation: + type: string + example: https://mariadb.com/kb/en/xor/ + display_name: + type: string + example: XOR + CreatorSaveDto: + required: + - creator_name + - id + type: object + properties: + id: + type: integer + format: int64 + example: 1 + firstname: + type: string + example: Josiah + lastname: + type: string + example: Carberry + affiliation: + type: string + example: Wesleyan University + creator_name: + type: string + example: "Carberry, Josiah" + name_type: + type: string + example: Personal + enum: + - Personal + - Organizational + name_identifier: + type: string + example: 0000-0002-1825-0097 + name_identifier_scheme: + type: string + example: ORCID + enum: + - ORCID + - ROR + - ISNI + - GRID + affiliation_identifier: + type: string + example: https://ror.org/04d836q62 + affiliation_identifier_scheme: + type: string + example: ROR + enum: + - ROR + - GRID + - ISNI + IdentifierFunderSaveDto: + required: + - funder_name + - id + type: object + properties: + id: + type: integer + format: int64 + example: 1 + funder_name: + type: string + example: European Commission + funder_identifier: + type: string + example: http://doi.org/10.13039/501100000780 + funder_identifier_type: + type: string + example: Crossref Funder ID + enum: + - Crossref Funder ID + - ROR + - GND + - ISNI + - Other + scheme_uri: + type: string + example: http://doi.org/ + award_number: + type: string + example: "824087" + award_title: + type: string + example: EOSC-Life + IdentifierSaveDescriptionDto: + required: + - description + - id + type: object + properties: + id: + type: integer + format: int64 + example: 1 + description: + type: string + example: "Air quality reports at Stephansplatz, Vienna" + language: + type: string + example: en + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + type: + type: string + example: Abstract + enum: + - Abstract + - Methods + - SeriesInformation + - TableOfContents + - TechnicalInfo + - Other + IdentifierSaveDto: + required: + - creators + - database_id + - id + - publication_year + - publisher + - titles + - type + type: object + properties: + id: + type: integer + format: int64 + example: 1 + type: + type: string + example: database + enum: + - database + - subset + - table + - view + doi: + type: string + example: 10.1111/11111111 + titles: + type: array + items: + $ref: "#/components/schemas/IdentifierSaveTitleDto" + descriptions: + type: array + items: + $ref: "#/components/schemas/IdentifierSaveDescriptionDto" + funders: + type: array + items: + $ref: "#/components/schemas/IdentifierFunderSaveDto" + licenses: + type: array + items: + $ref: "#/components/schemas/LicenseDto" + publisher: + type: string + example: TU Wien + language: + type: string + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + creators: + type: array + items: + $ref: "#/components/schemas/CreatorSaveDto" + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + view_id: + type: integer + format: int64 + table_id: + type: integer + format: int64 + publication_day: + type: integer + format: int32 + example: 15 + publication_month: + type: integer + format: int32 + example: 12 + publication_year: + type: integer + format: int32 + example: 2022 + related_identifiers: + type: array + items: + $ref: "#/components/schemas/RelatedIdentifierSaveDto" + IdentifierSaveTitleDto: + required: + - id + - title + type: object + properties: + id: + type: integer + format: int64 + example: 1 + title: + type: string + example: Airquality Demonstrator + language: + type: string + example: en + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + type: + type: string + example: Subtitle + enum: + - AlternativeTitle + - Subtitle + - TranslatedTitle + - Other + LicenseDto: + required: + - identifier + - uri + type: object + properties: + identifier: + type: string + example: MIT + uri: + type: string + example: https://opensource.org/licenses/MIT + description: + type: string + example: "A short and simple permissive license with conditions only requiring\ + \ preservation of copyright and license notices. Licensed works, modifications,\ + \ and larger works may be distributed under different terms and without\ + \ source code." + RelatedIdentifierSaveDto: + required: + - id + - relation + - type + - value + type: object + properties: + id: + type: integer + format: int64 + example: 1 + value: + type: string + example: 10.70124/dc4zh-9ce78 + type: + type: string + example: DOI + enum: + - DOI + - URL + - URN + - ARK + - arXiv + - bibcode + - EAN13 + - EISSN + - Handle + - IGSN + - ISBN + - ISTC + - LISSN + - LSID + - PMID + - PURL + - UPC + - w3id + relation: + type: string + example: Cites + enum: + - IsCitedBy + - Cites + - IsSupplementTo + - IsSupplementedBy + - IsContinuedBy + - Continues + - IsDescribedBy + - Describes + - HasMetadata + - IsMetadataFor + - HasVersion + - IsVersionOf + - IsNewVersionOf + - IsPreviousVersionOf + - IsPartOf + - HasPart + - IsPublishedIn + - IsReferencedBy + - References + - IsDocumentedBy + - Documents + - IsCompiledBy + - Compiles + - IsVariantFormOf + - IsOriginalFormOf + - IsIdenticalTo + - IsReviewedBy + - Reviews + - IsDerivedFrom + - IsSourceOf + - IsRequiredBy + - Requires + - IsObsoletedBy + - Obsoletes + CreatorDto: + required: + - creator_name + - id + type: object + properties: + id: + type: integer + format: int64 + firstname: + type: string + example: Josiah + lastname: + type: string + example: Carberry + affiliation: + type: string + example: Brown University + creator_name: + type: string + example: "Carberry, Josiah" + name_type: + type: string + example: Personal + enum: + - Personal + - Organizational + name_identifier: + type: string + example: 0000-0002-1825-0097 + name_identifier_scheme: + type: string + example: ORCID + enum: + - ORCID + - ROR + - ISNI + - GRID + name_identifier_scheme_uri: + type: string + example: https://orcid.org/ + affiliation_identifier: + type: string + example: https://ror.org/05gq02987 + affiliation_identifier_scheme: + type: string + example: ROR + enum: + - ROR + - GRID + - ISNI + affiliation_identifier_scheme_uri: + type: string + example: https://ror.org/ + IdentifierDescriptionDto: + required: + - id + type: object + properties: + id: + type: integer + format: int64 + description: + type: string + example: "Air quality reports at Stephansplatz, Vienna" + language: + type: string + example: en + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + type: + type: string + example: Abstract + enum: + - Abstract + - Methods + - SeriesInformation + - TableOfContents + - TechnicalInfo + - Other + IdentifierDto: + required: + - creators + - database_id + - id + - owner + - publication_year + - publisher + - query + - query_hash + - query_normalized + - titles + - type + type: object + properties: + id: + type: integer + format: int64 + type: + type: string + enum: + - database + - subset + - table + - view + titles: + type: array + items: + $ref: "#/components/schemas/IdentifierTitleDto" + descriptions: + type: array + items: + $ref: "#/components/schemas/IdentifierDescriptionDto" + funders: + type: array + items: + $ref: "#/components/schemas/IdentifierFunderDto" + query: + type: string + example: "SELECT `id`, `value`, `location` FROM `air_quality` WHERE `location`\ + \ = \"09:STEF\"" + execution: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + doi: + type: string + example: 10.1038/nphys1170 + publisher: + type: string + example: TU Wien + owner: + $ref: "#/components/schemas/UserBriefDto" + language: + type: string + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + licenses: + type: array + items: + $ref: "#/components/schemas/LicenseDto" + creators: + type: array + items: + $ref: "#/components/schemas/CreatorDto" + status: + type: string + enum: + - draft + - published + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + example: 1 + table_id: + type: integer + format: int64 + example: 1 + view_id: + type: integer + format: int64 + example: 1 + query_normalized: + type: string + example: "SELECT `id`, `value`, `location` FROM `air_quality` WHERE `location`\ + \ = \"09:STEF\"" + related_identifiers: + type: array + items: + $ref: "#/components/schemas/RelatedIdentifierDto" + query_hash: + type: string + description: query hash in sha512 + result_hash: + type: string + example: 34fe82cda2c53f13f8d90cfd7a3469e3a939ff311add50dce30d9136397bf8e5 + result_number: + type: integer + format: int64 + example: 1 + publication_day: + type: integer + format: int32 + example: 15 + publication_month: + type: integer + format: int32 + example: 12 + publication_year: + type: integer + format: int32 + example: 2022 + IdentifierFunderDto: + required: + - funder_name + - id + type: object + properties: + id: + type: integer + format: int64 + funder_name: + type: string + example: European Commission + funder_identifier: + type: string + example: http://doi.org/10.13039/501100000780 + funder_identifier_type: + type: string + example: Crossref Funder ID + enum: + - Crossref Funder ID + - ROR + - GND + - ISNI + - Other + scheme_uri: + type: string + example: http://doi.org/ + award_number: + type: string + example: "824087" + award_title: + type: string + example: EOSC-Life + RelatedIdentifierDto: + required: + - id + - relation + - type + - value + type: object + properties: + id: + type: integer + format: int64 + value: + type: string + example: 10.70124/dc4zh-9ce78 + type: + type: string + example: DOI + enum: + - DOI + - URL + - URN + - ARK + - arXiv + - bibcode + - EAN13 + - EISSN + - Handle + - IGSN + - ISBN + - ISTC + - LISSN + - LSID + - PMID + - PURL + - UPC + - w3id + relation: + type: string + example: Cites + enum: + - IsCitedBy + - Cites + - IsSupplementTo + - IsSupplementedBy + - IsContinuedBy + - Continues + - IsDescribedBy + - Describes + - HasMetadata + - IsMetadataFor + - HasVersion + - IsVersionOf + - IsNewVersionOf + - IsPreviousVersionOf + - IsPartOf + - HasPart + - IsPublishedIn + - IsReferencedBy + - References + - IsDocumentedBy + - Documents + - IsCompiledBy + - Compiles + - IsVariantFormOf + - IsOriginalFormOf + - IsIdenticalTo + - IsReviewedBy + - Reviews + - IsDerivedFrom + - IsSourceOf + - IsRequiredBy + - Requires + - IsObsoletedBy + - Obsoletes + DatabaseModifyVisibilityDto: + required: + - is_public + - is_schema_public + type: object + properties: + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + ViewUpdateDto: + required: + - is_public + - is_schema_public + type: object + properties: + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + ViewColumnDto: + required: + - auto_generated + - database_id + - id + - internal_name + - is_null_allowed + - name + - ord + - type + type: object + properties: + id: + type: integer + format: int64 + name: + maxLength: 64 + minLength: 0 + type: string + example: Date + size: + type: integer + format: int64 + example: 255 + d: + type: integer + format: int64 + example: 0 + description: + maxLength: 2048 + minLength: 0 + type: string + example: Column comment + database_id: + type: integer + format: int64 + ord: + type: integer + format: int32 + example: 0 + internal_name: + maxLength: 64 + minLength: 0 + type: string + example: mdb_date + auto_generated: + type: boolean + example: false + index_length: + type: integer + format: int64 + length: + type: integer + format: int64 + type: + type: string + example: string + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + is_null_allowed: + type: boolean + example: false + ViewDto: + required: + - columns + - database_id + - id + - internal_name + - name + - owner + - query + - query_hash + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + identifiers: + type: array + items: + $ref: "#/components/schemas/IdentifierDto" + query: + type: string + example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC + owner: + $ref: "#/components/schemas/UserBriefDto" + columns: + type: array + items: + $ref: "#/components/schemas/ViewColumnDto" + last_retrieved: + type: string + format: date-time + database_id: + type: integer + format: int64 + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + initial_view: + type: boolean + description: True if it is the default view for the database + example: true + query_hash: + type: string + example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 + TableUpdateDto: + required: + - is_public + - is_schema_public + type: object + properties: + description: + maxLength: 180 + minLength: 0 + type: string + example: Air Quality in Austria + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + TableBriefDto: + required: + - database_id + - id + - internal_name + - is_public + - is_schema_public + - is_versioned + - name + - owned_by + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + description: + type: string + example: Air Quality in Austria + database_id: + type: integer + format: int64 + internal_name: + type: string + example: air_quality + is_versioned: + type: boolean + example: true + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + owned_by: + type: string + format: uuid + ColumnSemanticsUpdateDto: + type: object + properties: + concept_uri: + type: string + unit_uri: + type: string + ColumnDto: + required: + - database_id + - id + - internal_name + - is_null_allowed + - name + - ord + - table_id + - type + type: object + properties: + id: + type: integer + format: int64 + example: 1 + name: + maxLength: 64 + minLength: 0 + type: string + example: Given Name + alias: + type: string + example: firstname + size: + type: integer + format: int64 + example: 255 + d: + type: integer + format: int64 + example: 0 + mean: + type: number + example: 45.4 + median: + type: number + example: 51 + concept: + $ref: "#/components/schemas/ConceptBriefDto" + unit: + $ref: "#/components/schemas/UnitBriefDto" + description: + maxLength: 2048 + minLength: 0 + type: string + example: Column comment + enums: + type: array + items: + type: string + sets: + type: array + items: + type: string + database_id: + type: integer + format: int64 + example: 2 + table_id: + type: integer + format: int64 + example: 3 + ord: + type: integer + format: int32 + example: 0 + internal_name: + maxLength: 64 + minLength: 0 + type: string + example: given_name + index_length: + type: integer + format: int64 + example: 255 + length: + type: integer + format: int64 + example: 255 + type: + type: string + example: varchar + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + data_length: + type: integer + format: int64 + example: 34300 + max_data_length: + type: integer + format: int64 + example: 34300 + num_rows: + type: integer + format: int64 + example: 32 + val_min: + type: number + example: 0 + val_max: + type: number + example: 100 + std_dev: + type: number + example: 5.32 + is_null_allowed: + type: boolean + example: false + ConceptBriefDto: + required: + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + example: 23 + uri: + type: string + example: http://www.wikidata.org/entity/Q202444 + name: + type: string + example: given name + description: + type: string + example: "name typically used to differentiate people from the same family,\ + \ clan, or other social group who have a common last name" + UnitBriefDto: + required: + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + example: 34 + uri: + type: string + example: http://www.wikidata.org/entity/Q1422583 + name: + type: string + example: importance + description: + type: string + example: "subjective magnitude of value, meaning, or purpose" + DatabaseTransferDto: + required: + - id + type: object + properties: + id: + type: string + format: uuid + DatabaseModifyImageDto: + type: object + properties: + key: + type: string + UpdateDatabaseAccessDto: + required: + - type + type: object + properties: + type: + type: string + enum: + - read + - write_own + - write_all + SignupRequestDto: + required: + - email + - password + - username + type: object + properties: + username: + pattern: "^[a-z0-9]{3,}$" + type: string + example: user + email: + type: string + example: user@example.com + password: + type: string + LoginRequestDto: + required: + - password + - username + type: object + properties: + username: + type: string + example: user + password: + type: string + OntologyCreateDto: + required: + - prefix + - uri + type: object + properties: + uri: + type: string + example: Ontology URI + prefix: + type: string + example: Ontology prefix + sparql_endpoint: + type: string + example: Ontology SPARQL endpoint + BannerMessageCreateDto: + required: + - message + - type + type: object + properties: + type: + type: string + enum: + - error + - warning + - info + message: + type: string + example: Maintenance starts on 8am on Monday + link: + type: string + example: https://example.com + link_text: + type: string + example: More + display_start: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + display_end: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + ImageCreateDto: + required: + - default_port + - dialect + - driver_class + - is_default + - jdbc_method + - name + - registry + - version + type: object + properties: + registry: + type: string + example: docker.io/library + name: + type: string + example: mariadb + version: + type: string + dialect: + type: string + is_default: + type: boolean + example: false + driver_class: + type: string + jdbc_method: + type: string + default_port: + maximum: 65535 + minimum: 1024 + type: integer + format: int32 + IdentifierCreateDto: + required: + - creators + - database_id + - publication_year + - publisher + - titles + - type + type: object + properties: + type: + type: string + example: database + enum: + - database + - subset + - table + - view + doi: + type: string + example: 10.1111/11111111 + titles: + type: array + items: + $ref: "#/components/schemas/IdentifierSaveTitleDto" + descriptions: + type: array + items: + $ref: "#/components/schemas/IdentifierSaveDescriptionDto" + funders: + type: array + items: + $ref: "#/components/schemas/IdentifierFunderSaveDto" + licenses: + type: array + items: + $ref: "#/components/schemas/LicenseDto" + publisher: + type: string + example: TU Wien + language: + type: string + enum: + - ab + - aa + - af + - ak + - sq + - am + - ar + - an + - hy + - as + - av + - ae + - ay + - az + - bm + - ba + - eu + - be + - bn + - bh + - bi + - bs + - br + - bg + - my + - ca + - km + - ch + - ce + - ny + - zh + - cu + - cv + - kw + - co + - cr + - hr + - cs + - da + - dv + - nl + - dz + - en + - eo + - et + - ee + - fo + - fj + - fi + - fr + - ff + - gd + - gl + - lg + - ka + - de + - ki + - el + - kl + - gn + - gu + - ht + - ha + - he + - hz + - hi + - ho + - hu + - is + - io + - ig + - id + - ia + - ie + - iu + - ik + - ga + - it + - ja + - jv + - kn + - kr + - ks + - kk + - rw + - kv + - kg + - ko + - kj + - ku + - ky + - lo + - la + - lv + - lb + - li + - ln + - lt + - lu + - mk + - mg + - ms + - ml + - mt + - gv + - mi + - mr + - mh + - ro + - mn + - na + - nv + - nd + - ng + - ne + - se + - "no" + - nb + - nn + - ii + - oc + - oj + - or + - om + - os + - pi + - pa + - ps + - fa + - pl + - pt + - qu + - rm + - rn + - ru + - sm + - sg + - sa + - sc + - sr + - sn + - sd + - si + - sk + - sl + - so + - st + - nr + - es + - su + - sw + - ss + - sv + - tl + - ty + - tg + - ta + - tt + - te + - th + - bo + - ti + - to + - ts + - tn + - tr + - tk + - tw + - ug + - uk + - ur + - uz + - ve + - vi + - vo + - wa + - cy + - fy + - wo + - xh + - yi + - yo + - za + - zu + creators: + type: array + items: + $ref: "#/components/schemas/CreatorSaveDto" + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + view_id: + type: integer + format: int64 + table_id: + type: integer + format: int64 + publication_day: + type: integer + format: int32 + example: 15 + publication_month: + type: integer + format: int32 + example: 12 + publication_year: + type: integer + format: int32 + example: 2022 + related_identifiers: + type: array + items: + $ref: "#/components/schemas/RelatedIdentifierSaveDto" + DatabaseCreateDto: + required: + - container_id + - is_public + - is_schema_public + - name + type: object + properties: + name: + type: string + example: Air Quality + container_id: + type: integer + format: int64 + example: 1 + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + ViewCreateDto: + required: + - is_public + - is_schema_public + - name + - query + type: object + properties: + name: + maxLength: 63 + minLength: 1 + type: string + example: Air Quality + query: + type: string + example: SELECT `id` FROM `air_quality` + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + ViewBriefDto: + required: + - database_id + - id + - internal_name + - name + - query + - query_hash + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + query: + type: string + example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC + database_id: + type: integer + format: int64 + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + initial_view: + type: boolean + description: True if it is the default view for the database + example: true + query_hash: + type: string + example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 + owned_by: + type: string + format: uuid + ColumnCreateDto: + required: + - name + - null_allowed + - type + type: object + properties: + name: + type: string + example: Date + type: + type: string + example: string + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + size: + type: integer + format: int64 + example: 255 + d: + type: integer + format: int64 + example: 0 + description: + maxLength: 2048 + minLength: 0 + type: string + example: Formatted as YYYY-MM-dd + enums: + type: array + description: "enum values, only considered when type = ENUM" + items: + type: string + description: "enum values, only considered when type = ENUM" + sets: + type: array + description: "set values, only considered when type = SET" + items: + type: string + description: "set values, only considered when type = SET" + index_length: + type: integer + format: int64 + null_allowed: + type: boolean + example: true + concept_uri: + type: string + unit_uri: + type: string + ConstraintsCreateDto: + required: + - checks + - foreign_keys + - primary_key + - uniques + type: object + properties: + uniques: + type: array + items: + type: array + items: + type: string + checks: + uniqueItems: true + type: array + items: + type: string + foreign_keys: + type: array + items: + $ref: "#/components/schemas/ForeignKeyCreateDto" + primary_key: + uniqueItems: true + type: array + items: + type: string + ForeignKeyCreateDto: + required: + - columns + - referenced_columns + - referenced_table + type: object + properties: + columns: + type: array + items: + type: string + referenced_table: + type: string + referenced_columns: + type: array + items: + type: string + on_update: + type: string + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + on_delete: + type: string + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + TableCreateDto: + required: + - columns + - constraints + - is_public + - is_schema_public + - name + type: object + properties: + name: + maxLength: 64 + minLength: 1 + type: string + example: Air Quality + description: + maxLength: 180 + minLength: 0 + type: string + example: Air Quality in Austria + columns: + type: array + items: + $ref: "#/components/schemas/ColumnCreateDto" + constraints: + $ref: "#/components/schemas/ConstraintsCreateDto" + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + ContainerCreateDto: + required: + - host + - image_id + - name + - privileged_password + - privileged_username + - quota + type: object + properties: + name: + type: string + example: Air Quality + host: + type: string + description: Hostname of container + port: + type: integer + description: Port of container + format: int32 + quota: + type: integer + format: int64 + example: 50 + image_id: + type: integer + description: Image ID + format: int64 + ui_host: + type: string + ui_port: + type: integer + format: int32 + privileged_username: + type: string + description: Username of privileged user + example: root + privileged_password: + type: string + description: Password of privileged user + ContainerDto: + required: + - count + - id + - image + - internal_name + - name + - quota + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + image: + $ref: "#/components/schemas/ImageDto" + quota: + type: integer + format: int64 + example: 50 + count: + type: integer + format: int64 + example: 10 + last_retrieved: + type: string + format: date-time + internal_name: + type: string + example: data-db + ui_host: + type: string + ui_port: + type: integer + format: int32 + ColumnBriefDto: + required: + - column_type + - database_id + - id + - internal_name + - name + - table_id + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: date + alias: + type: string + database_id: + type: integer + format: int64 + table_id: + type: integer + format: int64 + internal_name: + type: string + example: mdb_date + column_type: + type: string + example: date + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + UnitDto: + required: + - columns + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + uri: + type: string + name: + type: string + description: + type: string + columns: + type: array + items: + $ref: "#/components/schemas/ColumnBriefDto" + OntologyBriefDto: + required: + - id + - prefix + - rdf + - sparql + - uri + type: object + properties: + id: + type: integer + format: int64 + uri: + type: string + example: http://www.wikidata.org/ + prefix: + type: string + example: wd + sparql: + type: boolean + example: true + rdf: + type: boolean + example: false + uri_pattern: + type: string + example: http://www.wikidata.org/entity/.* + EntityDto: + required: + - label + - uri + type: object + properties: + uri: + type: string + example: https://www.wikidata.org/entity/Q1686799 + label: + type: string + example: Apache Jena + description: + type: string + example: open source semantic web framework for Java + OaiListIdentifiersParameters: + type: object + properties: + metadataPrefix: + type: string + from: + type: string + until: + type: string + set: + type: string + resumptionToken: + type: string + fromDate: + type: string + format: date-time + untilDate: + type: string + format: date-time + parametersString: + type: string + BannerMessageDto: + required: + - id + - message + - type + type: object + properties: + id: + type: integer + format: int64 + type: + type: string + enum: + - error + - warning + - info + message: + type: string + example: Maintenance starts on 8am on Monday + link: + type: string + example: https://example.com + link_text: + type: string + example: More + display_start: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + display_end: + type: string + format: date-time + example: 2021-03-12T15:26:21Z + ImageBriefDto: + required: + - default + - id + - jdbc_method + - name + - version + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: mariadb + version: + type: string + example: "10.5" + jdbc_method: + type: string + example: mariadb + default: + type: boolean + example: false + LdCreatorDto: + required: + - '@type' + - name + type: object + properties: + name: + type: string + sameAs: + type: string + givenName: + type: string + familyName: + type: string + '@type': + type: string + LdDatasetDto: + required: + - '@context' + - '@type' + - citation + - creator + - description + - hasPart + - identifier + - name + - temporalCoverage + - url + - version + type: object + properties: + name: + type: string + description: + type: string + url: + type: string + identifier: + type: array + items: + type: string + license: + type: string + creator: + type: array + items: + $ref: "#/components/schemas/LdCreatorDto" + citation: + type: string + hasPart: + type: array + items: + $ref: "#/components/schemas/LdDatasetDto" + temporalCoverage: + type: string + version: + type: string + format: date-time + '@context': + type: string + '@type': + type: string + ConstraintsDto: + type: object + properties: + uniques: + type: array + items: + $ref: "#/components/schemas/UniqueDto" + checks: + uniqueItems: true + type: array + items: + type: string + foreign_keys: + type: array + items: + $ref: "#/components/schemas/ForeignKeyDto" + primary_key: + uniqueItems: true + type: array + items: + $ref: "#/components/schemas/PrimaryKeyDto" + ForeignKeyBriefDto: + type: object + properties: + id: + type: integer + format: int64 + ForeignKeyDto: + required: + - name + - referenced_table + - references + - table + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + references: + type: array + items: + $ref: "#/components/schemas/ForeignKeyReferenceDto" + table: + $ref: "#/components/schemas/TableBriefDto" + referenced_table: + $ref: "#/components/schemas/TableBriefDto" + on_update: + type: string + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + on_delete: + type: string + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + ForeignKeyReferenceDto: + required: + - column + - foreign_key + - referenced_column + type: object + properties: + id: + type: integer + format: int64 + column: + $ref: "#/components/schemas/ColumnBriefDto" + foreign_key: + $ref: "#/components/schemas/ForeignKeyBriefDto" + referenced_column: + $ref: "#/components/schemas/ColumnBriefDto" + PrimaryKeyDto: + required: + - column + - table + type: object + properties: + id: + type: integer + format: int64 + table: + $ref: "#/components/schemas/TableBriefDto" + column: + $ref: "#/components/schemas/ColumnBriefDto" + TableDto: + required: + - columns + - constraints + - database_id + - id + - internal_name + - is_public + - is_schema_public + - is_versioned + - name + - owner + - queue_name + - routing_key + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + example: Air Quality + alias: + type: string + identifiers: + type: array + items: + $ref: "#/components/schemas/IdentifierDto" + owner: + $ref: "#/components/schemas/UserBriefDto" + description: + maxLength: 2048 + minLength: 0 + type: string + example: Air Quality in Austria + columns: + type: array + items: + $ref: "#/components/schemas/ColumnDto" + constraints: + $ref: "#/components/schemas/ConstraintsDto" + last_retrieved: + type: string + format: date-time + database_id: + type: integer + format: int64 + internal_name: + type: string + example: air_quality + is_versioned: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + queue_name: + type: string + example: air_quality + queue_type: + type: string + example: quorum + routing_key: + type: string + example: dbrepo.1.2 + is_public: + type: boolean + example: true + num_rows: + type: integer + format: int64 + example: 5 + data_length: + type: integer + description: in bytes + format: int64 + example: 16384 + max_data_length: + type: integer + description: in bytes + format: int64 + example: 0 + avg_row_length: + type: integer + description: in bytes + format: int64 + example: 3276 + UniqueDto: + required: + - columns + - id + - name + - table + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + table: + $ref: "#/components/schemas/TableBriefDto" + columns: + type: array + items: + $ref: "#/components/schemas/ColumnDto" + TableColumnEntityDto: + required: + - column_id + - database_id + - table_id + - uri + type: object + properties: + uri: + type: string + example: https://www.wikidata.org/entity/Q1686799 + label: + type: string + example: Apache Jena + description: + type: string + example: open source semantic web framework for Java + database_id: + type: integer + format: int64 + example: 1 + table_id: + type: integer + format: int64 + example: 1 + column_id: + type: integer + format: int64 + example: 1 + ContainerBriefDto: + required: + - count + - hash + - id + - image + - internal_name + - name + - quota + type: object + properties: + id: + type: integer + format: int64 + hash: + type: string + example: f829dd8a884182d0da846f365dee1221fd16610a14c81b8f9f295ff162749e50 + name: + type: string + example: Air Quality + image: + $ref: "#/components/schemas/ImageBriefDto" + quota: + type: integer + format: int32 + example: 50 + count: + type: integer + format: int32 + example: 10 + internal_name: + type: string + example: air-quality + ConceptDto: + required: + - columns + - id + - uri + type: object + properties: + id: + type: integer + format: int64 + uri: + type: string + name: + type: string + description: + type: string + columns: + type: array + items: + $ref: "#/components/schemas/ColumnBriefDto" + securitySchemes: + basicAuth: + type: http + scheme: basic + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT -- GitLab From e975e9200051c70f8cb5d137512c84aa466c9ee7 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Wed, 22 Jan 2025 18:50:50 +0100 Subject: [PATCH 08/19] Updated syntax Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .gitlab-ci.yml | 8 ++++---- .scripts/check-service.sh | 23 ++++++++++++++--------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 100e56ff21..4e4c751bf4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -51,9 +51,9 @@ lint-docker-compose: - 'wget https://github.com/mikefarah/yq/releases/download/v${VERSION}/${BINARY} -O /usr/bin/yq && chmod +x /usr/bin/yq' script: - "bash .scripts/check-compose.sh" - - "yq compare -P docker-compose.yml .docker/docker-compose.yml 'volumes.*'" + - "diff <(yq '.volumes' docker-compose.yml) <(yq '.volumes' .docker/docker-compose.yml)" - "IGNORE_IMAGE=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-analyse-service'" - - "bash .scripts/check-service.sh 'dbrepo-auth-db'" + - "IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-auth-db'" - "IGNORE_IMAGE=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-auth-service'" - "IGNORE_IMAGE=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-broker-service'" - "IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-dashboard-service'" @@ -126,8 +126,8 @@ lint-open-api-version: - 'apk --no-cache add bash wget' - 'wget https://github.com/mikefarah/yq/releases/download/v${VERSION}/${BINARY} -O /usr/bin/yq && chmod +x /usr/bin/yq' script: - - yq r ./.docs/.openapi/api.base.yaml 'externalDocs.url' | grep "${DOC_VERSION}" - - yq r ./.docs/.openapi/api.base.yaml 'info.version' | grep "${DOC_VERSION}" + - yq '.externalDocs.url' ./.docs/.openapi/api.base.yaml | grep "${DOC_VERSION}" + - yq '.info.version' ./.docs/.openapi/api.base.yaml | grep "${DOC_VERSION}" build-metadata-service: image: maven:3-openjdk-${JAVA_VERSION} diff --git a/.scripts/check-service.sh b/.scripts/check-service.sh index decc22312b..1af6d7eea1 100755 --- a/.scripts/check-service.sh +++ b/.scripts/check-service.sh @@ -1,16 +1,21 @@ #!/bin/bash -yq compare -P docker-compose.yml .docker/docker-compose.yml "services.$1.restart" -yq compare -P docker-compose.yml .docker/docker-compose.yml "services.$1.container_name" -yq compare -P docker-compose.yml .docker/docker-compose.yml "services.$1.hostname" +function compare () { + diff <(yq ".$1" docker-compose.yml) <(yq ".$1" .docker/docker-compose.yml) +} + +compare "services.$1.restart" +compare "services.$1.container_name" +compare "services.$1.hostname" +compare "services.$1.environment" +compare "services.$1.healthcheck" +compare "services.$1.logging" + if [ -z "$IGNORE_IMAGE" ]; then - yq compare -P docker-compose.yml .docker/docker-compose.yml "services.$1.image" + compare "services.$1.image" fi if [ -z "$IGNORE_VOLUMES" ]; then - yq compare -P docker-compose.yml .docker/docker-compose.yml "services.$1.volumes" + compare "services.$1.volumes" fi if [ -z "$IGNORE_PORTS" ]; then - yq compare -P docker-compose.yml .docker/docker-compose.yml "services.$1.ports" + compare "services.$1.ports" fi -yq compare -P docker-compose.yml .docker/docker-compose.yml "services.$1.environment" -yq compare -P docker-compose.yml .docker/docker-compose.yml "services.$1.healthcheck" -yq compare -P docker-compose.yml .docker/docker-compose.yml "services.$1.logging" \ No newline at end of file -- GitLab From 37f04e4035d9649a7d50e328823d066e8ebee8fb Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Thu, 23 Jan 2025 16:11:46 +0100 Subject: [PATCH 09/19] Re-added paging, ref #487 Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .docs/.openapi/api-data.yaml | 445 ++++--- .docs/.openapi/api-metadata.yaml | 1129 ++++++++++------- .docs/.openapi/api.yaml | 246 +++- .../at/tuwien/endpoints/AccessEndpoint.java | 6 +- .../at/tuwien/endpoints/TableEndpoint.java | 2 +- .../java/at/tuwien/mapper/DataMapper.java | 5 +- .../main/java/at/tuwien/api/CacheableDto.java | 12 +- .../api/container/ContainerBriefDto.java | 1 + .../at/tuwien/api/container/ContainerDto.java | 28 +- ...CreateDto.java => CreateContainerDto.java} | 14 +- .../api/container/image/ImageBriefDto.java | 1 + .../tuwien/api/container/image/ImageDto.java | 1 + .../at/tuwien/api/database/AccessTypeDto.java | 2 + ...aseAccessDto.java => CreateAccessDto.java} | 4 +- .../api/database/DatabaseAccessDto.java | 2 + .../tuwien/api/database/DatabaseBriefDto.java | 3 + .../at/tuwien/api/database/DatabaseDto.java | 17 +- .../at/tuwien/api/database/ViewBriefDto.java | 5 +- .../at/tuwien/api/database/ViewColumnDto.java | 17 +- .../java/at/tuwien/api/database/ViewDto.java | 14 +- .../tuwien/api/database/query/ImportDto.java | 2 +- .../tuwien/api/database/query/QueryDto.java | 2 + .../api/database/query/QueryTypeDto.java | 2 + .../api/database/table/TableBriefDto.java | 3 + .../tuwien/api/database/table/TableDto.java | 15 +- .../api/database/table/TupleDeleteDto.java | 2 + .../tuwien/api/database/table/TupleDto.java | 2 + .../api/database/table/TupleUpdateDto.java | 3 + .../table/columns/ColumnBriefDto.java | 20 +- .../api/database/table/columns/ColumnDto.java | 8 +- .../database/table/columns/ColumnTypeDto.java | 2 + .../table/constraints/ConstraintsDto.java | 2 + .../foreign/ForeignKeyBriefDto.java | 3 + .../constraints/foreign/ForeignKeyDto.java | 5 + .../foreign/ForeignKeyReferenceDto.java | 2 + .../constraints/primary/PrimaryKeyDto.java | 2 + .../table/constraints/unique/UniqueDto.java | 10 +- .../api/identifier/CreatorBriefDto.java | 53 + .../at/tuwien/api/identifier/CreatorDto.java | 1 + .../api/identifier/IdentifierBriefDto.java | 12 +- .../identifier/IdentifierDescriptionDto.java | 1 + .../tuwien/api/identifier/IdentifierDto.java | 8 + .../api/identifier/IdentifierFunderDto.java | 1 + .../api/identifier/IdentifierTitleDto.java | 1 + .../api/identifier/RelatedIdentifierDto.java | 1 + .../main/java/at/tuwien/api/user/UserDto.java | 35 +- .../java/at/tuwien/mapper/MetadataMapper.java | 16 +- .../at/tuwien/endpoints/AccessEndpoint.java | 6 +- .../tuwien/endpoints/ContainerEndpoint.java | 4 +- .../src/main/resources/application.yml | 4 +- .../endpoints/ContainerEndpointUnitTest.java | 10 +- .../tuwien/mvc/PrometheusEndpointMvcTest.java | 7 +- .../service/ContainerServiceUnitTest.java | 8 +- .../gateway/impl/DataServiceGatewayImpl.java | 4 +- .../at/tuwien/service/ContainerService.java | 6 +- .../service/impl/ContainerServiceImpl.java | 4 +- .../main/java/at/tuwien/test/BaseTest.java | 33 +- dbrepo-ui/components/identifier/Citation.vue | 2 +- dbrepo-ui/composables/identifier-service.ts | 2 +- dbrepo-ui/nuxt.config.ts | 30 +- .../[database_id]/table/[table_id]/data.vue | 1 + lib/python/dbrepo/RestClient.py | 6 +- lib/python/dbrepo/api/dto.py | 88 +- lib/python/tests/test_dtos.py | 72 +- 64 files changed, 1587 insertions(+), 868 deletions(-) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/{ContainerCreateDto.java => CreateContainerDto.java} (68%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/{UpdateDatabaseAccessDto.java => CreateAccessDto.java} (72%) create mode 100644 dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorBriefDto.java diff --git a/.docs/.openapi/api-data.yaml b/.docs/.openapi/api-data.yaml index 7be3a4039a..f61466dc6b 100644 --- a/.docs/.openapi/api-data.yaml +++ b/.docs/.openapi/api-data.yaml @@ -59,18 +59,6 @@ paths: type: string format: date-time responses: - "400": - description: Request pagination is malformed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to retrieve view data - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Retrieved view data headers: @@ -86,8 +74,8 @@ paths: application/json: schema: type: string - "409": - description: View schema could not be mapped + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: @@ -98,8 +86,20 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + "409": + description: View schema could not be mapped + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Request pagination is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to retrieve view data content: application/json: schema: @@ -147,18 +147,6 @@ paths: type: string format: date-time responses: - "400": - description: Request pagination is malformed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to retrieve view data - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Retrieved view data headers: @@ -174,8 +162,8 @@ paths: application/json: schema: type: string - "409": - description: View schema could not be mapped + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: @@ -186,8 +174,20 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + "409": + description: View schema could not be mapped + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Request pagination is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to retrieve view data content: application/json: schema: @@ -237,14 +237,14 @@ paths: type: integer format: int64 responses: - "404": - description: Failed to find table in metadata database + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Request pagination or table data select query is malformed + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: @@ -264,14 +264,14 @@ paths: application/json: schema: type: string - "403": - description: Not allowed to get table data + "404": + description: Failed to find table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + "403": + description: Not allowed to get table data content: application/json: schema: @@ -312,32 +312,32 @@ paths: $ref: "#/components/schemas/TupleUpdateDto" required: true responses: - "404": - description: Failed to find table in metadata database + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Request pagination or table data select query is malformed + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Updated table data "403": description: Update table data not allowed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + "404": + description: Failed to find table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Updated table data security: - basicAuth: [] - bearerAuth: [] @@ -374,30 +374,30 @@ paths: $ref: "#/components/schemas/TupleDto" required: true responses: - "503": - description: Failed to establish connection with the metadata service or - storage service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "400": description: Request pagination or table data select query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Create table data not allowed + "404": + description: Failed to find table in metadata database or blob in storage + service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" "201": description: Created table data - "404": - description: Failed to find table in metadata database or blob in storage - service + "503": + description: Failed to establish connection with the metadata service or + storage service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Create table data not allowed content: application/json: schema: @@ -438,28 +438,28 @@ paths: $ref: "#/components/schemas/TupleDeleteDto" required: true responses: - "404": - description: Failed to find table in metadata database + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Request pagination or table data select query is malformed + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Delete table data not allowed + "404": + description: Failed to find table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" "202": description: Deleted table data - "503": - description: Failed to establish connection with the metadata service + "403": + description: Delete table data not allowed content: application/json: schema: @@ -508,14 +508,14 @@ paths: type: integer format: int64 responses: - "404": - description: Failed to find table in metadata database + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Request pagination or table data select query is malformed + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: @@ -535,14 +535,14 @@ paths: application/json: schema: type: string - "403": - description: Not allowed to get table data + "404": + description: Failed to find table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + "403": + description: Not allowed to get table data content: application/json: schema: @@ -586,9 +586,8 @@ paths: type: integer format: int64 responses: - "404": - description: Failed to find database in metadata database or query in query - store of the data database + "503": + description: Failed to communicate with database content: application/json: schema: @@ -599,12 +598,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Invalid pagination - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Retrieved subset data headers: @@ -626,8 +619,15 @@ paths: application/json: schema: type: string - "503": - description: Failed to communicate with database + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Invalid pagination content: application/json: schema: @@ -670,9 +670,8 @@ paths: type: integer format: int64 responses: - "404": - description: Failed to find database in metadata database or query in query - store of the data database + "503": + description: Failed to communicate with database content: application/json: schema: @@ -683,12 +682,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Invalid pagination - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Retrieved subset data headers: @@ -710,8 +703,15 @@ paths: application/json: schema: type: string - "503": - description: Failed to communicate with database + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Invalid pagination content: application/json: schema: @@ -746,13 +746,6 @@ paths: $ref: "#/components/schemas/QueryPersistDto" required: true responses: - "404": - description: Failed to find database in metadata database or query in query - store of the data database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "202": description: Persisted subset content: @@ -765,8 +758,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Malformed select query + "503": + description: Failed to communicate with database content: application/json: schema: @@ -777,8 +770,15 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to communicate with database + "400": + description: Malformed select query + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database or query in query + store of the data database content: application/json: schema: @@ -823,8 +823,8 @@ paths: responses: "202": description: Imported dataset successfully - "403": - description: Import table dataset not allowed + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: @@ -835,14 +835,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Dataset and/or query are malformed + "403": + description: Import table dataset not allowed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + "400": + description: Dataset and/or query are malformed content: application/json: schema: @@ -874,8 +874,16 @@ paths: schema: type: boolean responses: - "403": - description: Not allowed to find subsets + "200": + description: Found subsets + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/QueryDto" + "503": + description: Failed to communicate with database content: application/json: schema: @@ -887,16 +895,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Found subsets - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/QueryDto" - "503": - description: Failed to communicate with database + "403": + description: Not allowed to find subsets content: application/json: schema: @@ -945,33 +945,32 @@ paths: $ref: "#/components/schemas/ExecuteStatementDto" required: true responses: - "404": - description: Failed to find database in metadata database or query in query - store of the data database + "403": + description: Not allowed to find subset content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Malformed select query + "501": + description: Failed to execute query as it contains non-supported keywords content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "501": - description: Failed to execute query as it contains non-supported keywords + "503": + description: Failed to communicate with database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "417": - description: Failed to insert query into query store of data database + "400": + description: Malformed select query content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to communicate with database + "417": + description: Failed to insert query into query store of data database content: application/json: schema: @@ -982,8 +981,9 @@ paths: application/json: schema: type: string - "403": - description: Not allowed to find subset + "404": + description: Failed to find database in metadata database or query in query + store of the data database content: application/json: schema: @@ -1020,12 +1020,6 @@ paths: type: string format: date-time responses: - "403": - description: Export view data not allowed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "400": description: Request pagination or view data select query is malformed content: @@ -1039,6 +1033,12 @@ paths: schema: type: string format: binary + "403": + description: Export view data not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "404": description: Failed to find view in metadata database or export dataset content: @@ -1089,8 +1089,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Find table history not allowed + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find table history in data database content: application/json: schema: @@ -1103,14 +1109,8 @@ paths: type: array items: $ref: "#/components/schemas/TableHistoryDto" - "404": - description: Failed to find table history in data database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + "403": + description: Find table history not allowed content: application/json: schema: @@ -1147,12 +1147,24 @@ paths: type: string format: date-time responses: + "400": + description: Request pagination or table data select query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "403": description: Export table data not allowed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to establish connection with the metadata service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "404": description: Failed to find table in metadata database content: @@ -1166,18 +1178,6 @@ paths: schema: type: string format: binary - "400": - description: Request pagination or table data select query is malformed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - basicAuth: [] - bearerAuth: [] @@ -1217,22 +1217,14 @@ paths: type: string format: date-time responses: - "200": - description: Found subset - content: - application/json: - schema: - $ref: "#/components/schemas/QueryDto" - text/csv: {} - "404": - description: Failed to find database in metadata database or query in query - store of the data database + "403": + description: Not allowed to find subset content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Malformed select query + "406": + description: Failed to find acceptable representation content: application/json: schema: @@ -1243,14 +1235,22 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to find subset + "400": + description: Malformed select query content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "406": - description: Failed to find acceptable representation + "200": + description: Found subset + content: + application/json: + schema: + $ref: "#/components/schemas/QueryDto" + text/csv: {} + "404": + description: Failed to find database in metadata database or query in query + store of the data database content: application/json: schema: @@ -1356,10 +1356,18 @@ components: type: object additionalProperties: type: object + example: + key: value + example: + key: value keys: type: object additionalProperties: type: object + example: + id: 1 + example: + id: 1 QueryPersistDto: required: - persist @@ -1368,13 +1376,58 @@ components: persist: type: boolean example: true + CreatorBriefDto: + required: + - creator_name + - id + type: object + properties: + id: + type: integer + format: int64 + example: 11 + affiliation: + type: string + example: Brown University + creator_name: + type: string + example: "Carberry, Josiah" + name_type: + type: string + example: Personal + enum: + - Personal + - Organizational + name_identifier: + type: string + example: 0000-0002-1825-0097 + name_identifier_scheme: + type: string + example: ORCID + enum: + - ORCID + - ROR + - ISNI + - GRID + affiliation_identifier: + type: string + example: https://ror.org/05gq02987 + affiliation_identifier_scheme: + type: string + example: ROR + enum: + - ROR + - GRID + - ISNI IdentifierBriefDto: required: - - created_by + - creators - database_id - id + - owned_by - publication_year - publisher + - status - titles - type type: object @@ -1382,13 +1435,19 @@ components: id: type: integer format: int64 + example: 2 type: type: string + example: database enum: - database - subset - table - view + creators: + type: array + items: + $ref: "#/components/schemas/CreatorBriefDto" titles: type: array items: @@ -1401,6 +1460,7 @@ components: example: TU Wien status: type: string + example: draft enum: - draft - published @@ -1424,9 +1484,10 @@ components: type: integer format: int32 example: 2022 - created_by: + owned_by: type: string format: uuid + example: 2f45ef7a-7f9b-4667-9156-152c87fe1ca5 IdentifierTitleDto: required: - id @@ -1435,6 +1496,7 @@ components: id: type: integer format: int64 + example: 4 title: type: string example: Airquality Demonstrator @@ -1649,6 +1711,7 @@ components: id: type: integer format: int64 + example: 4 owner: $ref: "#/components/schemas/UserBriefDto" execution: @@ -1671,6 +1734,7 @@ components: database_id: type: integer format: int64 + example: 1 query_normalized: type: string example: SELECT `id` FROM `air_quality` @@ -1725,6 +1789,10 @@ components: type: object additionalProperties: type: object + example: + key: value + example: + key: value ImportDto: required: - header @@ -1739,6 +1807,7 @@ components: type: boolean description: "If true, the first line contains the column names, otherwise\ \ it contains only data" + example: true separator: type: string example: "," @@ -1782,6 +1851,10 @@ components: type: object additionalProperties: type: object + example: + id: 1 + example: + id: 1 securitySchemes: basicAuth: type: http diff --git a/.docs/.openapi/api-metadata.yaml b/.docs/.openapi/api-metadata.yaml index 5578822fb4..2696cecd0c 100644 --- a/.docs/.openapi/api-metadata.yaml +++ b/.docs/.openapi/api-metadata.yaml @@ -64,8 +64,8 @@ paths: $ref: "#/components/schemas/DatabaseCreateDto" required: true responses: - "409": - description: Query store could not be created + "503": + description: Failed to save in search service content: application/json: schema: @@ -95,24 +95,24 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "201": + description: Created a new database content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to fin container/user/database in metadata database + $ref: "#/components/schemas/DatabaseBriefDto" + "409": + description: Query store could not be created content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Created a new database + "404": + description: Failed to fin container/user/database in metadata database content: application/json: schema: - $ref: "#/components/schemas/DatabaseBriefDto" + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -173,12 +173,6 @@ paths: type: string format: uuid responses: - "403": - description: No access to this database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "404": description: Database not found content: @@ -191,6 +185,12 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseAccessDto" + "403": + description: No access to this database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -218,7 +218,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/UpdateDatabaseAccessDto" + $ref: "#/components/schemas/CreateAccessDto" required: true responses: "404": @@ -227,9 +227,9 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Modify access not permitted when no access is granted in the - first place + "502": + description: Access could not be updated due to connection error in the + data service content: application/json: schema: @@ -242,15 +242,15 @@ paths: $ref: "#/components/schemas/ApiErrorDto" "202": description: Modified access - "502": - description: Access could not be updated due to connection error in the - data service + "400": + description: Modify access query or database connection is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Modify access query or database connection is malformed + "403": + description: Modify access not permitted when no access is granted in the + first place content: application/json: schema: @@ -282,41 +282,41 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/UpdateDatabaseAccessDto" + $ref: "#/components/schemas/CreateAccessDto" required: true responses: - "404": - description: Database or user not found + "400": + description: Granting access query or database connection is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Access could not be created in the data service + "403": + description: Failed giving access content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Failed giving access + "404": + description: Database or user not found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Granting access succeeded + "502": + description: Access could not be created due to connection error content: application/json: schema: - $ref: "#/components/schemas/DatabaseAccessDto" - "400": - description: Granting access query or database connection is malformed + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Granting access succeeded content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Access could not be created due to connection error + $ref: "#/components/schemas/DatabaseAccessDto" + "503": + description: Access could not be created in the data service content: application/json: schema: @@ -345,6 +345,12 @@ paths: type: string format: uuid responses: + "503": + description: Access could not be revoked in the data service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "403": description: Revoke of access not permitted as no access was found content: @@ -365,12 +371,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Access could not be revoked in the data service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "400": description: Modify access query or database connection is malformed content: @@ -404,12 +404,6 @@ paths: type: string format: uuid responses: - "403": - description: No access to this database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "404": description: Database not found content: @@ -422,6 +416,12 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseAccessDto" + "403": + description: No access to this database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -443,6 +443,12 @@ paths: type: string format: uuid responses: + "403": + description: Find user is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "404": description: User was not found content: @@ -455,12 +461,6 @@ paths: application/json: schema: $ref: "#/components/schemas/UserDto" - "403": - description: Find user is not permitted - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -484,14 +484,8 @@ paths: $ref: "#/components/schemas/UserUpdateDto" required: true responses: - "202": - description: Modified user information - content: - application/json: - schema: - $ref: "#/components/schemas/UserDto" - "404": - description: Failed to find database/user in metadata database + "403": + description: Not allowed to modify user metadata content: application/json: schema: @@ -502,8 +496,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to modify user metadata + "202": + description: Modified user information + content: + application/json: + schema: + $ref: "#/components/schemas/UserDto" + "404": + description: Failed to find database/user in metadata database content: application/json: schema: @@ -540,14 +540,14 @@ paths: $ref: "#/components/schemas/ApiErrorDto" "202": description: Modified user password - "403": - description: Not allowed to change foreign user password + "502": + description: Connection to auth service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to get user in auth service + "403": + description: Not allowed to change foreign user password content: application/json: schema: @@ -558,8 +558,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to auth service failed + "503": + description: Failed to get user in auth service content: application/json: schema: @@ -581,24 +581,24 @@ paths: $ref: "#/components/schemas/RefreshTokenRequestDto" required: true responses: - "202": - description: Refreshed user token + "400": + description: Invalid refresh token content: application/json: schema: - $ref: "#/components/schemas/TokenDto" + $ref: "#/components/schemas/ApiErrorDto" "403": description: Not allowed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Invalid refresh token + "202": + description: Refreshed user token content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" + $ref: "#/components/schemas/TokenDto" "502": description: Connection to auth service failed content: @@ -618,8 +618,9 @@ paths: $ref: "#/components/schemas/LoginRequestDto" required: true responses: - "400": - description: Invalid login request + "428": + description: Account is not fully setup in auth service (requires password + change?) content: application/json: schema: @@ -630,33 +631,32 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find user in auth database + "202": + description: Obtained user token content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to get user in auth service + $ref: "#/components/schemas/TokenDto" + "404": + description: Failed to find user in auth database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Obtained user token + "502": + description: Connection to auth service failed content: application/json: schema: - $ref: "#/components/schemas/TokenDto" - "428": - description: Account is not fully setup in auth service (requires password - change?) + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Invalid login request content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to auth service failed + "503": + description: Failed to get user in auth service content: application/json: schema: @@ -737,16 +737,16 @@ paths: type: integer format: int64 responses: - "202": - description: Deleted ontology successfully - content: - application/json: {} "404": description: Could not find ontology content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted ontology successfully + content: + application/json: {} security: - bearerAuth: [] - basicAuth: [] @@ -828,18 +828,18 @@ paths: type: integer format: int64 responses: - "404": - description: Image could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Found image content: application/json: schema: $ref: "#/components/schemas/ImageDto" + "404": + description: Image could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" put: tags: - image-endpoint @@ -891,14 +891,14 @@ paths: type: integer format: int64 responses: - "202": - description: Deleted image successfully "404": description: Image could not be found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted image successfully security: - bearerAuth: [] - basicAuth: [] @@ -923,27 +923,6 @@ paths: schema: type: string responses: - "200": - description: Found identifier successfully - content: - application/json: - schema: - $ref: "#/components/schemas/IdentifierDto" - application/ld+json: - schema: - $ref: "#/components/schemas/LdDatasetDto" - text/csv: {} - text/xml: {} - text/bibliography: {} - text/bibliography; style=apa: {} - text/bibliography; style=ieee: {} - text/bibliography; style=bibtex: {} - "502": - description: Connection to data service failed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "400": description: "Identifier could not be exported, the requested style is not\ \ known" @@ -957,39 +936,60 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "410": - description: Failed to retrieve from S3 endpoint + "406": + description: Failed to find acceptable representation content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Identifier could not be found + "502": + description: Connection to data service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "406": - description: Failed to find acceptable representation + "503": + description: Failed to find in data service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to find in data service + "404": + description: Identifier could not be found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - put: - tags: - - identifier-endpoint - summary: Save identifier - description: Saves an identifier with id as a draft identifier. Identifiers - can only be created for objects the user has at least *READ* access in the - associated database (requires role `create-identifier`) or for any object - in any database (requires role `create-foreign-identifier`). - operationId: save + "200": + description: Found identifier successfully + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" + application/ld+json: + schema: + $ref: "#/components/schemas/LdDatasetDto" + text/csv: {} + text/xml: {} + text/bibliography: {} + text/bibliography; style=apa: {} + text/bibliography; style=ieee: {} + text/bibliography; style=bibtex: {} + "410": + description: Failed to retrieve from S3 endpoint + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + put: + tags: + - identifier-endpoint + summary: Save identifier + description: Saves an identifier with id as a draft identifier. Identifiers + can only be created for objects the user has at least *READ* access in the + associated database (requires role `create-identifier`) or for any object + in any database (requires role `create-foreign-identifier`). + operationId: save parameters: - name: identifierId in: path @@ -1004,38 +1004,38 @@ paths: $ref: "#/components/schemas/IdentifierSaveDto" required: true responses: - "502": - description: Connection to search service failed + "202": + description: Saved identifier content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Failed to find database, table or view" + $ref: "#/components/schemas/IdentifierDto" + "403": + description: Insufficient access rights or authorities content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Identifier form contains invalid request data + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Saved identifier + "404": + description: "Failed to find database, table or view" content: application/json: schema: - $ref: "#/components/schemas/IdentifierDto" - "403": - description: Insufficient access rights or authorities + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Identifier form contains invalid request data content: application/json: schema: @@ -1069,16 +1069,16 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Identifier or database could not be found + "403": + description: Deleting identifier not permitted content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" "202": description: Deleted identifier - "403": - description: Deleting identifier not permitted + "404": + description: Identifier or database could not be found content: application/json: schema: @@ -1102,38 +1102,38 @@ paths: type: integer format: int64 responses: - "502": - description: Connection to search service failed + "202": + description: Published identifier content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Failed to find database, table or view" + $ref: "#/components/schemas/IdentifierDto" + "403": + description: Insufficient access rights or authorities content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Published identifier + "503": + description: Failed to save in search service content: application/json: schema: - $ref: "#/components/schemas/IdentifierDto" - "400": - description: Identifier form contains invalid request data + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "404": + description: "Failed to find database, table or view" content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Insufficient access rights or authorities + "400": + description: Identifier form contains invalid request data content: application/json: schema: @@ -1163,32 +1163,32 @@ paths: $ref: "#/components/schemas/DatabaseModifyVisibilityDto" required: true responses: - "502": - description: Connection to search service failed + "202": + description: Visibility modified successfully content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database in metadata database + $ref: "#/components/schemas/DatabaseBriefDto" + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Visibility modified successfully + "502": + description: Connection to search service failed content: application/json: schema: - $ref: "#/components/schemas/DatabaseBriefDto" + $ref: "#/components/schemas/ApiErrorDto" "400": description: The visibility payload is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "404": + description: Failed to find database in metadata database content: application/json: schema: @@ -1223,12 +1223,6 @@ paths: type: integer format: int64 responses: - "403": - description: Find view is not permitted - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "404": description: "Database, view or user could not be found" content: @@ -1266,6 +1260,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ViewDto" + "403": + description: Find view is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1296,18 +1296,6 @@ paths: $ref: "#/components/schemas/ViewUpdateDto" required: true responses: - "502": - description: Connection to search service failed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Database or View could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "403": description: Update not allowed content: @@ -1320,8 +1308,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Update view query is malformed + "502": + description: Connection to search service failed content: application/json: schema: @@ -1332,6 +1320,18 @@ paths: '*/*': schema: $ref: "#/components/schemas/ViewDto" + "400": + description: Update view query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database or View could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1355,20 +1355,20 @@ paths: type: integer format: int64 responses: - "502": - description: Connection to search service failed + "400": + description: Delete view query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "423": - description: Delete view resulted in an invalid query statement + "403": + description: Deletion not allowed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Database, view or user could not be found" + "503": + description: Failed to save in search service content: application/json: schema: @@ -1379,20 +1379,20 @@ paths: '*/*': schema: type: object - "400": - description: Delete view query is malformed + "423": + description: Delete view resulted in an invalid query statement content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Deletion not allowed + "404": + description: "Database, view or user could not be found" content: application/json: schema: @@ -1455,14 +1455,14 @@ paths: application/json: schema: $ref: "#/components/schemas/TableDto" - "503": - description: Failed to obtain queue information from broker service + "404": + description: "Table, database or container could not be found" content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Access to the database is forbidden + "503": + description: Failed to obtain queue information from broker service content: application/json: schema: @@ -1473,8 +1473,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Table, database or container could not be found" + "403": + description: Access to the database is forbidden content: application/json: schema: @@ -1508,8 +1508,8 @@ paths: $ref: "#/components/schemas/TableUpdateDto" required: true responses: - "403": - description: Update table visibility not permitted + "503": + description: Failed to save in search service content: application/json: schema: @@ -1520,26 +1520,26 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Table could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "202": description: Updated the table content: application/json: schema: $ref: "#/components/schemas/TableBriefDto" - "503": - description: Failed to save in search service + "400": + description: Update table visibility payload is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Update table visibility payload is malformed + "403": + description: Update table visibility not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Table could not be found content: application/json: schema: @@ -1569,34 +1569,34 @@ paths: type: integer format: int64 responses: - "502": - description: Connection to search service failed + "404": + description: "Table, database or container could not be found" content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Access to the database is forbidden + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Delete table successfully - "503": - description: Failed to save in search service + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Delete table successfully "400": description: Delete table query resulted in an invalid query statement content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Table, database or container could not be found" + "403": + description: Access to the database is forbidden content: application/json: schema: @@ -1627,32 +1627,32 @@ paths: type: integer format: int64 responses: - "502": - description: Connection to search service failed + "403": + description: Not the owner content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "404": + description: Failed to find database/table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Failed to map column statistic to known columns + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database/table in metadata database + "400": + description: Failed to map column statistic to known columns content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not the owner + "502": + description: Connection to search service failed content: application/json: schema: @@ -1698,26 +1698,27 @@ paths: $ref: "#/components/schemas/ColumnSemanticsUpdateDto" required: true responses: - "502": - description: Connection to search service failed + "404": + description: Failed to find user/table/database/ontology in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Access to the database is forbidden + "400": + description: Update semantic concept query is malformed or update unit of + measurement query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find user/table/database/ontology in metadata database + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "502": + description: Connection to search service failed content: application/json: schema: @@ -1728,9 +1729,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ColumnDto" - "400": - description: Update semantic concept query is malformed or update unit of - measurement query is malformed + "403": + description: Access to the database is forbidden content: application/json: schema: @@ -1760,14 +1760,20 @@ paths: $ref: "#/components/schemas/DatabaseTransferDto" required: true responses: + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "502": description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "403": + description: Transfer of ownership is not permitted content: application/json: schema: @@ -1778,12 +1784,6 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseBriefDto" - "403": - description: Transfer of ownership is not permitted - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "404": description: Database or user could not be found content: @@ -1816,36 +1816,36 @@ paths: type: integer format: int64 responses: - "502": - description: Connection to search service failed + "403": + description: Refresh view metadata is not permitted content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database in metadata database + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Refreshed database views metadata + "404": + description: Failed to find database in metadata database content: application/json: schema: - $ref: "#/components/schemas/DatabaseBriefDto" - "403": - description: Refresh view metadata is not permitted + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Refreshed database views metadata content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" + $ref: "#/components/schemas/DatabaseBriefDto" security: - bearerAuth: [] - basicAuth: [] @@ -1866,8 +1866,8 @@ paths: type: integer format: int64 responses: - "403": - description: Not allowed to refresh table metadata + "503": + description: Failed to save in search service content: application/json: schema: @@ -1878,14 +1878,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to fin user/database in metadata database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "400": + description: Failed to parse payload at search service content: application/json: schema: @@ -1896,8 +1890,14 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseBriefDto" - "400": - description: Failed to parse payload at search service + "403": + description: Not allowed to refresh table metadata + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to fin user/database in metadata database content: application/json: schema: @@ -1920,12 +1920,6 @@ paths: type: integer format: int64 responses: - "404": - description: Database or user could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: View of image was successful content: @@ -1935,6 +1929,12 @@ paths: items: type: string format: byte + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1959,38 +1959,38 @@ paths: $ref: "#/components/schemas/DatabaseModifyImageDto" required: true responses: - "502": - description: Connection to search service failed + "404": + description: Database could not be found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Modify of image was successful + "503": + description: Failed to save in search service content: application/json: schema: - $ref: "#/components/schemas/DatabaseBriefDto" - "410": - description: File was not found in the Storage Service + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Database could not be found + "403": + description: Modify of image is not permitted content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "202": + description: Modify of image was successful content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Modify of image is not permitted + $ref: "#/components/schemas/DatabaseBriefDto" + "410": + description: File was not found in the Storage Service content: application/json: schema: @@ -2036,30 +2036,32 @@ paths: $ref: "#/components/schemas/SignupRequestDto" required: true responses: - "403": - description: Internal authentication to the auth service is invalid + "502": + description: Failed to create in auth service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Parameters are not well-formed (likely email) - content: - application/json: {} "409": description: User with username already exists content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "417": - description: User with e-mail already exists + "404": + description: Default role not found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to create in auth service + "403": + description: Internal authentication to the auth service is invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "417": + description: User with e-mail already exists content: application/json: schema: @@ -2070,18 +2072,16 @@ paths: application/json: schema: $ref: "#/components/schemas/UserDto" - "502": + "503": description: Failed to create in auth service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Default role not found + "400": + description: Parameters are not well-formed (likely email) content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" + application/json: {} /api/ontology: get: tags: @@ -2204,18 +2204,18 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Image specification is invalid - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "201": description: Created image content: application/json: schema: $ref: "#/components/schemas/ImageDto" + "400": + description: Image specification is invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2257,6 +2257,13 @@ paths: schema: type: string responses: + "406": + description: "Identifier could not be exported, the requested style is not\ + \ known" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Found identifiers successfully content: @@ -2270,13 +2277,6 @@ paths: type: array items: $ref: "#/components/schemas/LdDatasetDto" - "406": - description: "Identifier could not be exported, the requested style is not\ - \ known" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" post: tags: - identifier-endpoint @@ -2293,42 +2293,42 @@ paths: $ref: "#/components/schemas/IdentifierCreateDto" required: true responses: - "502": - description: Connection to search service failed + "403": + description: Insufficient access rights or authorities content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Drafted identifier + "503": + description: Failed to save in search service content: application/json: schema: - $ref: "#/components/schemas/IdentifierDto" - "404": - description: "Failed to find database, table or view" + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Identifier form contains invalid request data + "404": + description: "Failed to find database, table or view" content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "400": + description: Identifier form contains invalid request data content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Insufficient access rights or authorities + "201": + description: Drafted identifier content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" + $ref: "#/components/schemas/IdentifierDto" security: - bearerAuth: [] - basicAuth: [] @@ -2385,14 +2385,8 @@ paths: $ref: "#/components/schemas/ViewCreateDto" required: true responses: - "423": - description: Create view resulted in an invalid query statement - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "503": + description: Failed to save in search service content: application/json: schema: @@ -2403,14 +2397,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ViewBriefDto" - "503": - description: Failed to save in search service + "423": + description: Create view resulted in an invalid query statement content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Credentials missing + "502": + description: Connection to search service failed content: application/json: schema: @@ -2427,6 +2421,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Credentials missing + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2447,12 +2447,6 @@ paths: type: integer format: int64 responses: - "403": - description: List tables not permitted - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "404": description: Database could not be found content: @@ -2467,6 +2461,12 @@ paths: type: array items: $ref: "#/components/schemas/TableBriefDto" + "403": + description: List tables not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2490,42 +2490,42 @@ paths: $ref: "#/components/schemas/TableCreateDto" required: true responses: - "502": - description: Connection to search service failed + "404": + description: "Database, container or user could not be found" content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Create table not permitted + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Created a new table + "403": + description: Create table not permitted content: application/json: schema: - $ref: "#/components/schemas/TableBriefDto" - "409": - description: Create table conflicts with existing table name + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Database, container or user could not be found" + "409": + description: Create table conflicts with existing table name content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "201": + description: Created a new table content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" + $ref: "#/components/schemas/TableBriefDto" "400": description: Create table query is malformed content: @@ -2568,7 +2568,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/ContainerCreateDto" + $ref: "#/components/schemas/CreateContainerDto" required: true responses: "400": @@ -2577,8 +2577,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Container image or user could not be found + "201": + description: Created a new container + content: + application/json: + schema: + $ref: "#/components/schemas/ContainerDto" + "403": + description: "Create container not permitted, need authority `create-container`" content: application/json: schema: @@ -2589,14 +2595,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Created a new container - content: - application/json: - schema: - $ref: "#/components/schemas/ContainerDto" - "403": - description: "Create container not permitted, need authority `create-container`" + "404": + description: Container image or user could not be found content: application/json: schema: @@ -2646,20 +2646,6 @@ paths: schema: type: string responses: - "400": - description: Filter params are invalid - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Found entities - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/EntityDto" "404": description: Could not find ontology content: @@ -2678,6 +2664,20 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Filter params are invalid + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found entities + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/EntityDto" security: - bearerAuth: [] - basicAuth: [] @@ -2785,14 +2785,20 @@ paths: type: integer format: int64 responses: - "503": - description: Failed to find queue information in broker service + "403": + description: Not allowed to view database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to the broker service could not be established + "404": + description: "Database, user or exchange could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to find queue information in broker service content: application/json: schema: @@ -2813,14 +2819,8 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseBriefDto" - "403": - description: Not allowed to view database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Database, user or exchange could not be found" + "502": + description: Connection to the broker service could not be established content: application/json: schema: @@ -2850,20 +2850,22 @@ paths: type: integer format: int64 responses: - "417": - description: Generated query is malformed + "404": + description: Failed to find database/table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "422": - description: Ontology does not have rdf or sparql endpoint + "200": + description: Suggested table semantics successfully content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Failed to parse statistic in search service + type: array + items: + $ref: "#/components/schemas/EntityDto" + "422": + description: Ontology does not have rdf or sparql endpoint content: application/json: schema: @@ -2874,20 +2876,18 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database/table in metadata database + "417": + description: Generated query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Suggested table semantics successfully + "400": + description: Failed to parse statistic in search service content: application/json: schema: - type: array - items: - $ref: "#/components/schemas/EntityDto" + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2918,8 +2918,8 @@ paths: type: integer format: int64 responses: - "400": - description: Generated query is malformed + "404": + description: Failed to find database/table in metadata database content: application/json: schema: @@ -2930,6 +2930,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Generated query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Suggested table column semantics successfully content: @@ -2938,12 +2944,6 @@ paths: type: array items: $ref: "#/components/schemas/TableColumnEntityDto" - "404": - description: Failed to find database/table in metadata database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -3023,10 +3023,54 @@ paths: $ref: "#/components/schemas/ConceptDto" components: schemas: + CreatorBriefDto: + required: + - creator_name + - id + type: object + properties: + id: + type: integer + format: int64 + example: 11 + affiliation: + type: string + example: Brown University + creator_name: + type: string + example: "Carberry, Josiah" + name_type: + type: string + example: Personal + enum: + - Personal + - Organizational + name_identifier: + type: string + example: 0000-0002-1825-0097 + name_identifier_scheme: + type: string + example: ORCID + enum: + - ORCID + - ROR + - ISNI + - GRID + affiliation_identifier: + type: string + example: https://ror.org/05gq02987 + affiliation_identifier_scheme: + type: string + example: ROR + enum: + - ROR + - GRID + - ISNI DatabaseBriefDto: required: - contact - id + - identifiers - internal_name - is_public - is_schema_public @@ -3037,6 +3081,7 @@ components: id: type: integer format: int64 + example: 3 name: type: string example: Air Quality @@ -3061,15 +3106,18 @@ components: owner_id: type: string format: uuid + example: 2f45ef7a-7f9b-4667-9156-152c87fe1ca5 preview_image: type: string IdentifierBriefDto: required: - - created_by + - creators - database_id - id + - owned_by - publication_year - publisher + - status - titles - type type: object @@ -3077,13 +3125,19 @@ components: id: type: integer format: int64 + example: 2 type: type: string + example: database enum: - database - subset - table - view + creators: + type: array + items: + $ref: "#/components/schemas/CreatorBriefDto" titles: type: array items: @@ -3096,6 +3150,7 @@ components: example: TU Wien status: type: string + example: draft enum: - draft - published @@ -3119,9 +3174,10 @@ components: type: integer format: int32 example: 2022 - created_by: + owned_by: type: string format: uuid + example: 2f45ef7a-7f9b-4667-9156-152c87fe1ca5 IdentifierTitleDto: required: - id @@ -3130,6 +3186,7 @@ components: id: type: integer format: int64 + example: 4 title: type: string example: Airquality Demonstrator @@ -3453,6 +3510,7 @@ components: $ref: "#/components/schemas/UserBriefDto" type: type: string + example: read enum: - read - write_own @@ -3505,6 +3563,22 @@ components: - id type: object properties: + jdbcMethod: + type: string + example: mariadb + host: + type: string + example: data-db + port: + type: integer + format: int32 + example: 3306 + username: + type: string + example: username + database: + type: string + example: air_quality id: type: string format: uuid @@ -3778,6 +3852,7 @@ components: id: type: integer format: int64 + example: 1 registry: type: string example: docker.io/library @@ -4700,6 +4775,7 @@ components: id: type: integer format: int64 + example: 11 firstname: type: string example: Josiah @@ -4753,6 +4829,7 @@ components: id: type: integer format: int64 + example: 3 description: type: string example: "Air quality reports at Stephansplatz, Vienna" @@ -4958,13 +5035,18 @@ components: required: - creators - database_id + - descriptions + - funders - id + - language + - licenses - owner - publication_year - publisher - query - query_hash - query_normalized + - status - titles - type type: object @@ -4972,8 +5054,10 @@ components: id: type: integer format: int64 + example: 2 type: type: string + example: database enum: - database - subset @@ -5204,6 +5288,7 @@ components: $ref: "#/components/schemas/CreatorDto" status: type: string + example: draft enum: - draft - published @@ -5262,6 +5347,7 @@ components: id: type: integer format: int64 + example: 2 funder_name: type: string example: European Commission @@ -5297,6 +5383,7 @@ components: id: type: integer format: int64 + example: 8 value: type: string example: 10.70124/dc4zh-9ce78 @@ -5386,7 +5473,6 @@ components: example: true ViewColumnDto: required: - - auto_generated - database_id - id - internal_name @@ -5399,11 +5485,12 @@ components: id: type: integer format: int64 + example: 12 name: maxLength: 64 minLength: 0 type: string - example: Date + example: Given Name size: type: integer format: int64 @@ -5420,6 +5507,7 @@ components: database_id: type: integer format: int64 + example: 1 ord: type: integer format: int32 @@ -5428,19 +5516,18 @@ components: maxLength: 64 minLength: 0 type: string - example: mdb_date - auto_generated: - type: boolean - example: false + example: given_name index_length: type: integer format: int64 + example: 255 length: type: integer format: int64 + example: 255 type: type: string - example: string + example: varchar enum: - char - varchar @@ -5480,6 +5567,7 @@ components: - columns - database_id - id + - identifiers - internal_name - name - owner @@ -5487,9 +5575,26 @@ components: - query_hash type: object properties: + jdbcMethod: + type: string + example: mariadb + host: + type: string + example: data-db + port: + type: integer + format: int32 + example: 3306 + username: + type: string + example: username + database: + type: string + example: air_quality id: type: integer format: int64 + example: 4 name: type: string example: Air Quality @@ -5512,6 +5617,7 @@ components: database_id: type: integer format: int64 + example: 1 internal_name: type: string example: air_quality @@ -5560,6 +5666,7 @@ components: id: type: integer format: int64 + example: 3 name: type: string example: Air Quality @@ -5569,6 +5676,7 @@ components: database_id: type: integer format: int64 + example: 2 internal_name: type: string example: air_quality @@ -5584,6 +5692,7 @@ components: owned_by: type: string format: uuid + example: 78337b80-5699-45db-8111-cec86439ab6b ColumnSemanticsUpdateDto: type: object properties: @@ -5640,12 +5749,18 @@ components: example: Column comment enums: type: array + example: + - val1 items: type: string + example: "[\"val1\"]" sets: type: array + example: + - val1 items: type: string + example: "[\"val1\"]" database_id: type: integer format: int64 @@ -5781,13 +5896,14 @@ components: properties: key: type: string - UpdateDatabaseAccessDto: + CreateAccessDto: required: - type type: object properties: type: type: string + example: read enum: - read - write_own @@ -6213,6 +6329,7 @@ components: id: type: integer format: int64 + example: 4 name: type: string example: Air Quality @@ -6222,6 +6339,7 @@ components: database_id: type: integer format: int64 + example: 1 internal_name: type: string example: air_quality @@ -6241,6 +6359,7 @@ components: owned_by: type: string format: uuid + example: ac750fcf-ea02-4fce-85ac-d73857e18b35 ColumnCreateDto: required: - name @@ -6412,7 +6531,7 @@ components: is_schema_public: type: boolean example: true - ContainerCreateDto: + CreateContainerDto: required: - host - image_id @@ -6428,10 +6547,12 @@ components: host: type: string description: Hostname of container + example: data-db2 port: type: integer description: Port of container format: int32 + example: 3306 quota: type: integer format: int64 @@ -6440,11 +6561,14 @@ components: type: integer description: Image ID format: int64 + example: 1 ui_host: type: string + example: example.com ui_port: type: integer format: int32 + example: 3306 privileged_username: type: string description: Username of privileged user @@ -6452,6 +6576,7 @@ components: privileged_password: type: string description: Password of privileged user + example: dbrepo ContainerDto: required: - count @@ -6462,9 +6587,26 @@ components: - quota type: object properties: + jdbcMethod: + type: string + example: mariadb + host: + type: string + example: data-db + port: + type: integer + format: int32 + example: 3306 + username: + type: string + example: username + database: + type: string + example: air_quality id: type: integer format: int64 + example: 4 name: type: string example: Air Quality @@ -6483,42 +6625,52 @@ components: format: date-time internal_name: type: string - example: data-db + example: air_quality ui_host: type: string + example: example.com ui_port: type: integer format: int32 + example: 3306 ColumnBriefDto: required: - - column_type - database_id - id - internal_name - name - table_id + - type type: object properties: id: type: integer format: int64 + example: 1 name: + maxLength: 64 + minLength: 0 type: string - example: date + example: Given Name alias: type: string + example: firstname database_id: type: integer format: int64 + example: 2 table_id: type: integer format: int64 + example: 3 internal_name: + maxLength: 64 + minLength: 0 type: string - example: mdb_date - column_type: + example: given_name + type: type: string - example: date + example: varchar enum: - char - varchar @@ -6678,6 +6830,7 @@ components: id: type: integer format: int64 + example: 5 name: type: string example: mariadb @@ -6762,8 +6915,11 @@ components: checks: uniqueItems: true type: array + example: + - value > 1 items: type: string + example: "[\"value > 1\"]" foreign_keys: type: array items: @@ -6779,6 +6935,7 @@ components: id: type: integer format: int64 + example: 8 ForeignKeyDto: required: - name @@ -6790,8 +6947,10 @@ components: id: type: integer format: int64 + example: 4 name: type: string + example: fk_name references: type: array items: @@ -6802,6 +6961,7 @@ components: $ref: "#/components/schemas/TableBriefDto" on_update: type: string + example: restrict enum: - restrict - cascade @@ -6810,6 +6970,7 @@ components: - set_default on_delete: type: string + example: restrict enum: - restrict - cascade @@ -6826,6 +6987,7 @@ components: id: type: integer format: int64 + example: 8 column: $ref: "#/components/schemas/ColumnBriefDto" foreign_key: @@ -6841,6 +7003,7 @@ components: id: type: integer format: int64 + example: 8 table: $ref: "#/components/schemas/TableBriefDto" column: @@ -6861,14 +7024,32 @@ components: - routing_key type: object properties: + jdbcMethod: + type: string + example: mariadb + host: + type: string + example: data-db + port: + type: integer + format: int32 + example: 3306 + username: + type: string + example: username + database: + type: string + example: air_quality id: type: integer format: int64 + example: 3 name: type: string example: Air Quality alias: type: string + example: a identifiers: type: array items: @@ -6892,6 +7073,7 @@ components: database_id: type: integer format: int64 + example: 2 internal_name: type: string example: air_quality @@ -6943,14 +7125,16 @@ components: id: type: integer format: int64 + example: 5 name: type: string + example: uk_name table: $ref: "#/components/schemas/TableBriefDto" columns: type: array items: - $ref: "#/components/schemas/ColumnDto" + $ref: "#/components/schemas/ColumnBriefDto" TableColumnEntityDto: required: - column_id @@ -6994,6 +7178,7 @@ components: id: type: integer format: int64 + example: 4 hash: type: string example: f829dd8a884182d0da846f365dee1221fd16610a14c81b8f9f295ff162749e50 diff --git a/.docs/.openapi/api.yaml b/.docs/.openapi/api.yaml index ef55c74ae9..3338c9d6a2 100644 --- a/.docs/.openapi/api.yaml +++ b/.docs/.openapi/api.yaml @@ -1617,7 +1617,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateDatabaseAccessDto' + $ref: '#/components/schemas/CreateAccessDto' required: true responses: '202': @@ -1684,7 +1684,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateDatabaseAccessDto' + $ref: '#/components/schemas/CreateAccessDto' required: true responses: '202': @@ -4002,7 +4002,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ContainerCreateDto' + $ref: '#/components/schemas/CreateContainerDto' required: true responses: '201': @@ -4806,10 +4806,18 @@ components: type: object additionalProperties: type: object + example: + key: value + example: + key: value keys: type: object additionalProperties: type: object + example: + id: 1 + example: + id: 1 QueryPersistDto: required: - persist @@ -4818,13 +4826,58 @@ components: persist: type: boolean example: true + CreatorBriefDto: + required: + - creator_name + - id + type: object + properties: + id: + type: integer + format: int64 + example: 11 + affiliation: + type: string + example: Brown University + creator_name: + type: string + example: 'Carberry, Josiah' + name_type: + type: string + example: Personal + enum: + - Personal + - Organizational + name_identifier: + type: string + example: 0000-0002-1825-0097 + name_identifier_scheme: + type: string + example: ORCID + enum: + - ORCID + - ROR + - ISNI + - GRID + affiliation_identifier: + type: string + example: 'https://ror.org/05gq02987' + affiliation_identifier_scheme: + type: string + example: ROR + enum: + - ROR + - GRID + - ISNI IdentifierBriefDto: required: - - created_by + - creators - database_id - id + - owned_by - publication_year - publisher + - status - titles - type type: object @@ -4832,13 +4885,19 @@ components: id: type: integer format: int64 + example: 2 type: type: string + example: database enum: - database - subset - table - view + creators: + type: array + items: + $ref: '#/components/schemas/CreatorBriefDto' titles: type: array items: @@ -4851,6 +4910,7 @@ components: example: TU Wien status: type: string + example: draft enum: - draft - published @@ -4874,9 +4934,10 @@ components: type: integer format: int32 example: 2022 - created_by: + owned_by: type: string format: uuid + example: 2f45ef7a-7f9b-4667-9156-152c87fe1ca5 IdentifierTitleDto: required: - id @@ -4885,6 +4946,7 @@ components: id: type: integer format: int64 + example: 4 title: type: string example: Airquality Demonstrator @@ -5099,6 +5161,7 @@ components: id: type: integer format: int64 + example: 4 owner: $ref: '#/components/schemas/UserBriefDto' execution: @@ -5121,6 +5184,7 @@ components: database_id: type: integer format: int64 + example: 1 query_normalized: type: string example: SELECT `id` FROM `air_quality` @@ -5175,6 +5239,10 @@ components: type: object additionalProperties: type: object + example: + key: value + example: + key: value ImportDto: required: - header @@ -5190,6 +5258,7 @@ components: description: >- If true, the first line contains the column names, otherwise it contains only data + example: true separator: type: string example: ',' @@ -5233,10 +5302,15 @@ components: type: object additionalProperties: type: object + example: + id: 1 + example: + id: 1 DatabaseBriefDto: required: - contact - id + - identifiers - internal_name - is_public - is_schema_public @@ -5247,6 +5321,7 @@ components: id: type: integer format: int64 + example: 3 name: type: string example: Air Quality @@ -5271,6 +5346,7 @@ components: owner_id: type: string format: uuid + example: 2f45ef7a-7f9b-4667-9156-152c87fe1ca5 preview_image: type: string DatabaseAccessDto: @@ -5283,6 +5359,7 @@ components: $ref: '#/components/schemas/UserBriefDto' type: type: string + example: read enum: - read - write_own @@ -5335,6 +5412,22 @@ components: - id type: object properties: + jdbcMethod: + type: string + example: mariadb + host: + type: string + example: data-db + port: + type: integer + format: int32 + example: 3306 + username: + type: string + example: username + database: + type: string + example: air_quality id: type: string format: uuid @@ -5608,6 +5701,7 @@ components: id: type: integer format: int64 + example: 1 registry: type: string example: docker.io/library @@ -6531,6 +6625,7 @@ components: id: type: integer format: int64 + example: 11 firstname: type: string example: Josiah @@ -6584,6 +6679,7 @@ components: id: type: integer format: int64 + example: 3 description: type: string example: 'Air quality reports at Stephansplatz, Vienna' @@ -6789,13 +6885,18 @@ components: required: - creators - database_id + - descriptions + - funders - id + - language + - licenses - owner - publication_year - publisher - query - query_hash - query_normalized + - status - titles - type type: object @@ -6803,8 +6904,10 @@ components: id: type: integer format: int64 + example: 2 type: type: string + example: database enum: - database - subset @@ -7036,6 +7139,7 @@ components: $ref: '#/components/schemas/CreatorDto' status: type: string + example: draft enum: - draft - published @@ -7095,6 +7199,7 @@ components: id: type: integer format: int64 + example: 2 funder_name: type: string example: European Commission @@ -7130,6 +7235,7 @@ components: id: type: integer format: int64 + example: 8 value: type: string example: 10.70124/dc4zh-9ce78 @@ -7219,7 +7325,6 @@ components: example: true ViewColumnDto: required: - - auto_generated - database_id - id - internal_name @@ -7232,11 +7337,12 @@ components: id: type: integer format: int64 + example: 12 name: maxLength: 64 minLength: 0 type: string - example: Date + example: Given Name size: type: integer format: int64 @@ -7253,6 +7359,7 @@ components: database_id: type: integer format: int64 + example: 1 ord: type: integer format: int32 @@ -7261,19 +7368,18 @@ components: maxLength: 64 minLength: 0 type: string - example: mdb_date - auto_generated: - type: boolean - example: false + example: given_name index_length: type: integer format: int64 + example: 255 length: type: integer format: int64 + example: 255 type: type: string - example: string + example: varchar enum: - char - varchar @@ -7313,6 +7419,7 @@ components: - columns - database_id - id + - identifiers - internal_name - name - owner @@ -7320,9 +7427,26 @@ components: - query_hash type: object properties: + jdbcMethod: + type: string + example: mariadb + host: + type: string + example: data-db + port: + type: integer + format: int32 + example: 3306 + username: + type: string + example: username + database: + type: string + example: air_quality id: type: integer format: int64 + example: 4 name: type: string example: Air Quality @@ -7345,6 +7469,7 @@ components: database_id: type: integer format: int64 + example: 1 internal_name: type: string example: air_quality @@ -7393,6 +7518,7 @@ components: id: type: integer format: int64 + example: 3 name: type: string example: Air Quality @@ -7402,6 +7528,7 @@ components: database_id: type: integer format: int64 + example: 2 internal_name: type: string example: air_quality @@ -7417,6 +7544,7 @@ components: owned_by: type: string format: uuid + example: 78337b80-5699-45db-8111-cec86439ab6b ColumnSemanticsUpdateDto: type: object properties: @@ -7473,12 +7601,18 @@ components: example: Column comment enums: type: array + example: + - val1 items: type: string + example: '["val1"]' sets: type: array + example: + - val1 items: type: string + example: '["val1"]' database_id: type: integer format: int64 @@ -7615,13 +7749,14 @@ components: properties: key: type: string - UpdateDatabaseAccessDto: + CreateAccessDto: required: - type type: object properties: type: type: string + example: read enum: - read - write_own @@ -8047,6 +8182,7 @@ components: id: type: integer format: int64 + example: 4 name: type: string example: Air Quality @@ -8056,6 +8192,7 @@ components: database_id: type: integer format: int64 + example: 1 internal_name: type: string example: air_quality @@ -8075,6 +8212,7 @@ components: owned_by: type: string format: uuid + example: ac750fcf-ea02-4fce-85ac-d73857e18b35 ColumnCreateDto: required: - name @@ -8246,7 +8384,7 @@ components: is_schema_public: type: boolean example: true - ContainerCreateDto: + CreateContainerDto: required: - host - image_id @@ -8262,10 +8400,12 @@ components: host: type: string description: Hostname of container + example: data-db2 port: type: integer description: Port of container format: int32 + example: 3306 quota: type: integer format: int64 @@ -8274,11 +8414,14 @@ components: type: integer description: Image ID format: int64 + example: 1 ui_host: type: string + example: example.com ui_port: type: integer format: int32 + example: 3306 privileged_username: type: string description: Username of privileged user @@ -8286,6 +8429,7 @@ components: privileged_password: type: string description: Password of privileged user + example: dbrepo ContainerDto: required: - count @@ -8296,9 +8440,26 @@ components: - quota type: object properties: + jdbcMethod: + type: string + example: mariadb + host: + type: string + example: data-db + port: + type: integer + format: int32 + example: 3306 + username: + type: string + example: username + database: + type: string + example: air_quality id: type: integer format: int64 + example: 4 name: type: string example: Air Quality @@ -8317,42 +8478,52 @@ components: format: date-time internal_name: type: string - example: data-db + example: air_quality ui_host: type: string + example: example.com ui_port: type: integer format: int32 + example: 3306 ColumnBriefDto: required: - - column_type - database_id - id - internal_name - name - table_id + - type type: object properties: id: type: integer format: int64 + example: 1 name: + maxLength: 64 + minLength: 0 type: string - example: date + example: Given Name alias: type: string + example: firstname database_id: type: integer format: int64 + example: 2 table_id: type: integer format: int64 + example: 3 internal_name: + maxLength: 64 + minLength: 0 type: string - example: mdb_date - column_type: + example: given_name + type: type: string - example: date + example: varchar enum: - char - varchar @@ -8512,6 +8683,7 @@ components: id: type: integer format: int64 + example: 5 name: type: string example: mariadb @@ -8596,8 +8768,11 @@ components: checks: uniqueItems: true type: array + example: + - value > 1 items: type: string + example: '["value > 1"]' foreign_keys: type: array items: @@ -8613,6 +8788,7 @@ components: id: type: integer format: int64 + example: 8 ForeignKeyDto: required: - name @@ -8624,8 +8800,10 @@ components: id: type: integer format: int64 + example: 4 name: type: string + example: fk_name references: type: array items: @@ -8636,6 +8814,7 @@ components: $ref: '#/components/schemas/TableBriefDto' on_update: type: string + example: restrict enum: - restrict - cascade @@ -8644,6 +8823,7 @@ components: - set_default on_delete: type: string + example: restrict enum: - restrict - cascade @@ -8660,6 +8840,7 @@ components: id: type: integer format: int64 + example: 8 column: $ref: '#/components/schemas/ColumnBriefDto' foreign_key: @@ -8675,6 +8856,7 @@ components: id: type: integer format: int64 + example: 8 table: $ref: '#/components/schemas/TableBriefDto' column: @@ -8695,14 +8877,32 @@ components: - routing_key type: object properties: + jdbcMethod: + type: string + example: mariadb + host: + type: string + example: data-db + port: + type: integer + format: int32 + example: 3306 + username: + type: string + example: username + database: + type: string + example: air_quality id: type: integer format: int64 + example: 3 name: type: string example: Air Quality alias: type: string + example: a identifiers: type: array items: @@ -8726,6 +8926,7 @@ components: database_id: type: integer format: int64 + example: 2 internal_name: type: string example: air_quality @@ -8777,14 +8978,16 @@ components: id: type: integer format: int64 + example: 5 name: type: string + example: uk_name table: $ref: '#/components/schemas/TableBriefDto' columns: type: array items: - $ref: '#/components/schemas/ColumnDto' + $ref: '#/components/schemas/ColumnBriefDto' TableColumnEntityDto: required: - column_id @@ -8828,6 +9031,7 @@ components: id: type: integer format: int64 + example: 4 hash: type: string example: f829dd8a884182d0da846f365dee1221fd16610a14c81b8f9f295ff162749e50 diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java index 99df658030..4d3803a1e1 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java @@ -1,7 +1,7 @@ package at.tuwien.endpoints; +import at.tuwien.api.database.CreateAccessDto; import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.database.UpdateDatabaseAccessDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.api.user.UserDto; import at.tuwien.exception.*; @@ -76,7 +76,7 @@ public class AccessEndpoint extends RestEndpoint { }) public ResponseEntity<Void> create(@NotNull @PathVariable("databaseId") Long databaseId, @PathVariable("userId") UUID userId, - @Valid @RequestBody UpdateDatabaseAccessDto data) + @Valid @RequestBody CreateAccessDto data) throws NotAllowedException, DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseMalformedException, MetadataServiceException { log.debug("endpoint give access to database, databaseId={}, userId={}", databaseId, userId); @@ -132,7 +132,7 @@ public class AccessEndpoint extends RestEndpoint { }) public ResponseEntity<Void> update(@NotNull @PathVariable("databaseId") Long databaseId, @PathVariable("userId") UUID userId, - @Valid @RequestBody UpdateDatabaseAccessDto access) throws NotAllowedException, + @Valid @RequestBody CreateAccessDto access) throws NotAllowedException, DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseMalformedException, MetadataServiceException { log.debug("endpoint modify access to database, databaseId={}, userId={}, access.type={}", databaseId, userId, 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 077ec5b819..35c6466747 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 @@ -288,7 +288,7 @@ public class TableEndpoint extends RestEndpoint { headers.set("Access-Control-Expose-Headers", "X-Headers"); headers.set("X-Headers", String.join(",", table.getColumns().stream().map(ColumnDto::getInternalName).toList())); final Dataset<Row> dataset = tableService.getData(credentialService.getDatabase(table.getTdbid()), - table.getInternalName(), timestamp, null, null, null, null); + table.getInternalName(), timestamp, page, size, null, null); metricsService.countTableGetData(databaseId, tableId); return ResponseEntity.ok() .headers(headers) diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java index 5b96a5934b..e287cd9d9c 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java @@ -176,7 +176,6 @@ public interface DataMapper { default ViewDto resultSetToTable(ResultSet resultSet, ViewDto view, QueryConfig queryConfig) throws SQLException { final ViewColumnDto column = ViewColumnDto.builder() .ordinalPosition(resultSet.getInt(1) - 1) /* start at zero */ - .autoGenerated(resultSet.getString(2) != null && resultSet.getString(2).startsWith("nextval")) .isNullAllowed(resultSet.getString(3).equals("YES")) .columnType(ColumnTypeDto.valueOf(resultSet.getString(4).toUpperCase())) .d(resultSet.getString(7) != null ? resultSet.getLong(7) : null) @@ -256,7 +255,7 @@ public interface DataMapper { if (optional2.isPresent()) { optional2.get() .getColumns() - .add(column); + .add(columnDtoToColumnBriefDto(column)); return table; } if (type.equals("UNIQUE")) { @@ -264,7 +263,7 @@ public interface DataMapper { .getUniques() .add(UniqueDto.builder() .name(name) - .columns(new LinkedList<>(List.of(column))) + .columns(new LinkedList<>(List.of(columnDtoToColumnBriefDto(column)))) .build()); return table; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java index 66de637504..4ff6f699d6 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java @@ -2,6 +2,7 @@ package at.tuwien.api; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; @@ -16,22 +17,23 @@ import java.time.Instant; public abstract class CacheableDto { @JsonProperty("last_retrieved") + @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; @ToString.Exclude - @JsonIgnore + @Schema(example = "mariadb") private String jdbcMethod; @ToString.Exclude - @JsonIgnore + @Schema(example = "data-db") private String host; @ToString.Exclude - @JsonIgnore + @Schema(example = "3306") private Integer port; @ToString.Exclude - @JsonIgnore + @Schema(example = "username") private String username; @ToString.Exclude @@ -39,7 +41,7 @@ public abstract class CacheableDto { private String password; @ToString.Exclude - @JsonIgnore + @Schema(example = "air_quality") private String database; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java index 9b1f8fcd47..71e7601b16 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java @@ -22,6 +22,7 @@ import java.time.Instant; public class ContainerBriefDto { @NotNull + @Schema(example = "4") private Long id; @NotNull diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java index da782c2bb7..56d0df315f 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java @@ -2,7 +2,6 @@ package at.tuwien.api.container; import at.tuwien.api.CacheableDto; import at.tuwien.api.container.image.ImageDto; -import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -16,6 +15,7 @@ import java.time.Instant; @Getter @Setter @Builder +@EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor @Jacksonized @@ -23,6 +23,7 @@ import java.time.Instant; public class ContainerDto extends CacheableDto { @NotNull + @Schema(example = "4") private Long id; @NotBlank @@ -31,19 +32,15 @@ public class ContainerDto extends CacheableDto { @NotBlank @JsonProperty("internal_name") - @Schema(example = "data-db") + @Schema(example = "air_quality") private String internalName; - @NotBlank - private String host; - - @NotNull - private Integer port; - @JsonProperty("ui_host") + @Schema(example = "example.com") private String uiHost; @JsonProperty("ui_port") + @Schema(example = "3306") private Integer uiPort; @NotNull @@ -60,14 +57,23 @@ public class ContainerDto extends CacheableDto { /* lombok limitations prevent from convenient builder functions */ @JsonProperty("last_retrieved") + @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; @ToString.Exclude - @JsonIgnore + @Schema(example = "mariadb") private String jdbcMethod; @ToString.Exclude - @JsonIgnore + @Schema(example = "data-db") + private String host; + + @ToString.Exclude + @Schema(example = "3306") + private Integer port; + + @ToString.Exclude + @Schema(example = "username") private String username; @ToString.Exclude @@ -75,7 +81,7 @@ public class ContainerDto extends CacheableDto { private String password; @ToString.Exclude - @JsonIgnore + @Schema(example = "air_quality") private String database; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/CreateContainerDto.java similarity index 68% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerCreateDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/CreateContainerDto.java index 86b50bea70..23062ffd90 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/CreateContainerDto.java @@ -14,7 +14,7 @@ import lombok.extern.jackson.Jacksonized; @AllArgsConstructor @Jacksonized @ToString -public class ContainerCreateDto { +public class CreateContainerDto { @NotBlank @Schema(example = "Air Quality") @@ -22,20 +22,22 @@ public class ContainerCreateDto { @NotNull @JsonProperty("image_id") - @Schema(description = "Image ID") + @Schema(example = "1", description = "Image ID") private Long imageId; @NotBlank - @Schema(description = "Hostname of container") + @Schema(example = "data-db2", description = "Hostname of container") private String host; - @Schema(description = "Port of container") + @Schema(example = "3306", description = "Port of container") private Integer port; @JsonProperty("ui_host") + @Schema(example = "example.com") private String uiHost; @JsonProperty("ui_port") + @Schema(example = "3306") private Integer uiPort; @NotNull @@ -44,11 +46,11 @@ public class ContainerCreateDto { @NotBlank @JsonProperty("privileged_username") - @Schema(description = "Username of privileged user", example = "root") + @Schema(example = "root", description = "Username of privileged user") private String privilegedUsername; @NotBlank @JsonProperty("privileged_password") - @Schema(description = "Password of privileged user") + @Schema(example = "dbrepo", description = "Password of privileged user") private String privilegedPassword; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageBriefDto.java index 880db11fbd..6a9a970c0f 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageBriefDto.java @@ -18,6 +18,7 @@ import lombok.extern.jackson.Jacksonized; public class ImageBriefDto { @NotNull + @Schema(example = "5") private Long id; @NotBlank diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageDto.java index 3baa76171b..a9c37fb848 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/image/ImageDto.java @@ -20,6 +20,7 @@ import java.util.List; public class ImageDto { @NotNull + @Schema(example = "1") private Long id; @NotBlank diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/AccessTypeDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/AccessTypeDto.java index a93e89ec96..fa0f6fea49 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/AccessTypeDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/AccessTypeDto.java @@ -1,9 +1,11 @@ package at.tuwien.api.database; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; @Getter +@Schema public enum AccessTypeDto { @JsonProperty("read") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/UpdateDatabaseAccessDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/CreateAccessDto.java similarity index 72% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/UpdateDatabaseAccessDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/CreateAccessDto.java index 32084c865f..965e10afd5 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/UpdateDatabaseAccessDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/CreateAccessDto.java @@ -1,5 +1,6 @@ package at.tuwien.api.database; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -12,9 +13,10 @@ import lombok.extern.jackson.Jacksonized; @AllArgsConstructor @Jacksonized @ToString -public class UpdateDatabaseAccessDto { +public class CreateAccessDto { @NotNull + @Schema(example = "read") private AccessTypeDto type; diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseAccessDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseAccessDto.java index 7e929eb748..d065a5892a 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseAccessDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseAccessDto.java @@ -3,6 +3,7 @@ package at.tuwien.api.database; import at.tuwien.api.user.UserBriefDto; import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -31,6 +32,7 @@ public class DatabaseAccessDto { private UserBriefDto user; @NotNull + @Schema(example = "read") private AccessTypeDto type; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java index 7b47a617ec..4cff48db18 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java @@ -23,6 +23,7 @@ import java.util.UUID; public class DatabaseBriefDto { @NotNull + @Schema(example = "3") private Long id; @NotBlank @@ -47,6 +48,7 @@ public class DatabaseBriefDto { @Schema(example = "true") private Boolean isSchemaPublic; + @NotNull private List<IdentifierBriefDto> identifiers; @NotNull @@ -54,6 +56,7 @@ public class DatabaseBriefDto { @NotNull @JsonProperty("owner_id") + @Schema(example = "2f45ef7a-7f9b-4667-9156-152c87fe1ca5") private UUID ownerId; @JsonProperty("preview_image") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java index 044e52df38..c6866c494b 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java @@ -27,6 +27,7 @@ import java.util.List; public class DatabaseDto extends CacheableDto { @NotNull + @Schema(example = "3") private Long id; @NotBlank @@ -50,8 +51,10 @@ public class DatabaseDto extends CacheableDto { @Schema(example = "Air Quality") private String description; + @NotNull private List<TableDto> tables; + @NotNull private List<ViewDto> views; @NotNull @@ -67,10 +70,13 @@ public class DatabaseDto extends CacheableDto { @NotNull private ContainerDto container; + @NotNull private List<DatabaseAccessDto> accesses; + @NotNull private List<IdentifierDto> identifiers; + @NotNull private List<IdentifierDto> subsets; @NotNull @@ -85,22 +91,23 @@ public class DatabaseDto extends CacheableDto { /* lombok limitations prevent from convenient builder functions */ @JsonProperty("last_retrieved") + @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; @ToString.Exclude - @JsonIgnore + @Schema(example = "mariadb") private String jdbcMethod; @ToString.Exclude - @JsonIgnore + @Schema(example = "data-db") private String host; @ToString.Exclude - @JsonIgnore + @Schema(example = "3306") private Integer port; @ToString.Exclude - @JsonIgnore + @Schema(example = "username") private String username; @ToString.Exclude @@ -108,7 +115,7 @@ public class DatabaseDto extends CacheableDto { private String password; @ToString.Exclude - @JsonIgnore + @Schema(example = "air_quality") private String database; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewBriefDto.java index 9b7c9fca89..f68067e8a8 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewBriefDto.java @@ -20,10 +20,12 @@ import java.util.UUID; public class ViewBriefDto { @NotNull + @Schema(example = "4") private Long id; @NotNull @JsonProperty("database_id") + @Schema(example = "1") private Long vdbid; @NotBlank @@ -31,8 +33,8 @@ public class ViewBriefDto { private String name; @NotBlank - @JsonProperty("internal_name") @Schema(example = "air_quality") + @JsonProperty("internal_name") private String internalName; @JsonProperty("is_public") @@ -57,6 +59,7 @@ public class ViewBriefDto { private String queryHash; @JsonProperty("owned_by") + @Schema(example = "ac750fcf-ea02-4fce-85ac-d73857e18b35") private UUID ownedBy; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewColumnDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewColumnDto.java index 4005433afe..24cca8e4cc 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewColumnDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewColumnDto.java @@ -20,42 +20,41 @@ import lombok.extern.jackson.Jacksonized; public class ViewColumnDto { @NotNull + @Schema(example = "12") private Long id; @NotNull @JsonProperty("database_id") + @Schema(example = "1") private Long databaseId; @NotNull - @Schema(example = "0") @JsonProperty("ord") + @Schema(example = "0") private Integer ordinalPosition; @NotBlank @Size(max = 64) - @Schema(example = "Date") + @Schema(example = "Given Name") private String name; @NotBlank @Size(max = 64) @JsonProperty("internal_name") - @Schema(example = "mdb_date") + @Schema(example = "given_name") private String internalName; - @NotNull - @JsonProperty("auto_generated") - @Schema(example = "false") - private Boolean autoGenerated; - @JsonProperty("index_length") + @Schema(example = "255") private Long indexLength; @JsonProperty("length") + @Schema(example = "255") private Long length; @NotNull @JsonProperty("type") - @Schema(example = "string") + @Schema(example = "varchar") private ColumnTypeDto columnType; @Schema(example = "255") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java index 13e64911f5..2785cf7107 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java @@ -25,16 +25,19 @@ import java.util.List; public class ViewDto extends CacheableDto { @NotNull + @Schema(example = "4") private Long id; @NotNull @JsonProperty("database_id") + @Schema(example = "1") private Long vdbid; @NotBlank @Schema(example = "Air Quality") private String name; + @NotNull private List<IdentifierDto> identifiers; @NotBlank @@ -72,22 +75,23 @@ public class ViewDto extends CacheableDto { /* lombok limitations prevent from convenient builder functions */ @JsonProperty("last_retrieved") + @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; @ToString.Exclude - @JsonIgnore + @Schema(example = "mariadb") private String jdbcMethod; @ToString.Exclude - @JsonIgnore + @Schema(example = "data-db") private String host; @ToString.Exclude - @JsonIgnore + @Schema(example = "3306") private Integer port; @ToString.Exclude - @JsonIgnore + @Schema(example = "username") private String username; @ToString.Exclude @@ -95,7 +99,7 @@ public class ViewDto extends CacheableDto { private String password; @ToString.Exclude - @JsonIgnore + @Schema(example = "air_quality") private String database; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ImportDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ImportDto.java index 39cb6683a1..20817e0176 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ImportDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/ImportDto.java @@ -22,7 +22,7 @@ public class ImportDto { private String location; @NotNull - @Schema(description = "If true, the first line contains the column names, otherwise it contains only data") + @Schema(example = "true", description = "If true, the first line contains the column names, otherwise it contains only data") private Boolean header; @NotNull diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryDto.java index cd1659e73c..41cb641a5d 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryDto.java @@ -24,10 +24,12 @@ import java.util.List; public class QueryDto { @NotNull + @Schema(example = "4") private Long id; @NotNull @JsonProperty("database_id") + @Schema(example = "1") private Long databaseId; @NotNull diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryTypeDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryTypeDto.java index afc03ab97f..4df28733cd 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryTypeDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/query/QueryTypeDto.java @@ -1,7 +1,9 @@ package at.tuwien.api.database.query; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +@Schema public enum QueryTypeDto { @JsonProperty("query") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableBriefDto.java index 29531012f1..98932f30a2 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableBriefDto.java @@ -20,10 +20,12 @@ import java.util.UUID; public class TableBriefDto { @NotNull + @Schema(example = "3") private Long id; @NotNull @JsonProperty("database_id") + @Schema(example = "2") private Long databaseId; @NotBlank @@ -55,5 +57,6 @@ public class TableBriefDto { @NotNull @JsonProperty("owned_by") + @Schema(example = "78337b80-5699-45db-8111-cec86439ab6b") private UUID ownedBy; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java index cf18e321a4..ba80f473b8 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java @@ -28,10 +28,12 @@ import java.util.List; public class TableDto extends CacheableDto { @NotNull + @Schema(example = "3") private Long id; @NotNull @JsonProperty("database_id") + @Schema(example = "2") private Long tdbid; @NotBlank @@ -43,7 +45,7 @@ public class TableDto extends CacheableDto { @Schema(example = "air_quality") private String internalName; - @Schema + @Schema(example = "a") private String alias; private List<IdentifierDto> identifiers; @@ -109,22 +111,23 @@ public class TableDto extends CacheableDto { /* lombok limitations prevent from convenient builder functions */ @JsonProperty("last_retrieved") + @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; @ToString.Exclude - @JsonIgnore + @Schema(example = "mariadb") private String jdbcMethod; @ToString.Exclude - @JsonIgnore + @Schema(example = "data-db") private String host; @ToString.Exclude - @JsonIgnore + @Schema(example = "3306") private Integer port; @ToString.Exclude - @JsonIgnore + @Schema(example = "username") private String username; @ToString.Exclude @@ -132,7 +135,7 @@ public class TableDto extends CacheableDto { private String password; @ToString.Exclude - @JsonIgnore + @Schema(example = "air_quality") private String database; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDeleteDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDeleteDto.java index 6c3a946e12..f74ffb3d89 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDeleteDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDeleteDto.java @@ -1,5 +1,6 @@ package at.tuwien.api.database.table; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -17,6 +18,7 @@ import java.util.Map; public class TupleDeleteDto { @NotNull + @Schema(example = "{\"id\": 1}") private Map<String, Object> keys; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDto.java index 62f57434b6..13dc2b9723 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleDto.java @@ -1,5 +1,6 @@ package at.tuwien.api.database.table; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -17,6 +18,7 @@ import java.util.Map; public class TupleDto { @NotNull + @Schema(example = "{\"key\": \"value\"}") private Map<String, Object> data; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleUpdateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleUpdateDto.java index be50791b94..ab3f1ae875 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleUpdateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TupleUpdateDto.java @@ -1,5 +1,6 @@ package at.tuwien.api.database.table; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -17,9 +18,11 @@ import java.util.Map; public class TupleUpdateDto { @NotNull + @Schema(example = "{\"key\": \"value\"}") private Map<String, Object> data; @NotNull + @Schema(example = "{\"id\": 1}") private Map<String, Object> keys; } \ No newline at end of file diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java index 4dee5f0837..bc13700bdd 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnBriefDto.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -18,31 +19,36 @@ import lombok.extern.jackson.Jacksonized; public class ColumnBriefDto { @NotNull + @Schema(example = "1") private Long id; - @JsonProperty("database_id") @NotNull + @Schema(example = "2") + @JsonProperty("database_id") private Long databaseId; - @JsonProperty("table_id") @NotNull + @Schema(example = "3") + @JsonProperty("table_id") private Long tableId; @NotBlank - @Schema(example = "date") + @Size(max = 64) + @Schema(example = "Given Name") private String name; @NotBlank + @Size(max = 64) @JsonProperty("internal_name") - @Schema(example = "mdb_date") + @Schema(example = "given_name") private String internalName; - @Schema + @Schema(example = "firstname") private String alias; @NotNull - @JsonProperty("column_type") - @Schema(example = "date") + @JsonProperty("type") + @Schema(example = "varchar") private ColumnTypeDto columnType; } 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 465bd3bd1e..462821b53c 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 @@ -29,18 +29,18 @@ public class ColumnDto { private Long id; @NotNull - @Schema(example = "2") @JsonProperty("database_id") + @Schema(example = "2") private Long databaseId; @NotNull - @Schema(example = "3") @JsonProperty("table_id") + @Schema(example = "3") private Long tableId; @NotNull - @Schema(example = "0") @JsonProperty("ord") + @Schema(example = "0") private Integer ordinalPosition; @NotBlank @@ -119,9 +119,11 @@ public class ColumnDto { @Schema(example = "false") private Boolean isNullAllowed; + @Schema(example = "[\"val1\"]") @Parameter(description = "enum values, only considered when type = ENUM") private List<String> enums; + @Schema(example = "[\"val1\"]") @Parameter(description = "enum values, only considered when type = ENUM") private List<String> sets; diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnTypeDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnTypeDto.java index d44b25b84e..a963370829 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnTypeDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnTypeDto.java @@ -1,10 +1,12 @@ package at.tuwien.api.database.table.columns; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; /* MYSQL 8 */ @Getter +@Schema public enum ColumnTypeDto { @JsonProperty("char") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsDto.java index 3cd36f6d68..b9288b659b 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsDto.java @@ -4,6 +4,7 @@ import at.tuwien.api.database.table.constraints.foreign.ForeignKeyDto; import at.tuwien.api.database.table.constraints.primary.PrimaryKeyDto; import at.tuwien.api.database.table.constraints.unique.UniqueDto; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -24,6 +25,7 @@ public class ConstraintsDto { @JsonProperty("foreign_keys") private List<ForeignKeyDto> foreignKeys; + @Schema(example = "[\"value > 1\"]") private Set<String> checks; @JsonProperty("primary_key") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyBriefDto.java index 58a4d5b245..a22b8749ae 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyBriefDto.java @@ -1,5 +1,6 @@ package at.tuwien.api.database.table.constraints.foreign; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -12,5 +13,7 @@ import lombok.extern.jackson.Jacksonized; @ToString public class ForeignKeyBriefDto { + @NonNull + @Schema(example = "8") private Long id; } 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 8016de9131..254666f9eb 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 @@ -4,6 +4,7 @@ import at.tuwien.api.database.table.TableBriefDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.columns.ColumnDto; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -19,9 +20,11 @@ import java.util.List; @ToString public class ForeignKeyDto { + @Schema(example = "4") private Long id; @NotNull + @Schema(example = "fk_name") private String name; @NotNull @@ -35,8 +38,10 @@ public class ForeignKeyDto { private TableBriefDto referencedTable; @JsonProperty("on_update") + @Schema(example = "restrict") private ReferenceTypeDto onUpdate; @JsonProperty("on_delete") + @Schema(example = "restrict") private ReferenceTypeDto onDelete; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyReferenceDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyReferenceDto.java index 55fcc46ecf..f0d5b249cd 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyReferenceDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyReferenceDto.java @@ -2,6 +2,7 @@ package at.tuwien.api.database.table.constraints.foreign; import at.tuwien.api.database.table.columns.ColumnBriefDto; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -15,6 +16,7 @@ import lombok.extern.jackson.Jacksonized; @ToString public class ForeignKeyReferenceDto { + @Schema(example = "8") private Long id; @NotNull 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 80df5d443b..86d1d49960 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 @@ -2,6 +2,7 @@ package at.tuwien.api.database.table.constraints.primary; import at.tuwien.api.database.table.TableBriefDto; import at.tuwien.api.database.table.columns.ColumnBriefDto; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -15,6 +16,7 @@ import lombok.extern.jackson.Jacksonized; @ToString public class PrimaryKeyDto { + @Schema(example = "8") private Long id; @NotNull 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 2bcc9d6cf4..755f3a31b7 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 @@ -1,13 +1,11 @@ - package at.tuwien.api.database.table.constraints.unique; import at.tuwien.api.database.table.TableBriefDto; -import at.tuwien.api.database.table.TableDto; -import at.tuwien.api.database.table.columns.ColumnDto; +import at.tuwien.api.database.table.columns.ColumnBriefDto; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; -import org.springframework.data.annotation.Id; import java.util.List; @@ -21,14 +19,16 @@ import java.util.List; public class UniqueDto { @NotNull + @Schema(example = "5") private Long id; @NotNull + @Schema(example = "uk_name") private String name; @NotNull private TableBriefDto table; @NotNull - private List<ColumnDto> columns; + private List<ColumnBriefDto> columns; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorBriefDto.java new file mode 100644 index 0000000000..8265a1106b --- /dev/null +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorBriefDto.java @@ -0,0 +1,53 @@ +package at.tuwien.api.identifier; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.*; +import lombok.extern.jackson.Jacksonized; + + +@Getter +@Setter +@Builder +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +@Jacksonized +@ToString +public class CreatorBriefDto { + + @NotNull + @Schema(example = "11") + private Long id; + + @NotBlank + @JsonProperty("creator_name") + @Schema(example = "Carberry, Josiah") + private String creatorName; + + @JsonProperty("name_type") + @Schema(example = "Personal") + private NameTypeDto nameType; + + @JsonProperty("name_identifier") + @Schema(example = "0000-0002-1825-0097") + private String nameIdentifier; + + @JsonProperty("name_identifier_scheme") + @Schema(example = "ORCID") + private NameIdentifierSchemeTypeDto nameIdentifierScheme; + + @Schema(example = "Brown University") + private String affiliation; + + @JsonProperty("affiliation_identifier") + @Schema(example = "https://ror.org/05gq02987") + private String affiliationIdentifier; + + @JsonProperty("affiliation_identifier_scheme") + @Schema(example = "ROR") + private AffiliationIdentifierSchemeTypeDto affiliationIdentifierScheme; + +} diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorDto.java index 9c166f869c..a3a2976901 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorDto.java @@ -21,6 +21,7 @@ import org.springframework.data.annotation.Id; public class CreatorDto { @NotNull + @Schema(example = "11") private Long id; @Schema(example = "Josiah") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java index 82ff3b0fe7..97f3502674 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java @@ -20,6 +20,7 @@ import java.util.UUID; public class IdentifierBriefDto { @NotNull + @Schema(example = "2") private Long id; @NotNull @@ -40,8 +41,12 @@ public class IdentifierBriefDto { private Long viewId; @NotNull + @Schema(example = "database") private IdentifierTypeDto type; + @NotNull + private List<CreatorBriefDto> creators; + @NotNull private List<IdentifierTitleDto> titles; @@ -57,10 +62,13 @@ public class IdentifierBriefDto { @Schema(example = "2022") private Integer publicationYear; + @NotNull + @Schema(example = "draft") private IdentifierStatusTypeDto status; @NotNull - @JsonProperty("created_by") - private UUID createdBy; + @JsonProperty("owned_by") + @Schema(example = "2f45ef7a-7f9b-4667-9156-152c87fe1ca5") + private UUID ownedBy; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDescriptionDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDescriptionDto.java index 55e6ff7621..fbcbb3afe7 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDescriptionDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDescriptionDto.java @@ -19,6 +19,7 @@ import org.springframework.data.annotation.Id; public class IdentifierDescriptionDto { @NotNull + @Schema(example = "3") private Long id; @Schema(example = "Air quality reports at Stephansplatz, Vienna") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java index 87baf48706..53d40ecd78 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierDto.java @@ -25,6 +25,7 @@ import java.util.List; public class IdentifierDto { @NotNull + @Schema(example = "2") private Long id; @NotNull @@ -45,13 +46,16 @@ public class IdentifierDto { private Long viewId; @NotNull + @Schema(example = "database") private IdentifierTypeDto type; @NotNull private List<IdentifierTitleDto> titles; + @NotNull private List<IdentifierDescriptionDto> descriptions; + @NotNull private List<IdentifierFunderDto> funders; @NotBlank @@ -106,13 +110,17 @@ public class IdentifierDto { @Schema(example = "2022") private Integer publicationYear; + @NotNull private LanguageTypeDto language; + @NotNull private List<LicenseDto> licenses; @NotNull private List<CreatorDto> creators; + @NotNull + @Schema(example = "draft") private IdentifierStatusTypeDto status; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderDto.java index 921ba3eb98..93084d2d17 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderDto.java @@ -19,6 +19,7 @@ import org.springframework.data.annotation.Id; public class IdentifierFunderDto { @NotNull + @Schema(example = "2") private Long id; @NotBlank diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierTitleDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierTitleDto.java index 9333a05ce9..27e3b323ea 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierTitleDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierTitleDto.java @@ -19,6 +19,7 @@ import org.springframework.data.annotation.Id; public class IdentifierTitleDto { @NotNull + @Schema(example = "4") private Long id; @Schema(example = "Airquality Demonstrator") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierDto.java index 74525bf711..6ff5d9aed1 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierDto.java @@ -24,6 +24,7 @@ import java.time.Instant; public class RelatedIdentifierDto { @NotNull + @Schema(example = "8") private Long id; @NotNull diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java index 43fb10201b..26192f460f 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java @@ -25,10 +25,6 @@ public class UserDto extends CacheableDto { @Schema(example = "1ffc7b0e-9aeb-4e8b-b8f1-68f3936155b4") private UUID id; - @NotNull - @Schema(example = "jcarberry", description = "Only contains lowercase characters") - private String username; - @Schema(example = "Josiah Carberry") private String name; @@ -44,14 +40,37 @@ public class UserDto extends CacheableDto { @Schema(example = "Carberry") private String lastname; - @ToString.Exclude - @JsonIgnore - private String password; - @NotNull private UserAttributesDto attributes; + /* lombok limitations prevent from convenient builder functions */ + @JsonProperty("last_retrieved") + @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; + @ToString.Exclude + @Schema(example = "mariadb") + private String jdbcMethod; + + @ToString.Exclude + @Schema(example = "data-db") + private String host; + + @ToString.Exclude + @Schema(example = "3306") + private Integer port; + + @ToString.Exclude + @Schema(example = "username") + private String username; + + @ToString.Exclude + @JsonIgnore + private String password; + + @ToString.Exclude + @Schema(example = "air_quality") + private String database; + } 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 ab13affe7e..75f589e4c5 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 @@ -2,7 +2,7 @@ package at.tuwien.mapper; import at.tuwien.api.auth.SignupRequestDto; import at.tuwien.api.container.ContainerBriefDto; -import at.tuwien.api.container.ContainerCreateDto; +import at.tuwien.api.container.CreateContainerDto; import at.tuwien.api.container.ContainerDto; import at.tuwien.api.container.image.DataTypeDto; import at.tuwien.api.container.image.ImageBriefDto; @@ -12,6 +12,7 @@ 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.ColumnBriefDto; 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; @@ -109,7 +110,7 @@ public interface MetadataMapper { @Mappings({ @Mapping(target = "internalName", source = "name", qualifiedByName = "internalMapping") }) - Container containerCreateRequestDtoToContainer(ContainerCreateDto data); + Container containerCreateRequestDtoToContainer(CreateContainerDto data); ContainerDto containerToContainerDto(Container data); @@ -302,7 +303,8 @@ public interface MetadataMapper { IdentifierDto identifierToIdentifierDto(Identifier data); @Mappings({ - @Mapping(target = "databaseId", source = "database.id") + @Mapping(target = "databaseId", source = "database.id"), + @Mapping(target = "ownedBy", source = "owner.id") }) IdentifierBriefDto identifierToIdentifierBriefDto(Identifier data); @@ -492,7 +494,7 @@ public interface MetadataMapper { .name(data.getName()) .columns(data.getColumns() .stream() - .map(this::tableColumnToColumnDto) + .map(this::tableColumnToColumnBriefDto) .toList()) .table(tableToTableBriefDto(data.getTable())) .build(); @@ -726,6 +728,12 @@ public interface MetadataMapper { }) ColumnDto tableColumnToColumnDto(TableColumn data); + @Mappings({ + @Mapping(target = "tableId", source = "table.id"), + @Mapping(target = "databaseId", source = "table.database.id") + }) + ColumnBriefDto tableColumnToColumnBriefDto(TableColumn data); + @Mappings({ @Mapping(target = "id", expression = "java(null)"), @Mapping(target = "columnType", source = "data.type"), 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 f184ffc337..9c109c87d6 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 @@ -1,7 +1,7 @@ package at.tuwien.endpoints; import at.tuwien.api.database.DatabaseAccessDto; -import at.tuwien.api.database.UpdateDatabaseAccessDto; +import at.tuwien.api.database.CreateAccessDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; @@ -91,7 +91,7 @@ public class AccessEndpoint extends AbstractEndpoint { }) public ResponseEntity<DatabaseAccessDto> create(@NotNull @PathVariable("databaseId") Long databaseId, @PathVariable("userId") UUID userId, - @Valid @RequestBody UpdateDatabaseAccessDto data, + @Valid @RequestBody CreateAccessDto data, @NotNull Principal principal) throws NotAllowedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException, AccessNotFoundException, SearchServiceException, SearchServiceConnectionException { @@ -153,7 +153,7 @@ public class AccessEndpoint extends AbstractEndpoint { }) public ResponseEntity<Void> update(@NotNull @PathVariable("databaseId") Long databaseId, @PathVariable("userId") UUID userId, - @Valid @RequestBody UpdateDatabaseAccessDto data, + @Valid @RequestBody CreateAccessDto data, @NotNull Principal principal) throws NotAllowedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException, AccessNotFoundException, SearchServiceException, SearchServiceConnectionException { 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 3c506cfd0a..b5d153318c 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 @@ -1,7 +1,7 @@ package at.tuwien.endpoints; import at.tuwien.api.container.ContainerBriefDto; -import at.tuwien.api.container.ContainerCreateDto; +import at.tuwien.api.container.CreateContainerDto; import at.tuwien.api.container.ContainerDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.entities.container.Container; @@ -105,7 +105,7 @@ public class ContainerEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<ContainerDto> create(@Valid @RequestBody ContainerCreateDto data) + public ResponseEntity<ContainerDto> create(@Valid @RequestBody CreateContainerDto data) throws ImageNotFoundException, ContainerAlreadyExistsException { log.debug("endpoint create container, data={}", data); return ResponseEntity.status(HttpStatus.CREATED) diff --git a/dbrepo-metadata-service/rest-service/src/main/resources/application.yml b/dbrepo-metadata-service/rest-service/src/main/resources/application.yml index 290864eea9..01d2de7d8a 100644 --- a/dbrepo-metadata-service/rest-service/src/main/resources/application.yml +++ b/dbrepo-metadata-service/rest-service/src/main/resources/application.yml @@ -3,7 +3,7 @@ application: version: '@project.version@' spring: datasource: - url: "jdbc:mariadb://${METADATA_HOST:metadata-db}:${METADATA_PORT:3306}/${METADATA_DB:dbrepo}${METADATA_JDBC_EXTRA_ARGS}" + url: "jdbc:mariadb://${METADATA_HOST:localhost}:${METADATA_PORT:3306}/${METADATA_DB:dbrepo}${METADATA_JDBC_EXTRA_ARGS:}" driver-class-name: org.mariadb.jdbc.Driver username: "${METADATA_USERNAME:root}" password: "${METADATA_DB_PASSWORD:dbrepo}" @@ -18,7 +18,7 @@ spring: application: name: metadata-service rabbitmq: - host: "${BROKER_HOST:broker-service}" + host: "${BROKER_HOST:localhost}" virtual-host: "${BROKER_VIRTUALHOST:dbrepo}" username: "${BROKER_USERNAME:admin}" password: "${BROKER_PASSWORD:admin}" diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ContainerEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ContainerEndpointUnitTest.java index ab3f4485b2..98ece39e1e 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ContainerEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ContainerEndpointUnitTest.java @@ -1,8 +1,8 @@ package at.tuwien.endpoints; +import at.tuwien.api.container.CreateContainerDto; import at.tuwien.test.AbstractUnitTest; 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 at.tuwien.exception.*; @@ -122,7 +122,7 @@ public class ContainerEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser public void create_anonymous_fails() { - final ContainerCreateDto request = ContainerCreateDto.builder() + final CreateContainerDto request = CreateContainerDto.builder() .name(CONTAINER_1_NAME) .imageId(IMAGE_1_ID) .build(); @@ -136,7 +136,7 @@ public class ContainerEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"create-container"}) public void create_hasRole_succeeds() throws ContainerAlreadyExistsException, ImageNotFoundException { - final ContainerCreateDto request = ContainerCreateDto.builder() + final CreateContainerDto request = CreateContainerDto.builder() .name(CONTAINER_1_NAME) .imageId(IMAGE_1_ID) .build(); @@ -148,7 +148,7 @@ public class ContainerEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_4_USERNAME) public void create_noRole_fails() { - final ContainerCreateDto request = ContainerCreateDto.builder() + final CreateContainerDto request = CreateContainerDto.builder() .name(CONTAINER_1_NAME) .imageId(IMAGE_1_ID) .build(); @@ -221,7 +221,7 @@ public class ContainerEndpointUnitTest extends AbstractUnitTest { assertEquals(CONTAINER_2_INTERNALNAME, container2.getInternalName()); } - public void create_generic(ContainerCreateDto data) throws ContainerAlreadyExistsException, ImageNotFoundException { + public void create_generic(CreateContainerDto data) throws ContainerAlreadyExistsException, ImageNotFoundException { /* mock */ when(containerService.create(data)) diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java index 5ae4aad018..dc41121b90 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java @@ -1,11 +1,8 @@ package at.tuwien.mvc; import at.tuwien.api.auth.RefreshTokenRequestDto; -import at.tuwien.api.database.table.TableStatisticDto; -import at.tuwien.api.database.table.columns.ColumnStatisticDto; -import at.tuwien.api.semantics.TableColumnEntityDto; +import at.tuwien.api.container.CreateContainerDto; import at.tuwien.test.AbstractUnitTest; -import at.tuwien.api.container.ContainerCreateDto; import at.tuwien.api.database.*; import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto; import at.tuwien.config.MetricsConfig; @@ -173,7 +170,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* ignore */ } try { - containerEndpoint.create(ContainerCreateDto.builder().name(CONTAINER_1_NAME).imageId(IMAGE_1_ID).build()); + containerEndpoint.create(CreateContainerDto.builder().name(CONTAINER_1_NAME).imageId(IMAGE_1_ID).build()); } catch (Exception e) { /* ignore */ } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceUnitTest.java index a4f0676893..bb19a404dd 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceUnitTest.java @@ -1,7 +1,7 @@ package at.tuwien.service; +import at.tuwien.api.container.CreateContainerDto; import at.tuwien.test.AbstractUnitTest; -import at.tuwien.api.container.ContainerCreateDto; import at.tuwien.entities.container.Container; import at.tuwien.exception.*; import at.tuwien.repository.ContainerRepository; @@ -48,7 +48,7 @@ public class ContainerServiceUnitTest extends AbstractUnitTest { @Test public void create_succeeds() throws ContainerAlreadyExistsException, ImageNotFoundException { - final ContainerCreateDto request = ContainerCreateDto.builder() + final CreateContainerDto request = CreateContainerDto.builder() .imageId(IMAGE_1_ID) .name(CONTAINER_1_NAME) .build(); @@ -68,7 +68,7 @@ public class ContainerServiceUnitTest extends AbstractUnitTest { @Test public void create_containerExists_fails() { - final ContainerCreateDto request = ContainerCreateDto.builder() + final CreateContainerDto request = CreateContainerDto.builder() .imageId(IMAGE_1_ID) .name(CONTAINER_1_NAME) .build(); @@ -85,7 +85,7 @@ public class ContainerServiceUnitTest extends AbstractUnitTest { @Test public void create_imageNotFound_fails() { - final ContainerCreateDto request = ContainerCreateDto.builder() + final CreateContainerDto request = CreateContainerDto.builder() .name(CONTAINER_3_NAME) .imageId(9999L) .build(); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java index 2b0cd72dda..191f42fea3 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java @@ -48,7 +48,7 @@ public class DataServiceGatewayImpl implements DataServiceGateway { log.trace("create access at endpoint {} with path {}", gatewayConfig.getDataEndpoint(), path); try { response = restTemplate.exchange(path, HttpMethod.POST, - new HttpEntity<>(UpdateDatabaseAccessDto.builder().type(access).build()), Void.class); + new HttpEntity<>(CreateAccessDto.builder().type(access).build()), Void.class); } catch (HttpServerErrorException e) { log.error("Failed to create access: {}", e.getMessage()); throw new DataServiceConnectionException("Failed to create access: " + e.getMessage(), e); @@ -73,7 +73,7 @@ public class DataServiceGatewayImpl implements DataServiceGateway { log.trace("update access at endpoint {} with path {}", gatewayConfig.getDataEndpoint(), path); try { response = restTemplate.exchange(path, HttpMethod.PUT, - new HttpEntity<>(UpdateDatabaseAccessDto.builder().type(access).build()), Void.class); + new HttpEntity<>(CreateAccessDto.builder().type(access).build()), Void.class); } catch (HttpServerErrorException e) { log.error("Failed to update access: {}", e.getMessage()); throw new DataServiceConnectionException("Failed to update access: " + e.getMessage(), e); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/ContainerService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/ContainerService.java index aa5a3295c4..9aa2dc6c89 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/ContainerService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/ContainerService.java @@ -1,11 +1,9 @@ package at.tuwien.service; -import at.tuwien.api.container.ContainerCreateDto; +import at.tuwien.api.container.CreateContainerDto; import at.tuwien.entities.container.Container; -import at.tuwien.entities.user.User; import at.tuwien.exception.*; -import java.security.Principal; import java.util.List; public interface ContainerService { @@ -18,7 +16,7 @@ public interface ContainerService { * @throws ImageNotFoundException The image of the container was not found in the metadata database. * @throws ContainerAlreadyExistsException A container with this name already exists. */ - Container create(ContainerCreateDto createDto) throws ImageNotFoundException, + Container create(CreateContainerDto createDto) throws ImageNotFoundException, ContainerAlreadyExistsException; /** 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 91db7736db..2a3c321536 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 @@ -1,6 +1,6 @@ package at.tuwien.service.impl; -import at.tuwien.api.container.ContainerCreateDto; +import at.tuwien.api.container.CreateContainerDto; import at.tuwien.entities.container.Container; import at.tuwien.entities.container.image.ContainerImage; import at.tuwien.exception.ContainerAlreadyExistsException; @@ -38,7 +38,7 @@ public class ContainerServiceImpl implements ContainerService { @Override @Transactional - public Container create(ContainerCreateDto data) throws ImageNotFoundException, + public Container create(CreateContainerDto data) throws ImageNotFoundException, ContainerAlreadyExistsException { final String containerName = "dbrepo-userdb-" + metadataMapper.nameToInternalName(data.getName()); /* check */ 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 12dbd35efc..bab95a366b 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 @@ -262,15 +262,15 @@ public abstract class BaseTest { public final static String ROLE_DEFAULT_RESEARCHER_ROLES_NAME = "default-researcher-roles"; public final static UUID ROLE_DEFAULT_RESEARCHER_ROLES_REALM_ID = REALM_DBREPO_ID; - public final static UpdateDatabaseAccessDto UPDATE_DATABASE_ACCESS_READ_DTO = UpdateDatabaseAccessDto.builder() + public final static CreateAccessDto UPDATE_DATABASE_ACCESS_READ_DTO = CreateAccessDto.builder() .type(AccessTypeDto.READ) .build(); - public final static UpdateDatabaseAccessDto UPDATE_DATABASE_ACCESS_WRITE_OWN_DTO = UpdateDatabaseAccessDto.builder() + public final static CreateAccessDto UPDATE_DATABASE_ACCESS_WRITE_OWN_DTO = CreateAccessDto.builder() .type(AccessTypeDto.WRITE_OWN) .build(); - public final static UpdateDatabaseAccessDto UPDATE_DATABASE_ACCESS_WRITE_ALL_DTO = UpdateDatabaseAccessDto.builder() + public final static CreateAccessDto UPDATE_DATABASE_ACCESS_WRITE_ALL_DTO = CreateAccessDto.builder() .type(AccessTypeDto.WRITE_ALL) .build(); @@ -3373,6 +3373,31 @@ public abstract class BaseTest { .sets(null) .build()); + public final static List<ColumnBriefDto> TABLE_2_COLUMNS_BRIEF_DTO = List.of(ColumnBriefDto.builder() + .id(COLUMN_2_1_ID) + .tableId(TABLE_2_ID) + .databaseId(DATABASE_1_ID) + .name("location") + .internalName("location") + .columnType(ColumnTypeDto.VARCHAR) + .build(), + ColumnBriefDto.builder() + .id(COLUMN_2_2_ID) + .tableId(TABLE_2_ID) + .databaseId(DATABASE_1_ID) + .name("lat") + .internalName("lat") + .columnType(ColumnTypeDto.DOUBLE) + .build(), + ColumnBriefDto.builder() + .id(COLUMN_2_3_ID) + .tableId(TABLE_2_ID) + .databaseId(DATABASE_1_ID) + .name("lng") + .internalName("lng") + .columnType(ColumnTypeDto.DOUBLE) + .build()); + public final static Long COLUMN_3_1_ID = 9L; public final static Long COLUMN_3_2_ID = 10L; @@ -8104,7 +8129,7 @@ public abstract class BaseTest { .id(1L) .table(TABLE_2_BRIEF_DTO) .name("uk_1") - .columns(new LinkedList<>(List.of(TABLE_2_COLUMNS_DTO.get(1)))) + .columns(new LinkedList<>(List.of(TABLE_2_COLUMNS_BRIEF_DTO.get(1)))) .build()))) .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder() .table(TABLE_2_BRIEF_DTO) diff --git a/dbrepo-ui/components/identifier/Citation.vue b/dbrepo-ui/components/identifier/Citation.vue index 5722351f0a..9ee434c943 100644 --- a/dbrepo-ui/components/identifier/Citation.vue +++ b/dbrepo-ui/components/identifier/Citation.vue @@ -4,7 +4,7 @@ <v-col v-if="!loading" md="10"> - <pre>{{ citation }}</pre> + {{ citation }} </v-col> <v-col v-if="!$vuetify.display.mdAndDown" diff --git a/dbrepo-ui/composables/identifier-service.ts b/dbrepo-ui/composables/identifier-service.ts index 3853d9df75..3ae194ff2f 100644 --- a/dbrepo-ui/composables/identifier-service.ts +++ b/dbrepo-ui/composables/identifier-service.ts @@ -103,7 +103,7 @@ export const useIdentifierService = (): any => { }) } - function identifierToCreators(identifier: IdentifierDto) { + function identifierToCreators(identifier: IdentifierDto): string | null { if (!identifier) { return null } diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts index 4bce6ec5c5..70084f3cca 100644 --- a/dbrepo-ui/nuxt.config.ts +++ b/dbrepo-ui/nuxt.config.ts @@ -3,19 +3,19 @@ import vuetify from 'vite-plugin-vuetify' const proxy: any = {} -/* proxies the backend calls, >>NOT<< the frontend calls (clicking) */ -if (process.env.NODE_ENV === 'development') { - const api = 'http://localhost' - proxy['/api'] = api - proxy['/pid'] = { - target: api + '/api', - changeOrigin: true, - pathRewrite: { - '^/pid': '/pid' - } - } - process.env.NUXT_PUBLIC_API_SERVER = api -} +// /* proxies the backend calls, >>NOT<< the frontend calls (clicking) */ +// if (process.env.NODE_ENV === 'development') { +// const api = 'http://localhost' +// proxy['/api'] = api +// proxy['/pid'] = { +// target: api + '/api', +// changeOrigin: true, +// pathRewrite: { +// '^/pid': '/pid' +// } +// } +// process.env.NUXT_PUBLIC_API_SERVER = api +// } /** * https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering @@ -75,8 +75,8 @@ export default defineNuxtConfig({ } }, api: { - client: 'http://localhost', - server: 'http://gateway-service', + client: 'https://dbrepo1.ec.tuwien.ac.at', + server: 'https://dbrepo1.ec.tuwien.ac.at', }, upload: { client: 'http://localhost/api/upload/files', 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 2e76d4ebd4..2eea6a69bf 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 @@ -127,6 +127,7 @@ export default { data () { return { loading: true, + error: true, loadingData: false, loadingCount: false, loadingDelete: false, diff --git a/lib/python/dbrepo/RestClient.py b/lib/python/dbrepo/RestClient.py index bc6940b7d0..1f9f778a84 100644 --- a/lib/python/dbrepo/RestClient.py +++ b/lib/python/dbrepo/RestClient.py @@ -1091,7 +1091,7 @@ class RestClient: :raises ResponseCodeError: If something went wrong with the insert. """ url = f'/api/database/{database_id}/table/{table_id}/data' - response = self._wrapper(method="post", url=url, force_auth=True, payload=CreateData(data=data)) + response = self._wrapper(method="post", url=url, force_auth=True, payload=Tuple(data=data)) if response.status_code == 201: return if response.status_code == 400: @@ -1279,7 +1279,7 @@ class RestClient: :raises ResponseCodeError: If something went wrong with the update. """ url = f'/api/database/{database_id}/table/{table_id}/data' - response = self._wrapper(method="put", url=url, force_auth=True, payload=UpdateData(data=data, keys=keys)) + response = self._wrapper(method="put", url=url, force_auth=True, payload=TupleUpdate(data=data, keys=keys)) if response.status_code == 202: return if response.status_code == 400: @@ -1309,7 +1309,7 @@ class RestClient: :raises ResponseCodeError: If something went wrong with the deletion. """ url = f'/api/database/{database_id}/table/{table_id}/data' - response = self._wrapper(method="delete", url=url, force_auth=True, payload=DeleteData(keys=keys)) + response = self._wrapper(method="delete", url=url, force_auth=True, payload=TupleDelete(keys=keys)) if response.status_code == 202: return if response.status_code == 400: diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py index a8518ec79e..76f6d37a3a 100644 --- a/lib/python/dbrepo/api/dto.py +++ b/lib/python/dbrepo/api/dto.py @@ -463,16 +463,16 @@ class License(BaseModel): description: str -class CreateData(BaseModel): +class Tuple(BaseModel): data: dict -class UpdateData(BaseModel): +class TupleUpdate(BaseModel): data: dict keys: dict -class DeleteData(BaseModel): +class TupleDelete(BaseModel): keys: dict @@ -528,7 +528,26 @@ class CreateTableConstraints(BaseModel): foreign_keys: List[CreateForeignKey] = field(default_factory=list) -class IdentifierCreator(BaseModel): +class NameIdentifierSchemeType(str, Enum): + """ + Enumeration of name identifier scheme types. + """ + ORCID = "ORCID" + ROR = "ROR" + ISNI = "ISNI" + GRID = "GRID" + + +class AffiliationIdentifierSchemeType(str, Enum): + """ + Enumeration of affiliation identifier scheme types. + """ + ROR = "ROR" + ISNI = "ISNI" + GRID = "GRID" + + +class Creator(BaseModel): id: int creator_name: str firstname: Optional[str] = None @@ -536,13 +555,24 @@ class IdentifierCreator(BaseModel): affiliation: Optional[str] = None name_type: Optional[str] = None name_identifier: Optional[str] = None - name_identifier_scheme: Optional[str] = None + name_identifier_scheme: Optional[NameIdentifierSchemeType] = None name_identifier_scheme_uri: Optional[str] = None affiliation_identifier: Optional[str] = None affiliation_identifier_scheme: Optional[str] = None affiliation_identifier_scheme_uri: Optional[str] = None +class CreatorBrief(BaseModel): + id: int + creator_name: str + affiliation: Optional[str] = None + name_type: Optional[str] = None + name_identifier: Optional[str] = None + name_identifier_scheme: Optional[NameIdentifierSchemeType] = None + affiliation_identifier: Optional[str] = None + affiliation_identifier_scheme: Optional[str] = None + + class CreateIdentifierCreator(BaseModel): creator_name: str firstname: Optional[str] = None @@ -603,7 +633,7 @@ class Identifier(BaseModel): status: IdentifierStatusType publication_year: int publisher: str - creators: List[IdentifierCreator] + creators: List[Creator] titles: List[IdentifierTitle] descriptions: List[IdentifierDescription] funders: Optional[List[IdentifierFunder]] = field(default_factory=list) @@ -627,7 +657,7 @@ class IdentifierBrief(BaseModel): id: int database_id: int type: IdentifierType - created_by: str + owned_by: str status: IdentifierStatusType publication_year: int publisher: str @@ -872,7 +902,7 @@ class Query(BaseModel): result_hash: str query_normalized: str result_number: Optional[int] = None - identifiers: List[Identifier] = field(default_factory=list) + identifiers: List[IdentifierBrief] = field(default_factory=list) class UpdateQuery(BaseModel): @@ -966,36 +996,17 @@ class Table(BaseModel): avg_row_length: Optional[int] = None -class TableMinimal(BaseModel): - id: int - database_id: int - - -class ColumnMinimal(BaseModel): - id: int - table_id: int - database_id: int - - class DatabaseBrief(BaseModel): id: int name: str - owner: UserBrief contact: UserBrief - exchange_name: str + owner_id: str internal_name: str is_public: bool is_schema_public: bool - container: ContainerBrief identifiers: Optional[List[IdentifierBrief]] = field(default_factory=list) - subsets: Optional[List[IdentifierBrief]] = field(default_factory=list) preview_image: Optional[str] = None description: Optional[str] = None - tables: Optional[List[TableBrief]] = field(default_factory=list) - views: Optional[List[ViewBrief]] = field(default_factory=list) - image: Optional[str] = None - accesses: Optional[List[DatabaseAccess]] = field(default_factory=list) - exchange_name: Optional[str] = None class Database(BaseModel): @@ -1014,22 +1025,21 @@ class Database(BaseModel): description: Optional[str] = None tables: Optional[List[Table]] = field(default_factory=list) views: Optional[List[View]] = field(default_factory=list) - image: Optional[str] = None accesses: Optional[List[DatabaseAccess]] = field(default_factory=list) exchange_name: Optional[str] = None class Unique(BaseModel): id: int - table: TableMinimal - columns: List[ColumnMinimal] + table: TableBrief + columns: List[ColumnBrief] class ForeignKeyReference(BaseModel): id: int - foreign_key: ForeignKeyMinimal - column: ColumnMinimal - referenced_column: ColumnMinimal + foreign_key: ForeignKeyBrief + column: ColumnBrief + referenced_column: ColumnBrief class ReferenceType(str, Enum): @@ -1043,7 +1053,7 @@ class ReferenceType(str, Enum): SET_DEFAULT = "set_default" -class ForeignKeyMinimal(BaseModel): +class ForeignKeyBrief(BaseModel): id: int @@ -1051,8 +1061,8 @@ class ForeignKey(BaseModel): id: int name: str references: List[ForeignKeyReference] - table: TableMinimal - referenced_table: TableMinimal + table: TableBrief + referenced_table: TableBrief on_update: Optional[ReferenceType] = None on_delete: Optional[ReferenceType] = None @@ -1067,8 +1077,8 @@ class CreateForeignKey(BaseModel): class PrimaryKey(BaseModel): id: int - table: TableMinimal - column: ColumnMinimal + table: TableBrief + column: ColumnBrief class Constraints(BaseModel): diff --git a/lib/python/tests/test_dtos.py b/lib/python/tests/test_dtos.py index f338c30dcc..54208a1d9a 100644 --- a/lib/python/tests/test_dtos.py +++ b/lib/python/tests/test_dtos.py @@ -1,44 +1,94 @@ import inspect +import logging import sys import unittest +from logging.config import dictConfig +from math import floor from yaml import safe_load from dbrepo.api import dto +logging.addLevelName(level=logging.NOTSET, levelName='TRACE') +logging.basicConfig(level=logging.DEBUG) + +# logging configuration +dictConfig({ + 'version': 1, + 'formatters': { + 'default': { + 'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s', + }, + 'simple': { + 'format': '[%(asctime)s] %(levelname)s: %(message)s', + }, + }, + 'handlers': {'console': { + 'class': 'logging.StreamHandler', + 'stream': 'ext://sys.stdout', + 'formatter': 'simple' # default + }}, + 'root': { + 'level': 'DEBUG', + 'handlers': ['console'] + } +}) + class AnalyseUnitTest(unittest.TestCase): schemas = None models: [()] = [] found: int = 0 - skipped: int = 0 + skipped: [str] = [] def setUp(self): with open('../../../.docs/.openapi/api.yaml', 'r') as f: self.schemas = safe_load(f)['components']['schemas'] for name, obj in inspect.getmembers(sys.modules[dto.__name__]): + self.found += 1 if not inspect.isclass(obj): - self.found += 1 continue if f'{name}Dto' not in self.schemas: - self.skipped += 1 + logging.debug(f'skip model {name}: OpenAPI schema definition {name}Dto not found') + self.skipped.append(f'{name}Dto') continue self.models.append((name, obj)) def build_model(self, name: str, obj: any, definition: any) -> dict: model_dict = dict() for property in definition['properties']: - if 'example' not in definition['properties'][property]: + if 'example' in definition['properties'][property]: if '$ref' not in definition['properties'][property]: - self.fail(f'OpenAPI model {name}Dto does not have example for property: {property}') + model_dict[property] = definition['properties'][property]['example'] + continue + ref = definition['properties'][property]['$ref'][len('#/components/schemas/'):-3] + # recursive call + model_dict[property] = self.build_model(ref, self.get_model(ref), self.schemas[f'{ref}Dto']) + continue + if 'items' in definition['properties'][property]: + if '$ref' not in definition['properties'][property]['items']: + continue + ref = definition['properties'][property]['items']['$ref'][len('#/components/schemas/'):-3] + # recursive call + model_dict[property] = [self.build_model(ref, self.get_model(ref), self.schemas[f'{ref}Dto'])] + continue + if '$ref' in definition['properties'][property]: ref = definition['properties'][property]['$ref'][len('#/components/schemas/'):-3] # recursive call - model_dict[property] = self.build_model(ref, obj, self.schemas[f'{name}Dto']) - model_dict[property] = definition['properties'][property]['example'] - model = obj(**model_dict) + model_dict[property] = self.build_model(ref, self.get_model(ref), self.schemas[f'{ref}Dto']) + return model_dict - def test_dtos_succeeds(self): + def get_model(self, ref: str): for name, obj in self.models: - self.build_model(name, obj, self.schemas[f'{name}Dto']) + if name == ref: + return obj + return None - pass + def test_dtos_succeeds(self): + logging.info(f'Found {self.found} model(s) in {dto.__name__}') + for name, obj in self.models: + logging.debug(f'building model: {name} against OpenAPI schema definition {name}Dto') + model = obj(**self.build_model(name, obj, self.schemas[f'{name}Dto'])) + logging.warning(f'Unable to find {len(self.skipped)} OpenAPI schema definition(s): {self.skipped}') + logging.info(f'Coverage: {floor((1 - len(self.skipped) / self.found) * 100)}%') + pass -- GitLab From 618708e55b00eca3c994d56e674a5e450eb33dde Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Thu, 23 Jan 2025 18:10:28 +0100 Subject: [PATCH 10/19] Fixed lint Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .docker/docker-compose.yml | 4 ++-- .gitlab-ci.yml | 9 +++++---- docker-compose.yml | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml index b6a7478e82..2db8fc6185 100644 --- a/.docker/docker-compose.yml +++ b/.docker/docker-compose.yml @@ -88,13 +88,12 @@ services: environment: KEYCLOAK_ENABLE_HTTPS: "false" KEYCLOAK_ENABLE_STATISTICS: "true" - KEYCLOAK_ENABLE_HEALTH_ENDPOINTS: "true" KEYCLOAK_DATABASE_HOST: "auth-db" KEYCLOAK_DATABASE_NAME: "${AUTH_DB_NAME:-keycloak}" KEYCLOAK_DATABASE_USER: "${AUTH_DB_USERNAME:-keycloak}" KEYCLOAK_DATABASE_PASSWORD: "${AUTH_DB_PASSWORD:-dbrepo}" healthcheck: - test: curl --head -fsS http://localhost:9000/health/ready + test: curl -fsS http://localhost:8080/realms/master interval: 10s timeout: 5s retries: 12 @@ -109,6 +108,7 @@ services: dbrepo-auth-service-init: init: true restart: "no" + container_name: dbrepo-auth-service-init image: registry.datalab.tuwien.ac.at/dbrepo/metadata-service:1.6.2 environment: AUTH_SERVICE_ADMIN: ${AUTH_SERVICE_ADMIN:-admin} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4e4c751bf4..43b8b17a6f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -55,22 +55,23 @@ lint-docker-compose: - "IGNORE_IMAGE=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-analyse-service'" - "IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-auth-db'" - "IGNORE_IMAGE=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-auth-service'" + - "IGNORE_VOLUMES=1 IGNORE_IMAGE=1 bash .scripts/check-service.sh 'dbrepo-auth-service-init'" - "IGNORE_IMAGE=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-broker-service'" - "IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-dashboard-service'" - "bash .scripts/check-service.sh 'dbrepo-data-db'" - "IGNORE_IMAGE=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-data-service'" - "IGNORE_VOLUMES=1 bash .scripts/check-service.sh 'dbrepo-gateway-service'" - - "IGNORE_VOLUMES=1 bash .scripts/check-service.sh 'dbrepo-identity-service'" + - "IGNORE_VOLUMES=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-identity-service'" - "IGNORE_VOLUMES=1 bash .scripts/check-service.sh 'dbrepo-metadata-db'" - "IGNORE_IMAGE=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-metadata-service'" - - "IGNORE_VOLUMES=1 bash .scripts/check-service.sh 'dbrepo-metric-db'" + - "IGNORE_VOLUMES=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-metric-db'" - "IGNORE_IMAGE=1 bash .scripts/check-service.sh 'dbrepo-search-db'" - "IGNORE_IMAGE=1 IGNORE_PORTS=1 bash .scripts/check-service.sh 'dbrepo-search-service'" - "IGNORE_IMAGE=1 bash .scripts/check-service.sh 'dbrepo-search-service-init'" - "IGNORE_VOLUMES=1 bash .scripts/check-service.sh 'dbrepo-storage-service'" - - "IGNORE_VOLUMES=1 bash .scripts/check-service.sh 'dbrepo-storage-service-init'" + - "IGNORE_VOLUMES=1 IGNORE_IMAGE=1 bash .scripts/check-service.sh 'dbrepo-storage-service-init'" - "IGNORE_IMAGE=1 bash .scripts/check-service.sh 'dbrepo-ui'" - - "bash .scripts/check-service.sh 'dbrepo-upload-service'" + - "IGNORE_VOLUMES=1 bash .scripts/check-service.sh 'dbrepo-upload-service'" lint-helm-chart: image: docker.io/alpine:${ALPINE_VERSION} diff --git a/docker-compose.yml b/docker-compose.yml index 5a0d1a4242..ad1dacb6b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -110,8 +110,8 @@ services: dbrepo-auth-service-init: init: true restart: "no" - image: dbrepo-auth-service-init:latest container_name: dbrepo-auth-service-init + image: dbrepo-auth-service-init:latest build: context: ./dbrepo-auth-service/init network: host -- GitLab From c9fd3c50e4aa3c6b7c002ccfe1bdf0ef2077f0eb Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Thu, 23 Jan 2025 22:31:30 +0100 Subject: [PATCH 11/19] Fixed some UI bugs and paginating for Spark Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- dbrepo-data-service/Dockerfile | 4 +- .../at/tuwien/endpoints/RestEndpoint.java | 1 + .../impl/MetadataServiceGatewayImpl.java | 1 + .../java/at/tuwien/mapper/MariaDbMapper.java | 47 +++++++++---------- .../at/tuwien/service/impl/DataConnector.java | 3 +- .../service/impl/TableServiceMariaDbImpl.java | 14 ++++-- dbrepo-metadata-service/Dockerfile | 4 +- dbrepo-ui/components/subset/Results.vue | 18 +++++-- dbrepo-ui/components/subset/SubsetToolbar.vue | 12 +++-- dbrepo-ui/composables/table-service.ts | 26 +++++----- dbrepo-ui/nuxt.config.ts | 4 +- .../[database_id]/subset/[subset_id]/data.vue | 8 ++-- .../[database_id]/table/[table_id]/data.vue | 2 +- dbrepo-ui/stores/cache.js | 8 ++++ 14 files changed, 88 insertions(+), 64 deletions(-) diff --git a/dbrepo-data-service/Dockerfile b/dbrepo-data-service/Dockerfile index f4e2be5b96..4b45e94290 100644 --- a/dbrepo-data-service/Dockerfile +++ b/dbrepo-data-service/Dockerfile @@ -8,7 +8,7 @@ LABEL org.opencontainers.image.authors="martin.weise@tuwien.ac.at" COPY ./pom.xml ./ -RUN mvn -fn -B dependency:go-offline +RUN mvn -fn -B -q dependency:go-offline COPY --from=dependency /root/.m2/repository/at/tuwien /root/.m2/repository/at/tuwien @@ -18,7 +18,7 @@ COPY ./rest-service ./rest-service COPY ./services ./services # Make sure it compiles -RUN mvn clean package -DskipTests +RUN mvn -fn -B -q clean package -DskipTests ###### THIRD STAGE ###### FROM amazoncorretto:17-alpine3.19 AS runtime diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/RestEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/RestEndpoint.java index 333e0c8398..45cea4371c 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/RestEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/RestEndpoint.java @@ -45,6 +45,7 @@ public abstract class RestEndpoint { return UUID.fromString(user.getId()); } + /* FIXME: Heap may run OOM */ public List<Map<String, Object>> transform(Dataset<Row> dataset) { return dataset.collectAsList() .stream() diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java index 4dcfaf13a2..650652d62f 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java @@ -115,6 +115,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { database.setJdbcMethod(response.getHeaders().get("X-Type").get(0)); database.setUsername(response.getHeaders().get("X-Username").get(0)); database.setPassword(response.getHeaders().get("X-Password").get(0)); + database.setDatabase(database.getInternalName()); database.setHost(response.getHeaders().get("X-Host").get(0)); database.setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0))); database.setLastRetrieved(Instant.now()); 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 7f5b63b21c..d3af548164 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 @@ -231,12 +231,6 @@ public interface MariaDbMapper { return statement.toString(); } - default String tableCreateDtoToSequenceName(at.tuwien.api.database.table.internal.TableCreateDto data) { - final String name = "seq_" + nameToInternalName(data.getName()) + "_id"; - log.trace("mapped table name {} to sequence name {}", data.getName(), name); - return name; - } - /** * Maps the desired data type to a MySQL string with the default MySQL 8 values for each * @@ -731,27 +725,30 @@ public interface MariaDbMapper { } } - default String selectRawSelectQuery(String query, Instant timestamp, Long page, Long size) { - query = query.toLowerCase(Locale.ROOT) - .trim(); - if (query.matches(";$")) { - /* remove last semicolon */ - query = query.substring(0, query.length() - 1); - } + default String defaultRawSelectQuery(String databaseName, String tableOrViewName, Instant timestamp, Long page, + Long size) { /* query check (this is enforced by the db also) */ - final StringBuilder statement = new StringBuilder("SELECT * FROM (") - .append(query) - .append(") FOR SYSTEM_TIME AS OF TIMESTAMP '") - .append(mariaDbFormatter.format(timestamp)) - .append("' as tbl"); + final StringBuilder statement = new StringBuilder("SELECT * FROM (SELECT * FROM `") + .append(databaseName) + .append("`.`") + .append(tableOrViewName) + .append("`"); + if (timestamp != null) { + statement.append(" FOR SYSTEM_TIME AS OF TIMESTAMP '") + .append(mariaDbFormatter.format(timestamp)) + .append("'"); + } + statement.append(" as tbl"); /* pagination */ - log.trace("pagination size/limit of {}", size); - statement.append(" LIMIT ") - .append(size); - log.trace("pagination page/offset of {}", page); - statement.append(" OFFSET ") - .append(page * size); - statement.append(";"); + if (size != null && page != null) { + log.trace("pagination size/limit of {}", size); + statement.append(" LIMIT ") + .append(size); + log.trace("pagination page/offset of {}", page); + statement.append(" OFFSET ") + .append(page * size); + } + statement.append(") as tbl2"); log.trace("mapped select query: {}", statement); return statement.toString(); } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java index 1044869ad8..3a54b399d4 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java @@ -10,7 +10,6 @@ import org.springframework.stereotype.Service; public abstract class DataConnector<T extends CacheableDto> { public ComboPooledDataSource getDataSource(T entity) { - final long start = System.currentTimeMillis(); final ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setJdbcUrl(getJdbcUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPort(), entity.getDatabase())); @@ -25,7 +24,6 @@ public abstract class DataConnector<T extends CacheableDto> { } public ComboPooledDataSource getDataSource(T entity, String databaseName) { - final long start = System.currentTimeMillis(); final ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setJdbcUrl(getJdbcUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPort(), databaseName)); dataSource.setUser(entity.getUsername()); @@ -60,6 +58,7 @@ public abstract class DataConnector<T extends CacheableDto> { stringBuilder.append("/") .append(databaseName); } + log.trace("mapped jdbc url: {}", stringBuilder); return stringBuilder.toString(); } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java index 68fb59cb5e..482f874624 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java @@ -373,12 +373,16 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements Long page, Long size, SortTypeDto sortDirection, String sortColumn) throws QueryMalformedException, TableNotFoundException { try { - final Properties properties = new Properties(); - properties.setProperty("user", database.getUsername()); - properties.setProperty("password", database.getPassword()); return sparkSession.read() - .jdbc(getSparkUrl(database.getJdbcMethod(), database.getHost(), database.getPort(), - database.getInternalName()), tableOrView, properties); + .format("jdbc") + .option("user", database.getUsername()) + .option("password", database.getPassword()) + .option("url", getSparkUrl(database.getJdbcMethod(), database.getHost(), database.getPort(), + database.getInternalName())) + .option("query", mariaDbMapper.defaultRawSelectQuery(database.getInternalName(), tableOrView, + timestamp, page, size)) + .load(); + } catch (Exception e) { if (e instanceof ExtendedAnalysisException exception) { if (exception.getSimpleMessage().contains("TABLE_OR_VIEW_NOT_FOUND")) { diff --git a/dbrepo-metadata-service/Dockerfile b/dbrepo-metadata-service/Dockerfile index 843c334a9a..ddc20cb420 100644 --- a/dbrepo-metadata-service/Dockerfile +++ b/dbrepo-metadata-service/Dockerfile @@ -12,7 +12,7 @@ COPY ./rest-service/pom.xml ./rest-service/ COPY ./services/pom.xml ./services/ COPY ./test/pom.xml ./test/ -RUN mvn verify -B -fn +RUN mvn -fn -B dependency:go-offline COPY ./api ./api COPY ./entities ./entities @@ -24,7 +24,7 @@ COPY ./services ./services COPY ./test ./test # Make sure it compiles -RUN mvn clean install -DskipTests +RUN mvn -fn -B clean install -DskipTests ###### SECOND STAGE ###### FROM amazoncorretto:17-alpine3.19 AS runtime diff --git a/dbrepo-ui/components/subset/Results.vue b/dbrepo-ui/components/subset/Results.vue index e558186daf..3948667518 100644 --- a/dbrepo-ui/components/subset/Results.vue +++ b/dbrepo-ui/components/subset/Results.vue @@ -110,9 +110,13 @@ export default { this.id = id this.loadingExecute = false }) - .catch(({code}) => { + .catch(({code, message}) => { this.loadingExecute = false const toast = useToastInstance() + if (message) { + toast.error(message) + return + } if (typeof code !== 'string') { return } @@ -129,9 +133,13 @@ export default { this.id = id this.loadingExecute = false }) - .catch(({code}) => { + .catch(({code, message}) => { this.loadingExecute = false const toast = useToastInstance() + if (message) { + toast.error(message) + return + } if (typeof code !== 'string') { return } @@ -148,9 +156,13 @@ export default { this.id = id this.loadingExecute = false }) - .catch(({code}) => { + .catch(({code, message}) => { this.loadingExecute = false const toast = useToastInstance() + if (message) { + toast.error(message) + return + } if (typeof code !== 'string') { return } diff --git a/dbrepo-ui/components/subset/SubsetToolbar.vue b/dbrepo-ui/components/subset/SubsetToolbar.vue index 874e691c5f..d5f45e48e3 100644 --- a/dbrepo-ui/components/subset/SubsetToolbar.vue +++ b/dbrepo-ui/components/subset/SubsetToolbar.vue @@ -35,7 +35,6 @@ variant="flat" class="mr-2" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-content-save-outline' : null" - :disabled="!executionUTC" :to="`/database/${$route.params.database_id}/subset/${$route.params.subset_id}/persist`"> {{ ($vuetify.display.lgAndUp ? $t('toolbars.subset.pid.xl') + ' ' : '') + $t('toolbars.subset.pid.permanent') }} </v-btn> @@ -177,8 +176,9 @@ export default { this.loadingSave = true const queryService = useQueryService() queryService.update(this.$route.params.database_id, this.$route.params.subset_id, { persist: true }) - .then((subset) => { - this.subset = subset + .then(() => { + const cacheStore = useCacheStore() + cacheStore.reloadSubset() this.loadingSave = false }) .catch(() => { @@ -192,8 +192,10 @@ export default { this.loadingSave = true const queryService = useQueryService() queryService.update(this.$route.params.database_id, this.$route.params.subset_id, { persist: false }) - .then((subset) => { - this.subset = subset + .then(() => { + const cacheStore = useCacheStore() + cacheStore.reloadSubset() + this.loadingSave = false }) .catch(() => { this.loadingSave = false diff --git a/dbrepo-ui/composables/table-service.ts b/dbrepo-ui/composables/table-service.ts index ca757c7451..45268d6295 100644 --- a/dbrepo-ui/composables/table-service.ts +++ b/dbrepo-ui/composables/table-service.ts @@ -9,7 +9,7 @@ export const useTableService = (): any => { return new Promise<TableBriefDto>((resolve, reject) => { axios.get<TableBriefDto>(`/api/database/${databaseId}/table`) .then((response) => { - console.info('Found tables(s)') + console.info(`Found ${response.data.length} tables(s)`) resolve(response.data) }) .catch((error) => { @@ -25,7 +25,7 @@ export const useTableService = (): any => { return new Promise<TableDto>((resolve, reject) => { axios.get<TableDto>(`/api/database/${databaseId}/table/${tableId}`) .then((response) => { - console.info('Found table with id', tableId, 'in database with id', databaseId); + console.info('Found table'); resolve(response.data) }) .catch((error) => { @@ -41,7 +41,7 @@ export const useTableService = (): any => { return new Promise<ColumnDto>((resolve, reject) => { axios.put<ColumnDto>(`/api/database/${databaseId}/table/${tableId}/column/${columnId}`, data) .then((response) => { - console.info('Updated column with id', columnId, 'table with id', tableId, 'in database with id', databaseId); + console.info('Updated column'); resolve(response.data) }) .catch((error) => { @@ -57,7 +57,7 @@ export const useTableService = (): any => { return new Promise<TableDto>((resolve, reject) => { axios.put<TableDto>(`/api/database/${databaseId}/table/${tableId}`, data) .then((response) => { - console.info('Updated table with id', tableId, 'in database with id', databaseId); + console.info('Updated table'); resolve(response.data) }) .catch((error) => { @@ -73,7 +73,7 @@ export const useTableService = (): any => { return new Promise<ImportDto>((resolve, reject) => { axios.post<ImportDto>(`/api/database/${databaseId}/table/${tableId}/data/import`, data) .then((response) => { - console.info('Imported csv to table with id', tableId, 'in database with id', databaseId) + console.info('Imported csv to table') resolve(response.data) }) .catch((error) => { @@ -89,7 +89,7 @@ export const useTableService = (): any => { return new Promise<QueryResultDto>((resolve, reject) => { axios.get<QueryResultDto>(`/api/database/${databaseId}/table/${tableId}/data`, { params: mapFilter(timestamp, page, size) }) .then((response) => { - console.info('Got data for table with id', tableId, 'in database with id', databaseId) + console.info('Got data for table') const result: QueryResultDto = { id: tableId, headers: response.headers['x-headers'] ? response.headers['x-headers'].split(',') : [], @@ -111,7 +111,7 @@ export const useTableService = (): any => { axios.head<void>(`/api/database/${databaseId}/table/${tableId}/data`, { params: mapFilter(timestamp, null, null) }) .then((response: AxiosResponse<void>) => { const count: number = Number(response.headers['x-count']) - console.info('Found' + count + 'in table with id', tableId, 'in database with id', databaseId) + console.info(`Found ${count} tuple(s)`) resolve(count) }) .catch((error) => { @@ -134,7 +134,7 @@ export const useTableService = (): any => { return new Promise<QueryResultDto>((resolve, reject) => { axios.get<QueryResultDto>(`/api/database/${databaseId}/table/${tableId}/export`, config) .then((response) => { - console.info('Exported data for table with id', tableId, 'in database with id', databaseId) + console.info('Exported data for table') resolve(response.data) }) .catch((error) => { @@ -150,7 +150,7 @@ export const useTableService = (): any => { return new Promise<TableDto>((resolve, reject) => { axios.post<TableDto>(`/api/database/${databaseId}/table`, data) .then((response) => { - console.info('Created table in database with id', databaseId) + console.info('Created table') resolve(response.data) }) .catch((error: AxiosError) => { @@ -166,7 +166,7 @@ export const useTableService = (): any => { return new Promise<void>((resolve, reject) => { axios.delete<void>(`/api/database/${databaseId}/table/${tableId}`) .then((response) => { - console.info('Deleted table with id', tableId, 'in database with id', databaseId) + console.info('Deleted table') resolve(response.data) }) .catch((error) => { @@ -182,7 +182,7 @@ export const useTableService = (): any => { return new Promise<void>((resolve, reject) => { axios.delete<void>(`/api/database/${databaseId}/table/${tableId}`, {data}) .then((response) => { - console.info('Deleted tuple(s) in table with id', tableId, 'in database with id', databaseId) + console.info(`Deleted tuple(s)`) resolve(response.data) }) .catch((error) => { @@ -198,7 +198,7 @@ export const useTableService = (): any => { return new Promise<TableHistoryDto[]>((resolve, reject) => { axios.get<TableHistoryDto[]>(`/api/database/${databaseId}/table/${tableId}/history`) .then((response) => { - console.info('Loaded history of table with id', tableId, 'in database with id', databaseId) + console.info('Loaded history of table') resolve(response.data) }) .catch((error) => { @@ -214,7 +214,7 @@ export const useTableService = (): any => { return new Promise<TableColumnEntityDto[]>((resolve, reject) => { axios.get<TableColumnEntityDto[]>(`/api/database/${databaseId}/table/${tableId}/column/${columnId}/suggest`) .then((response) => { - console.info('Suggested semantic entities for table column with id', columnId, 'of table with id', tableId, 'of database with id', databaseId) + console.info('Suggested semantic entities') resolve(response.data) }) .catch((error) => { diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts index 70084f3cca..4b1833d816 100644 --- a/dbrepo-ui/nuxt.config.ts +++ b/dbrepo-ui/nuxt.config.ts @@ -75,8 +75,8 @@ export default defineNuxtConfig({ } }, api: { - client: 'https://dbrepo1.ec.tuwien.ac.at', - server: 'https://dbrepo1.ec.tuwien.ac.at', + client: 'http://localhost', + server: 'http://gateway-service', }, upload: { client: 'http://localhost/api/upload/files', diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue index f96b6e4175..e9719cd0ef 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue @@ -119,7 +119,7 @@ export default { }, }, mounted () { - this.loadResult() + this.loadSubset() }, methods: { loadSubset () { @@ -128,7 +128,9 @@ export default { queryService.findOne(this.$route.params.database_id, this.$route.params.subset_id) .then((subset) => { this.subset = subset - this.loadResult() + this.$refs.queryResults.reExecute(subset.id) + this.$refs.queryResults.reExecuteCount(subset.id) + this.loadingSubset = false }) .catch(() => { this.loadingSubset = false @@ -139,8 +141,6 @@ export default { }, loadResult () { if (this.subset) { - this.$refs.queryResults.reExecute(this.subset.id) - this.$refs.queryResults.reExecuteCount(this.subset.id) } }, download () { 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 2eea6a69bf..07747ed0cb 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 @@ -127,7 +127,7 @@ export default { data () { return { loading: true, - error: true, + error: false, loadingData: false, loadingCount: false, loadingDelete: false, diff --git a/dbrepo-ui/stores/cache.js b/dbrepo-ui/stores/cache.js index 3574b24d7c..41059ba727 100644 --- a/dbrepo-ui/stores/cache.js +++ b/dbrepo-ui/stores/cache.js @@ -81,6 +81,14 @@ export const useCacheStore = defineStore('cache', { console.error('Failed to reload view', error) }) }, + reloadSubset() { + const queryService = useQueryService() + queryService.findOne(this.subset.database_id, this.subset.id) + .then(subset => this.subset = subset) + .catch((error) => { + console.error('Failed to reload subset', error) + }) + }, setRouteDatabase (databaseId) { return new Promise((resolve, reject) => { if (!databaseId) { -- GitLab From fc67996b5df5256c6f0f52e858b3d230c29cf518 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Thu, 23 Jan 2025 23:21:58 +0100 Subject: [PATCH 12/19] WIP Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .../gateway/DataServiceGatewayUnitTest.java | 14 ++++---- lib/python/dbrepo/api/dto.py | 2 +- lib/python/tests/test_dtos.py | 2 +- lib/python/tests/test_unit_database.py | 4 +-- lib/python/tests/test_unit_identifier.py | 14 ++++---- lib/python/tests/test_unit_table.py | 36 +++++++++++++++---- 6 files changed, 46 insertions(+), 26 deletions(-) diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java index c423d6b1ae..daeb1c1a96 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/DataServiceGatewayUnitTest.java @@ -2,7 +2,7 @@ package at.tuwien.gateway; import at.tuwien.ExportResourceDto; import at.tuwien.api.database.AccessTypeDto; -import at.tuwien.api.database.DatabaseBriefDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.table.TableDto; @@ -252,9 +252,9 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { DatabaseNotFoundException { /* mock */ - when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseBriefDto.class))) + when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class))) .thenReturn(ResponseEntity.status(HttpStatus.CREATED) - .body(DATABASE_1_BRIEF_DTO)); + .body(DATABASE_1_DTO)); /* test */ dataServiceGateway.createDatabase(DATABASE_1_CREATE_INTERNAL); @@ -266,7 +266,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ doThrow(HttpServerErrorException.class) .when(dataServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseBriefDto.class)); + .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class)); /* test */ assertThrows(DataServiceConnectionException.class, () -> { @@ -280,7 +280,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ doThrow(HttpClientErrorException.Unauthorized.class) .when(dataServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseBriefDto.class)); + .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class)); /* test */ assertThrows(DataServiceException.class, () -> { @@ -294,7 +294,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ doThrow(HttpClientErrorException.BadRequest.class) .when(dataServiceRestTemplate) - .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseBriefDto.class)); + .exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class)); /* test */ assertThrows(DataServiceException.class, () -> { @@ -306,7 +306,7 @@ public class DataServiceGatewayUnitTest extends AbstractUnitTest { public void createDatabase_responseCode_fails() { /* mock */ - when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseBriefDto.class))) + when(dataServiceRestTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class), eq(DatabaseDto.class))) .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT) .build()); diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py index 76f6d37a3a..b7556a03a5 100644 --- a/lib/python/dbrepo/api/dto.py +++ b/lib/python/dbrepo/api/dto.py @@ -113,11 +113,11 @@ class ContainerBrief(BaseModel): class ColumnBrief(BaseModel): id: int name: str - alias: str database_id: int table_id: int internal_name: str type: ColumnType + alias: Optional[str] = None class TableBrief(BaseModel): diff --git a/lib/python/tests/test_dtos.py b/lib/python/tests/test_dtos.py index 54208a1d9a..0a90949dba 100644 --- a/lib/python/tests/test_dtos.py +++ b/lib/python/tests/test_dtos.py @@ -42,7 +42,7 @@ class AnalyseUnitTest(unittest.TestCase): skipped: [str] = [] def setUp(self): - with open('../../../.docs/.openapi/api.yaml', 'r') as f: + with open('../../.docs/.openapi/api.yaml', 'r') as f: self.schemas = safe_load(f)['components']['schemas'] for name, obj in inspect.getmembers(sys.modules[dto.__name__]): self.found += 1 diff --git a/lib/python/tests/test_unit_database.py b/lib/python/tests/test_unit_database.py index 7605809e61..203c296b9c 100644 --- a/lib/python/tests/test_unit_database.py +++ b/lib/python/tests/test_unit_database.py @@ -4,7 +4,7 @@ import requests_mock from pydantic_core import ValidationError from dbrepo.RestClient import RestClient -from dbrepo.api.dto import Database, Container, Image, DatabaseAccess, AccessType, DatabaseBrief, UserBrief, \ +from dbrepo.api.dto import Database, DatabaseAccess, AccessType, DatabaseBrief, UserBrief, \ ContainerBrief, ImageBrief from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, ForbiddenError, MalformedError, AuthenticationError @@ -24,7 +24,7 @@ class DatabaseUnitTest(unittest.TestCase): DatabaseBrief( id=1, name='test', - owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'), + owner_id='8638c043-5145-4be8-a3e4-4b79991b0a16', contact=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'), internal_name='test_abcd', is_public=True, diff --git a/lib/python/tests/test_unit_identifier.py b/lib/python/tests/test_unit_identifier.py index 45b0a919e7..0c71c0216f 100644 --- a/lib/python/tests/test_unit_identifier.py +++ b/lib/python/tests/test_unit_identifier.py @@ -1,14 +1,12 @@ import unittest import requests_mock -import datetime from dbrepo.RestClient import RestClient - -from dbrepo.api.dto import Identifier, IdentifierType, CreateIdentifierTitle, CreateIdentifierCreator, \ - IdentifierCreator, IdentifierTitle, IdentifierDescription, CreateIdentifierDescription, Language, \ - CreateIdentifierFunder, CreateRelatedIdentifier, RelatedIdentifierRelation, RelatedIdentifierType, IdentifierFunder, \ - RelatedIdentifier, UserBrief, IdentifierStatusType +from dbrepo.api.dto import Identifier, IdentifierType, CreateIdentifierTitle, Creator, IdentifierTitle, \ + IdentifierDescription, CreateIdentifierDescription, Language, CreateIdentifierFunder, CreateRelatedIdentifier, \ + RelatedIdentifierRelation, RelatedIdentifierType, IdentifierFunder, RelatedIdentifier, UserBrief, \ + IdentifierStatusType, CreateIdentifierCreator from dbrepo.api.exceptions import MalformedError, ForbiddenError, NotExistsError, AuthenticationError @@ -29,7 +27,7 @@ class IdentifierUnitTest(unittest.TestCase): related_identifiers=[ RelatedIdentifier(id=7, value='10.12345/abc', relation=RelatedIdentifierRelation.CITES, type=RelatedIdentifierType.DOI)], - creators=[IdentifierCreator(id=5, creator_name='Carberry, Josiah')], + creators=[Creator(id=5, creator_name='Carberry, Josiah')], status=IdentifierStatusType.PUBLISHED, owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')) # mock @@ -127,7 +125,7 @@ class IdentifierUnitTest(unittest.TestCase): related_identifiers=[RelatedIdentifier(id=7, value='10.12345/abc', relation=RelatedIdentifierRelation.CITES, type=RelatedIdentifierType.DOI)], - creators=[IdentifierCreator(id=5, creator_name='Carberry, Josiah')], + creators=[Creator(id=5, creator_name='Carberry, Josiah')], status=IdentifierStatusType.PUBLISHED, owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'))] # mock diff --git a/lib/python/tests/test_unit_table.py b/lib/python/tests/test_unit_table.py index 4c4ad2af6a..36391e1fdd 100644 --- a/lib/python/tests/test_unit_table.py +++ b/lib/python/tests/test_unit_table.py @@ -6,7 +6,7 @@ from pandas import DataFrame from dbrepo.RestClient import RestClient from dbrepo.api.dto import Table, CreateTableConstraints, Column, Constraints, ColumnType, ConceptBrief, UnitBrief, \ - TableStatistics, ColumnStatistic, PrimaryKey, TableMinimal, ColumnMinimal, TableBrief, UserBrief + TableStatistics, ColumnStatistic, PrimaryKey, ColumnBrief, TableBrief, UserBrief from dbrepo.api.exceptions import MalformedError, ForbiddenError, NotExistsError, NameExistsError, \ AuthenticationError, ExternalSystemError @@ -29,9 +29,20 @@ class TableUnitTest(unittest.TestCase): foreign_keys=[], checks=[], primary_key=[PrimaryKey(id=1, - table=TableMinimal(id=2, database_id=1), - column=ColumnMinimal(id=1, table_id=2, - database_id=1))]), + table=TableBrief(id=2, database_id=1, + name='Other', + internal_name='other', + description=None, + is_versioned=True, + is_public=True, + is_schema_public=True, + owned_by='8638c043-5145-4be8-a3e4-4b79991b0a16'), + column=ColumnBrief(id=1, table_id=2, + database_id=1, + name='id', + alias=None, + internal_name='id', + type=ColumnType.BIGINT))]), columns=[Column(id=1, ord=0, name="ID", @@ -159,9 +170,20 @@ class TableUnitTest(unittest.TestCase): foreign_keys=[], checks=[], primary_key=[PrimaryKey(id=1, - table=TableMinimal(id=2, database_id=1), - column=ColumnMinimal(id=1, table_id=2, - database_id=1))]), + table=TableBrief(id=2, database_id=1, + name='Other', + internal_name='other', + description=None, + is_versioned=True, + is_public=True, + is_schema_public=True, + owned_by='8638c043-5145-4be8-a3e4-4b79991b0a16'), + column=ColumnBrief(id=1, table_id=2, + database_id=1, + name='id', + alias=None, + internal_name='id', + type=ColumnType.BIGINT))]), columns=[Column(id=1, name="ID", ord=0, -- GitLab From bfa569edc65491d77ed81b3a2fbc27b75c1a4972 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Fri, 24 Jan 2025 07:46:44 +0100 Subject: [PATCH 13/19] Updated tests Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .gitlab-ci.yml | 2 +- .../endpoint/SubsetEndpointUnitTest.java | 32 +++++++------------ .../endpoint/TableEndpointUnitTest.java | 12 +++---- .../main/java/at/tuwien/test/BaseTest.java | 5 +++ 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 43b8b17a6f..3661711509 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -266,7 +266,7 @@ test-data-service: dependencies: - build-data-service script: - - "mvn -f ./dbrepo-metadata-service/pom.xml clean install $MAVEN_OPTS" + - "mvn -f ./dbrepo-metadata-service/pom.xml clean install -DskipTests $MAVEN_OPTS" - "mvn -f ./dbrepo-data-service/pom.xml clean test verify $MAVEN_OPTS" - "cat ./dbrepo-data-service/report/target/site/jacoco-aggregate/index.html | grep -o 'Total[^%]*%' | sed 's/<.*>/ /; s/Total/Jacoco Coverage Total:/'" artifacts: diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java index c76aa91ebf..002374fe9d 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java @@ -128,14 +128,12 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void findById_privateDataPrivateSchemaAnonymous_fails() throws DatabaseNotFoundException, SQLException, - RemoteUnavailableException, UserNotFoundException, QueryNotFoundException, MetadataServiceException { + public void findById_privateDataPrivateSchemaAnonymous_fails() throws DatabaseNotFoundException, + RemoteUnavailableException, MetadataServiceException { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) .thenReturn(DATABASE_1_DTO); - when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) - .thenReturn(QUERY_1_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -208,7 +206,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(QUERY_5_DTO); when(storageService.transformDataset(any(Dataset.class))) .thenReturn(EXPORT_RESOURCE_DTO); - when(subsetService.getData(any(DatabaseDto.class), any(QueryDto.class), eq(null), eq(null))) + when(subsetService.getData(any(DatabaseDto.class), any(QueryDto.class), eq(0L), eq(10L))) .thenReturn(mock); /* test */ @@ -218,20 +216,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser public void findById_publicDataPrivateSchemaAnonymous_fails() throws DatabaseNotFoundException, - RemoteUnavailableException, UserNotFoundException, StorageUnavailableException, QueryMalformedException, - QueryNotFoundException, SQLException, MetadataServiceException, TableNotFoundException, - ViewMalformedException { - final Dataset<Row> mock = sparkSession.emptyDataFrame(); + RemoteUnavailableException, MetadataServiceException { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) .thenReturn(DATABASE_3_DTO); - when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) - .thenReturn(QUERY_5_DTO); - when(subsetService.getData(any(DatabaseDto.class), any(QueryDto.class), eq(null), eq(null))) - .thenReturn(mock); - when(storageService.transformDataset(any(Dataset.class))) - .thenReturn(EXPORT_RESOURCE_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -251,7 +240,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_DTO); when(subsetService.findById(DATABASE_4_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(any(DatabaseDto.class), any(QueryDto.class), eq(null), eq(null))) + when(subsetService.getData(any(DatabaseDto.class), any(QueryDto.class), eq(0L), eq(10L))) .thenReturn(mock); when(storageService.transformDataset(any(Dataset.class))) .thenReturn(EXPORT_RESOURCE_DTO); @@ -298,9 +287,10 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME) - public void findById_publicDataPrivateSchemaUnavailableExport_fails() throws DatabaseNotFoundException, RemoteUnavailableException, - MetadataServiceException, SQLException, QueryMalformedException, UserNotFoundException, - QueryNotFoundException, TableNotFoundException, ViewMalformedException, StorageUnavailableException { + public void findById_publicDataPrivateSchemaUnavailableExport_fails() throws DatabaseNotFoundException, + RemoteUnavailableException, MetadataServiceException, SQLException, QueryMalformedException, + UserNotFoundException, QueryNotFoundException, TableNotFoundException, ViewMalformedException, + StorageUnavailableException { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) @@ -311,7 +301,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(EXPORT_RESOURCE_DTO); doThrow(SQLException.class) .when(subsetService) - .getData(eq(DATABASE_3_DTO), eq(QUERY_5_DTO), eq(null), eq(null)); + .getData(eq(DATABASE_3_DTO), eq(QUERY_5_DTO), eq(0L), eq(10L)); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -404,7 +394,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_DTO); doThrow(SQLException.class) .when(subsetService) - .getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(null), eq(null)); + .getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(0L), eq(10L)); when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) .thenReturn(QUERY_5_DTO); when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_1_ID)) 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 82be231e56..35df8f5dd9 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 @@ -284,7 +284,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) .thenReturn(TABLE_8_DTO); - when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -307,7 +307,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(TABLE_8_DTO); when(tableService.getCount(eq(TABLE_8_DTO), any(Instant.class))) .thenReturn(3L); - when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); @@ -365,7 +365,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(TABLE_8_DTO); doThrow(QueryMalformedException.class) .when(tableService) - .getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null)); + .getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null)); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -406,7 +406,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(TABLE_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(access); - when(tableService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + when(tableService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -1147,7 +1147,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) .thenReturn(TABLE_8_DTO); - when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); /* test */ @@ -1168,7 +1168,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(TABLE_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(access); - when(tableService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + when(tableService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); /* test */ 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 bab95a366b..d732a35843 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 @@ -1379,6 +1379,7 @@ public abstract class BaseTest { public final static DatabaseDto DATABASE_3_DTO = DatabaseDto.builder() .id(DATABASE_3_ID) .isPublic(DATABASE_3_PUBLIC) + .isSchemaPublic(DATABASE_3_SCHEMA_PUBLIC) .name(DATABASE_3_NAME) .internalName(DATABASE_3_INTERNALNAME) .owner(USER_3_BRIEF_DTO) @@ -1392,6 +1393,7 @@ public abstract class BaseTest { public final static DatabaseBriefDto DATABASE_3_BRIEF_DTO = DatabaseBriefDto.builder() .id(DATABASE_3_ID) .isPublic(DATABASE_3_PUBLIC) + .isSchemaPublic(DATABASE_3_SCHEMA_PUBLIC) .name(DATABASE_3_NAME) .internalName(DATABASE_3_INTERNALNAME) .ownerId(USER_3_ID) @@ -7552,6 +7554,7 @@ public abstract class BaseTest { public final static DatabaseDto DATABASE_1_DTO = DatabaseDto.builder() .id(DATABASE_1_ID) .isPublic(DATABASE_1_PUBLIC) + .isSchemaPublic(DATABASE_1_SCHEMA_PUBLIC) .name(DATABASE_1_NAME) .container(CONTAINER_1_DTO) .internalName(DATABASE_1_INTERNALNAME) @@ -7564,6 +7567,7 @@ public abstract class BaseTest { public final static DatabaseBriefDto DATABASE_1_BRIEF_DTO = DatabaseBriefDto.builder() .id(DATABASE_1_ID) .isPublic(DATABASE_1_PUBLIC) + .isSchemaPublic(DATABASE_1_SCHEMA_PUBLIC) .name(DATABASE_1_NAME) .internalName(DATABASE_1_INTERNALNAME) .identifiers(new LinkedList<>(List.of(IDENTIFIER_1_BRIEF_DTO, IDENTIFIER_2_BRIEF_DTO, IDENTIFIER_3_BRIEF_DTO, IDENTIFIER_4_BRIEF_DTO))) @@ -7718,6 +7722,7 @@ public abstract class BaseTest { public final static DatabaseBriefDto DATABASE_2_BRIEF_DTO = DatabaseBriefDto.builder() .id(DATABASE_2_ID) .isPublic(DATABASE_2_PUBLIC) + .isSchemaPublic(DATABASE_2_SCHEMA_PUBLIC) .name(DATABASE_2_NAME) .internalName(DATABASE_2_INTERNALNAME) .identifiers(new LinkedList<>(List.of(IDENTIFIER_5_BRIEF_DTO))) -- GitLab From 673132ef161ad02ab03e1d8252fac07cf826927e Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Fri, 24 Jan 2025 09:31:13 +0100 Subject: [PATCH 14/19] WIP Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .docs/changelog.md | 18 ++- .../at/tuwien/endpoints/TableEndpoint.java | 6 +- .../endpoint/AccessEndpointUnitTest.java | 24 ++-- .../endpoint/DatabaseEndpointUnitTest.java | 8 +- .../endpoint/TableEndpointUnitTest.java | 7 +- .../MetadataServiceGatewayUnitTest.java | 36 +---- .../DefaultListenerIntegrationTest.java | 4 +- .../listener/DefaultListenerUnitTest.java | 5 +- .../service/AccessServiceIntegrationTest.java | 36 ++--- .../ContainerServiceIntegrationTest.java | 2 +- .../DatabaseServiceIntegrationTest.java | 2 +- .../service/TableServiceIntegrationTest.java | 2 +- .../impl/MetadataServiceGatewayImpl.java | 32 ++--- .../java/at/tuwien/service/TableService.java | 3 +- .../impl/AccessServiceMariaDbImpl.java | 2 +- .../impl/ContainerServiceMariaDbImpl.java | 2 +- .../at/tuwien/service/impl/DataConnector.java | 65 +++++---- .../impl/DatabaseServiceMariaDbImpl.java | 2 +- .../impl/QueueServiceRabbitMqImpl.java | 2 +- .../impl/SubsetServiceMariaDbImpl.java | 11 +- .../service/impl/TableServiceMariaDbImpl.java | 31 ++-- .../service/impl/ViewServiceMariaDbImpl.java | 4 +- .../main/java/at/tuwien/api/CacheableDto.java | 25 ---- .../at/tuwien/api/container/ContainerDto.java | 35 ++--- .../at/tuwien/api/database/DatabaseDto.java | 26 ---- .../java/at/tuwien/api/database/ViewDto.java | 27 +--- .../tuwien/api/database/table/TableDto.java | 28 +--- .../main/java/at/tuwien/api/user/UserDto.java | 34 ++--- .../java/at/tuwien/mapper/MetadataMapper.java | 5 +- .../at/tuwien/endpoints/AbstractEndpoint.java | 11 +- .../at/tuwien/endpoints/DatabaseEndpoint.java | 7 +- .../at/tuwien/endpoints/TableEndpoint.java | 35 ++--- .../at/tuwien/endpoints/UserEndpoint.java | 8 +- .../at/tuwien/endpoints/ViewEndpoint.java | 22 +-- .../endpoints/TableEndpointUnitTest.java | 6 +- .../endpoints/UserEndpointUnitTest.java | 8 +- .../endpoints/ViewEndpointUnitTest.java | 2 +- .../tuwien/service/ImageServiceUnitTest.java | 4 +- .../java/at/tuwien/test/AbstractUnitTest.java | 5 + .../main/java/at/tuwien/test/BaseTest.java | 135 ++++++++++-------- lib/python/dbrepo/RestClient.py | 19 ++- 41 files changed, 309 insertions(+), 437 deletions(-) diff --git a/.docs/changelog.md b/.docs/changelog.md index f2bb1ab057..e2bb59c374 100644 --- a/.docs/changelog.md +++ b/.docs/changelog.md @@ -2,6 +2,22 @@ author: Martin Weise --- +## v1.6.2 (2025-01-24) + +[:simple-gitlab: GitLab Release](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/tags/v1.6.2) + +### What's Changed + +#### Changes + +* Added interface tests for the Python library in Gitlab CI/CD pipeline + in [#486](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/issues/486). + +#### Fixes + +* Fixed a bug where no pagination was possible + in [#487](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/issues/487). + ## v1.6.1 (2025-01-21) [:simple-gitlab: GitLab Release](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/tags/v1.6.1) @@ -15,7 +31,7 @@ author: Martin Weise #### Fixes -* Added init container that adds the admin user to the Metadata Database +* Added init container that adds the admin user to the Metadata Database in [#480](https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/issues/480). ## v1.6.0 (2025-01-07) 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 35c6466747..92ba523f36 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 @@ -723,11 +723,11 @@ public class TableEndpoint extends RestEndpoint { public ResponseEntity<TableStatisticDto> statistic(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, - MetadataServiceException, TableMalformedException, DatabaseNotFoundException { + MetadataServiceException, TableMalformedException { log.debug("endpoint generate table statistic, databaseId={}, tableId={}", databaseId, tableId); - final TableDto table = credentialService.getTable(databaseId, tableId); try { - return ResponseEntity.ok(tableService.getStatistics(credentialService.getDatabase(table.getTdbid()), table)); + return ResponseEntity.ok(tableService.getStatistics( + credentialService.getTable(databaseId, tableId))); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database", e); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java index 00553dce06..3cecdb6457 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java @@ -53,7 +53,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { when(credentialService.getDatabase(DATABASE_1_ID)) .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_4_ID)) - .thenReturn(USER_4_PRIVILEGED_DTO); + .thenReturn(USER_4_DTO); /* test */ final ResponseEntity<Void> response = accessEndpoint.create(DATABASE_1_ID, USER_4_ID, UPDATE_DATABASE_ACCESS_READ_DTO); @@ -70,7 +70,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { when(credentialService.getDatabase(DATABASE_1_ID)) .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_1_ID)) - .thenReturn(USER_1_PRIVILEGED_DTO); + .thenReturn(USER_1_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -87,10 +87,10 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { when(credentialService.getDatabase(DATABASE_1_ID)) .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_4_ID)) - .thenReturn(USER_4_PRIVILEGED_DTO); + .thenReturn(USER_4_DTO); doThrow(SQLException.class) .when(accessService) - .create(DATABASE_1_DTO, USER_4_PRIVILEGED_DTO, AccessTypeDto.READ); + .create(DATABASE_1_DTO, USER_4_DTO, AccessTypeDto.READ); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -151,7 +151,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { when(credentialService.getDatabase(DATABASE_1_ID)) .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_1_ID)) - .thenReturn(USER_1_PRIVILEGED_DTO); + .thenReturn(USER_1_DTO); /* test */ final ResponseEntity<Void> response = accessEndpoint.update(DATABASE_1_ID, USER_1_ID, UPDATE_DATABASE_ACCESS_READ_DTO); @@ -168,10 +168,10 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { when(credentialService.getDatabase(DATABASE_1_ID)) .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_1_ID)) - .thenReturn(USER_1_PRIVILEGED_DTO); + .thenReturn(USER_1_DTO); doThrow(SQLException.class) .when(accessService) - .update(DATABASE_1_DTO, USER_1_PRIVILEGED_DTO, AccessTypeDto.READ); + .update(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.READ); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -188,7 +188,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { when(credentialService.getDatabase(DATABASE_1_ID)) .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_4_ID)) - .thenReturn(USER_4_PRIVILEGED_DTO); + .thenReturn(USER_4_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -250,7 +250,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { when(credentialService.getDatabase(DATABASE_1_ID)) .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_1_ID)) - .thenReturn(USER_1_PRIVILEGED_DTO); + .thenReturn(USER_1_DTO); doNothing() .when(accessService) .delete(any(DatabaseDto.class), any(UserDto.class)); @@ -270,7 +270,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { when(credentialService.getDatabase(DATABASE_1_ID)) .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_4_ID)) - .thenReturn(USER_4_PRIVILEGED_DTO); + .thenReturn(USER_4_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -331,10 +331,10 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { when(credentialService.getDatabase(DATABASE_1_ID)) .thenReturn(DATABASE_1_DTO); when(credentialService.getUser(USER_1_ID)) - .thenReturn(USER_1_PRIVILEGED_DTO); + .thenReturn(USER_1_DTO); doThrow(SQLException.class) .when(accessService) - .delete(DATABASE_1_DTO, USER_1_PRIVILEGED_DTO); + .delete(DATABASE_1_DTO, USER_1_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java index 592063e034..0c6f9d8521 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java @@ -5,7 +5,10 @@ import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.user.UserDto; import at.tuwien.endpoints.DatabaseEndpoint; import at.tuwien.exception.*; -import at.tuwien.service.*; +import at.tuwien.service.AccessService; +import at.tuwien.service.ContainerService; +import at.tuwien.service.CredentialService; +import at.tuwien.service.DatabaseService; import at.tuwien.test.AbstractUnitTest; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; @@ -35,9 +38,6 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { @Autowired private DatabaseEndpoint databaseEndpoint; - @MockBean - private SubsetService queryService; - @MockBean private ContainerService containerService; 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 35df8f5dd9..c7a74de17f 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 @@ -1,7 +1,6 @@ package at.tuwien.endpoint; import at.tuwien.api.database.DatabaseAccessDto; -import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.table.*; import at.tuwien.endpoints.TableEndpoint; @@ -166,12 +165,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser public void statistic_succeeds() throws DatabaseUnavailableException, TableNotFoundException, SQLException, - TableMalformedException, RemoteUnavailableException, MetadataServiceException, DatabaseNotFoundException { + TableMalformedException, RemoteUnavailableException, MetadataServiceException { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) .thenReturn(TABLE_8_DTO); - when(tableService.getStatistics(any(DatabaseDto.class), any(TableDto.class))) + when(tableService.getStatistics(any(TableDto.class))) .thenReturn(TABLE_8_STATISTIC_DTO); /* test */ @@ -189,7 +188,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(TABLE_8_DTO); doThrow(SQLException.class) .when(tableService) - .getStatistics(any(DatabaseDto.class), any(TableDto.class)); + .getStatistics(any(TableDto.class)); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java index a8633836b8..a2ed7e3817 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java @@ -36,10 +36,6 @@ import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { - @MockBean - @Qualifier("restTemplate") - private RestTemplate restTemplate; - @MockBean @Qualifier("internalRestTemplate") private RestTemplate internalRestTemplate; @@ -56,13 +52,8 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { public void getTableById_succeeds() throws TableNotFoundException, RemoteUnavailableException, MetadataServiceException { final HttpHeaders headers = new HttpHeaders(); - headers.set("X-Type", IMAGE_1_JDBC); - headers.set("X-Host", CONTAINER_1_HOST); - headers.set("X-Port", "" + CONTAINER_1_PORT); headers.set("X-Username", CONTAINER_1_PRIVILEGED_USERNAME); headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD); - headers.set("X-Database", DATABASE_1_INTERNALNAME); - headers.set("X-Table", TABLE_1_INTERNAL_NAME); /* mock */ when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class))) @@ -72,12 +63,11 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* test */ final TableDto response = metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID); - assertEquals(IMAGE_1_JDBC, response.getJdbcMethod()); - assertEquals(CONTAINER_1_HOST, response.getHost()); - assertEquals(CONTAINER_1_PORT, response.getPort()); - assertEquals(CONTAINER_1_PRIVILEGED_USERNAME, response.getUsername()); - assertEquals(CONTAINER_1_PRIVILEGED_PASSWORD, response.getPassword()); - assertEquals(DATABASE_1_INTERNALNAME, response.getDatabase()); + assertEquals(IMAGE_1_JDBC, response.getDatabase().getContainer().getImage().getJdbcMethod()); + assertEquals(CONTAINER_1_HOST, response.getDatabase().getContainer().getHost()); + assertEquals(CONTAINER_1_PORT, response.getDatabase().getContainer().getPort()); + assertEquals(CONTAINER_1_PRIVILEGED_USERNAME, response.getDatabase().getContainer().getUsername()); + assertEquals(CONTAINER_1_PRIVILEGED_PASSWORD, response.getDatabase().getContainer().getPassword()); assertEquals(TABLE_1_INTERNAL_NAME, response.getInternalName()); } @@ -147,13 +137,8 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { @Test public void getTableById_emptyBody_fails() { final HttpHeaders headers = new HttpHeaders(); - headers.set("X-Type", IMAGE_1_JDBC); - headers.set("X-Host", CONTAINER_1_HOST); - headers.set("X-Port", "" + CONTAINER_1_PORT); headers.set("X-Username", CONTAINER_1_PRIVILEGED_USERNAME); headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD); - headers.set("X-Database", DATABASE_1_INTERNALNAME); - headers.set("X-Table", TABLE_1_INTERNAL_NAME); /* mock */ when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class))) @@ -173,8 +158,6 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { final HttpHeaders headers = new HttpHeaders(); headers.set("X-Username", CONTAINER_1_PRIVILEGED_USERNAME); headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD); - headers.set("X-Host", CONTAINER_1_HOST); - headers.set("X-Port", "" + CONTAINER_1_PORT); /* mock */ when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseDto.class))) @@ -369,13 +352,8 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { @Test public void getViewById_succeeds() throws RemoteUnavailableException, ViewNotFoundException, MetadataServiceException { final HttpHeaders headers = new HttpHeaders(); - headers.set("X-Type", IMAGE_1_JDBC); - headers.set("X-Host", CONTAINER_1_HOST); - headers.set("X-Port", "" + CONTAINER_1_PORT); headers.set("X-Username", CONTAINER_1_PRIVILEGED_USERNAME); headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD); - headers.set("X-Database", DATABASE_1_INTERNALNAME); - headers.set("X-View", VIEW_1_INTERNAL_NAME); /* mock */ when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(ViewDto.class))) @@ -454,12 +432,8 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { @Test public void getViewById_emptyBody_fails() { final HttpHeaders headers = new HttpHeaders(); - headers.set("X-Type", IMAGE_1_JDBC); - headers.set("X-Host", CONTAINER_1_HOST); - headers.set("X-Port", "" + CONTAINER_1_PORT); headers.set("X-Username", CONTAINER_1_PRIVILEGED_USERNAME); headers.set("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD); - headers.set("X-Database", DATABASE_1_INTERNALNAME); /* mock */ when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(ViewDto.class))) diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java index 44e5f88912..752a9b0e05 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java @@ -57,8 +57,8 @@ public class DefaultListenerIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* database */ - MariaDbConfig.dropAllDatabases(CONTAINER_1_DTO); - MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); + MariaDbConfig.dropAllDatabases(CONTAINER_1_PRIVILEGED_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); } @Test diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java index b3a3bf6397..7a439ed55c 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java @@ -51,9 +51,10 @@ public class DefaultListenerUnitTest extends AbstractUnitTest { @BeforeEach public void beforeEach() throws SQLException { + genesis(); /* metadata database */ - MariaDbConfig.dropAllDatabases(CONTAINER_1_DTO); - MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); + MariaDbConfig.dropAllDatabases(CONTAINER_1_PRIVILEGED_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); } @Test diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/AccessServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/AccessServiceIntegrationTest.java index bc121c3b26..2c174e0a3d 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/AccessServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/AccessServiceIntegrationTest.java @@ -44,16 +44,16 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_PRIVILEGED_DTO); } @Test public void create_read_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.create(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.READ); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); + accessService.create(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO, AccessTypeDto.READ); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); for (String privilege : grantDefaultRead.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -63,8 +63,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void create_writeOwn_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.create(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.WRITE_OWN); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); + accessService.create(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO, AccessTypeDto.WRITE_OWN); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); for (String privilege : grantDefaultWrite.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -74,8 +74,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void create_writeAll_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.create(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.WRITE_ALL); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); + accessService.create(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO, AccessTypeDto.WRITE_ALL); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); for (String privilege : grantDefaultWrite.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -85,8 +85,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void update_read_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.update(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.READ); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); + accessService.update(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO, AccessTypeDto.READ); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); for (String privilege : grantDefaultRead.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -96,8 +96,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void update_writeOwn_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.update(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.WRITE_OWN); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); + accessService.update(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO, AccessTypeDto.WRITE_OWN); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); for (String privilege : grantDefaultWrite.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -107,8 +107,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void update_writeAll_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.update(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.WRITE_ALL); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); + accessService.update(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO, AccessTypeDto.WRITE_ALL); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); for (String privilege : grantDefaultWrite.split(",")) { assertTrue(privileges.stream().anyMatch(p -> p.trim().equals(privilege.trim()))); } @@ -119,7 +119,7 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseMalformedException.class, () -> { - accessService.update(DATABASE_1_DTO, USER_5_DTO, AccessTypeDto.WRITE_ALL); + accessService.update(DATABASE_1_PRIVILEGED_DTO, USER_5_DTO, AccessTypeDto.WRITE_ALL); }); } @@ -127,8 +127,8 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { public void delete_succeeds() throws SQLException, DatabaseMalformedException { /* test */ - accessService.delete(DATABASE_1_DTO, USER_1_DTO); - final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_DTO, USER_1_USERNAME); + accessService.delete(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO); + final List<String> privileges = MariaDbConfig.getPrivileges(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); assertEquals(1, privileges.size()); assertEquals("USAGE", privileges.get(0)); } @@ -138,7 +138,7 @@ public class AccessServiceIntegrationTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseMalformedException.class, () -> { - accessService.delete(DATABASE_1_DTO, USER_5_DTO); + accessService.delete(DATABASE_1_PRIVILEGED_DTO, USER_5_DTO); }); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java index e793a8362c..251dfe53c5 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java @@ -45,7 +45,7 @@ public class ContainerServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); } @Test diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java index 26ff951dda..ae1917202d 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java @@ -63,7 +63,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); } @Test diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java index f8687d8e0d..cfab2600e5 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java @@ -374,7 +374,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void getStatistics_succeeds() throws TableMalformedException, SQLException, TableNotFoundException { /* test */ - final TableStatisticDto response = tableService.getStatistics(DATABASE_1_DTO, TABLE_2_DTO); + final TableStatisticDto response = tableService.getStatistics(TABLE_2_DTO); assertEquals(TABLE_2_COLUMNS.size(), response.getColumns().size()); log.trace("response rows: {}", response.getRows()); assertEquals(3L, response.getRows()); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java index 650652d62f..7b02192b7e 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java @@ -100,7 +100,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { log.error("Failed to find database with id {}: service responded unsuccessful: {}", id, response.getStatusCode()); throw new MetadataServiceException("Failed to find database: service responded unsuccessful: " + response.getStatusCode()); } - final List<String> expectedHeaders = List.of("X-Username", "X-Password", "X-Host", "X-Port", "X-Type"); + final List<String> expectedHeaders = List.of("X-Username", "X-Password"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { log.error("Failed to find all database headers"); log.debug("expected headers: {}", expectedHeaders); @@ -112,12 +112,8 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { throw new MetadataServiceException("Failed to find database with id " + id + ": body is empty"); } final DatabaseDto database = response.getBody(); - database.setJdbcMethod(response.getHeaders().get("X-Type").get(0)); - database.setUsername(response.getHeaders().get("X-Username").get(0)); - database.setPassword(response.getHeaders().get("X-Password").get(0)); - database.setDatabase(database.getInternalName()); - database.setHost(response.getHeaders().get("X-Host").get(0)); - database.setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0))); + database.getContainer().setUsername(response.getHeaders().get("X-Username").get(0)); + database.getContainer().setPassword(response.getHeaders().get("X-Password").get(0)); database.setLastRetrieved(Instant.now()); return database; } @@ -141,7 +137,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { log.error("Failed to find table with id {}: service responded unsuccessful: {}", id, response.getStatusCode()); throw new MetadataServiceException("Failed to find table: service responded unsuccessful: " + response.getStatusCode()); } - final List<String> expectedHeaders = List.of("X-Type", "X-Host", "X-Port", "X-Username", "X-Password", "X-Database", "X-Table", "X-Type"); + final List<String> expectedHeaders = List.of("X-Username", "X-Password"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { log.error("Failed to find all table headers"); log.debug("expected headers: {}", expectedHeaders); @@ -153,13 +149,8 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { throw new MetadataServiceException("Failed to find table with id " + id + ": body is empty"); } final TableDto table = metadataMapper.tableDtoToTableDto(response.getBody()); - table.setJdbcMethod(response.getHeaders().get("X-Type").get(0)); - table.setHost(response.getHeaders().get("X-Host").get(0)); - table.setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0))); - table.setUsername(response.getHeaders().get("X-Username").get(0)); - table.setPassword(response.getHeaders().get("X-Password").get(0)); - table.setDatabase(response.getHeaders().get("X-Database").get(0)); - table.setInternalName(response.getHeaders().get("X-Table").get(0)); + table.getDatabase().getContainer().setUsername(response.getHeaders().get("X-Username").get(0)); + table.getDatabase().getContainer().setPassword(response.getHeaders().get("X-Password").get(0)); table.setLastRetrieved(Instant.now()); return table; } @@ -183,7 +174,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { log.error("Failed to find view with id {}: service responded unsuccessful: {}", id, response.getStatusCode()); throw new MetadataServiceException("Failed to find view: service responded unsuccessful: " + response.getStatusCode()); } - final List<String> expectedHeaders = List.of("X-Type", "X-Host", "X-Port", "X-Username", "X-Password", "X-Database", "X-View", "X-Type"); + final List<String> expectedHeaders = List.of("X-Username", "X-Password"); if (!response.getHeaders().keySet().containsAll(expectedHeaders)) { log.error("Failed to find all view headers"); log.debug("expected headers: {}", expectedHeaders); @@ -195,13 +186,8 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { throw new MetadataServiceException("Failed to find view with id " + id + ": body is empty"); } final ViewDto view = metadataMapper.viewDtoToViewDto(response.getBody()); - view.setJdbcMethod(response.getHeaders().get("X-Type").get(0)); - view.setHost(response.getHeaders().get("X-Host").get(0)); - view.setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0))); - view.setUsername(response.getHeaders().get("X-Username").get(0)); - view.setPassword(response.getHeaders().get("X-Password").get(0)); - view.setDatabase(response.getHeaders().get("X-Database").get(0)); - view.setInternalName(response.getHeaders().get("X-View").get(0)); + view.getDatabase().getContainer().setUsername(response.getHeaders().get("X-Username").get(0)); + view.getDatabase().getContainer().setPassword(response.getHeaders().get("X-Password").get(0)); view.setLastRetrieved(Instant.now()); return view; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java index b74c491abe..636e12ceb6 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java @@ -17,14 +17,13 @@ public interface TableService { /** * Generate table statistic for a given table. Only numerical columns are calculated. * - * @param database The database. * @param table The table. * @return The table statistic, if successful. * @throws SQLException Failed to parse SQL query, contains invalid syntax. * @throws TableMalformedException The table statistic generation was unsuccessful, likely due to a bug in the mapping. * @throws TableNotFoundException The table could not be inspected in the data database. */ - TableStatisticDto getStatistics(DatabaseDto database, TableDto table) throws SQLException, TableMalformedException, + TableStatisticDto getStatistics(TableDto table) throws SQLException, TableMalformedException, TableNotFoundException; /** diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java index 16ace6dc9e..be049663b7 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/AccessServiceMariaDbImpl.java @@ -17,7 +17,7 @@ import java.sql.SQLException; @Log4j2 @Service -public class AccessServiceMariaDbImpl extends DataConnector<DatabaseDto> implements AccessService { +public class AccessServiceMariaDbImpl extends DataConnector implements AccessService { @Value("${dbrepo.grant.default.read}") private String grantDefaultRead; diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceMariaDbImpl.java index fc9b2d97c3..aedba1aed2 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceMariaDbImpl.java @@ -19,7 +19,7 @@ import java.sql.SQLException; @Log4j2 @Service -public class ContainerServiceMariaDbImpl extends DataConnector<ContainerDto> implements ContainerService { +public class ContainerServiceMariaDbImpl extends DataConnector implements ContainerService { private final RabbitConfig rabbitConfig; private final MariaDbMapper mariaDbMapper; diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java index 3a54b399d4..c21d37721b 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java @@ -1,20 +1,22 @@ package at.tuwien.service.impl; -import at.tuwien.api.CacheableDto; +import at.tuwien.api.container.ContainerDto; +import at.tuwien.api.database.DatabaseDto; +import at.tuwien.api.database.ViewDto; +import at.tuwien.api.database.table.TableDto; import com.mchange.v2.c3p0.ComboPooledDataSource; import lombok.extern.log4j.Log4j2; import org.springframework.stereotype.Service; @Log4j2 @Service -public abstract class DataConnector<T extends CacheableDto> { +public abstract class DataConnector { - public ComboPooledDataSource getDataSource(T entity) { + public ComboPooledDataSource getDataSource(ContainerDto container, String databaseName) { final ComboPooledDataSource dataSource = new ComboPooledDataSource(); - dataSource.setJdbcUrl(getJdbcUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPort(), - entity.getDatabase())); - dataSource.setUser(entity.getUsername()); - dataSource.setPassword(entity.getPassword()); + dataSource.setJdbcUrl(getJdbcUrl(container, databaseName)); + dataSource.setUser(container.getUsername()); + dataSource.setPassword(container.getPassword()); dataSource.setInitialPoolSize(5); dataSource.setMinPoolSize(5); dataSource.setAcquireIncrement(5); @@ -23,37 +25,48 @@ public abstract class DataConnector<T extends CacheableDto> { return dataSource; } - public ComboPooledDataSource getDataSource(T entity, String databaseName) { - final ComboPooledDataSource dataSource = new ComboPooledDataSource(); - dataSource.setJdbcUrl(getJdbcUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPort(), databaseName)); - dataSource.setUser(entity.getUsername()); - dataSource.setPassword(entity.getPassword()); - dataSource.setInitialPoolSize(5); - dataSource.setMinPoolSize(5); - dataSource.setAcquireIncrement(5); - dataSource.setMaxPoolSize(20); - dataSource.setMaxStatements(100); - return dataSource; + public ComboPooledDataSource getDataSource(ViewDto view) { + return getDataSource(view.getDatabase().getContainer(), null); + } + + public ComboPooledDataSource getDataSource(TableDto table) { + return getDataSource(table.getDatabase().getContainer(), null); } - public String getSparkUrl(String jdbcMethod, String host, Integer port, String databaseName) { - final StringBuilder sb = new StringBuilder(getJdbcUrl(jdbcMethod, host, port, databaseName)) + public ComboPooledDataSource getDataSource(ContainerDto container) { + return getDataSource(container, null); + } + + public ComboPooledDataSource getDataSource(DatabaseDto database) { + return getDataSource(database.getContainer(), database.getInternalName()); + } + + public String getSparkUrl(ContainerDto container, String databaseName) { + final StringBuilder sb = new StringBuilder(getJdbcUrl(container, databaseName)) .append("?sessionVariables=sql_mode='ANSI_QUOTES'"); log.trace("mapped container to spark url: {}", sb.toString()); return sb.toString(); } - public String getSparkUrl(T entity) { - return getSparkUrl(entity.getJdbcMethod(), entity.getHost(), entity.getPort(), entity.getDatabase()); + public String getSparkUrl(TableDto table) { + return getSparkUrl(table.getDatabase().getContainer(), null); + } + + public String getSparkUrl(DatabaseDto databaseDto) { + return getSparkUrl(databaseDto.getContainer(), null); + } + + public String getSparkUrl(ContainerDto container) { + return getSparkUrl(container, null); } - public String getJdbcUrl(String jdbcMethod, String host, Integer port, String databaseName) { + public String getJdbcUrl(ContainerDto container, String databaseName) { final StringBuilder stringBuilder = new StringBuilder("jdbc:") - .append(jdbcMethod) + .append(container.getImage().getJdbcMethod()) .append("://") - .append(host) + .append(container.getHost()) .append(":") - .append(port); + .append(container.getPort()); if (databaseName != null) { stringBuilder.append("/") .append(databaseName); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java index cfe43d8f8f..c4afe44287 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java @@ -29,7 +29,7 @@ import java.util.List; @Log4j2 @Service -public class DatabaseServiceMariaDbImpl extends DataConnector<DatabaseDto> implements DatabaseService { +public class DatabaseServiceMariaDbImpl extends DataConnector implements DatabaseService { private final DataMapper dataMapper; private final QueryConfig queryConfig; diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java index 1a26a84ef6..4db29335fb 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceRabbitMqImpl.java @@ -18,7 +18,7 @@ import java.util.Optional; @Log4j2 @Service -public class QueueServiceRabbitMqImpl extends DataConnector<TableDto> implements QueueService { +public class QueueServiceRabbitMqImpl extends DataConnector implements QueueService { private final DataMapper dataMapper; private final MetadataMapper metadataMapper; diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java index 62b7c77a84..0a208b2890 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java @@ -13,7 +13,6 @@ import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.DatabaseService; import at.tuwien.service.SubsetService; import at.tuwien.service.TableService; -import at.tuwien.service.ViewService; import com.mchange.v2.c3p0.ComboPooledDataSource; import lombok.extern.log4j.Log4j2; import org.apache.spark.sql.Dataset; @@ -29,10 +28,9 @@ import java.util.UUID; @Log4j2 @Service -public class SubsetServiceMariaDbImpl extends DataConnector<DatabaseDto> implements SubsetService { +public class SubsetServiceMariaDbImpl extends DataConnector implements SubsetService { private final DataMapper dataMapper; - private final ViewService viewService; private final TableService tableService; private final MariaDbMapper mariaDbMapper; private final MetadataMapper metadataMapper; @@ -40,11 +38,10 @@ public class SubsetServiceMariaDbImpl extends DataConnector<DatabaseDto> impleme private final MetadataServiceGateway metadataServiceGateway; @Autowired - public SubsetServiceMariaDbImpl(DataMapper dataMapper, ViewService viewService, TableService tableService, - MariaDbMapper mariaDbMapper, MetadataMapper metadataMapper, - DatabaseService databaseService, MetadataServiceGateway metadataServiceGateway) { + public SubsetServiceMariaDbImpl(DataMapper dataMapper, TableService tableService, MariaDbMapper mariaDbMapper, + MetadataMapper metadataMapper, DatabaseService databaseService, + MetadataServiceGateway metadataServiceGateway) { this.dataMapper = dataMapper; - this.viewService = viewService; this.tableService = tableService; this.mariaDbMapper = mariaDbMapper; this.metadataMapper = metadataMapper; diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java index 482f874624..4d4121f060 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java @@ -33,7 +33,7 @@ import java.util.Properties; @Log4j2 @Service -public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements TableService { +public class TableServiceMariaDbImpl extends DataConnector implements TableService { private final DataMapper dataMapper; private final SparkSession sparkSession; @@ -52,7 +52,7 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements } @Override - public TableStatisticDto getStatistics(DatabaseDto database, TableDto table) throws SQLException, TableMalformedException, + public TableStatisticDto getStatistics(TableDto table) throws SQLException, TableMalformedException, TableNotFoundException { final ComboPooledDataSource dataSource = getDataSource(table); final Connection connection = dataSource.getConnection(); @@ -62,14 +62,14 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements final long start = System.currentTimeMillis(); final String query = mariaDbMapper.tableColumnStatisticsSelectRawQuery(table.getColumns(), table.getInternalName()); if (query == null) { - log.debug("table {}.{} does not have columns that can be analysed for statistical properties (i.e. no numeric columns)", database.getInternalName(), table.getInternalName()); + log.debug("table {}.{} does not have columns that can be analysed for statistical properties (i.e. no numeric columns)", table.getDatabase().getInternalName(), table.getInternalName()); statistic = null; } else { final ResultSet resultSet = connection.prepareStatement(query) .executeQuery(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); statistic = dataMapper.resultSetToTableStatistic(resultSet); - final TableDto tmpTable = databaseService.inspectTable(database, table.getInternalName()); + final TableDto tmpTable = databaseService.inspectTable(table.getDatabase(), table.getInternalName()); statistic.setAvgRowLength(tmpTable.getAvgRowLength()); statistic.setDataLength(tmpTable.getDataLength()); statistic.setMaxDataLength(tmpTable.getMaxDataLength()); @@ -95,7 +95,7 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements @Override public void updateTable(TableDto table, TableUpdateDto data) throws SQLException, TableMalformedException { - final ComboPooledDataSource dataSource = getDataSource(table); + final ComboPooledDataSource dataSource = getDataSource(table.getDatabase()); final Connection connection = dataSource.getConnection(); try { /* create table if not exists */ @@ -122,7 +122,7 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements @Override public void delete(TableDto table) throws SQLException, QueryMalformedException { - final ComboPooledDataSource dataSource = getDataSource(table); + final ComboPooledDataSource dataSource = getDataSource(table.getDatabase()); final Connection connection = dataSource.getConnection(); try { /* create table if not exists */ @@ -144,14 +144,14 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements @Override public List<TableHistoryDto> history(TableDto table, Long size) throws SQLException, TableNotFoundException { - final ComboPooledDataSource dataSource = getDataSource(table); + final ComboPooledDataSource dataSource = getDataSource(table.getDatabase()); final Connection connection = dataSource.getConnection(); final List<TableHistoryDto> history; try { /* find table data */ final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.selectHistoryRawQuery( - table.getDatabase(), table.getInternalName(), size)) + table.getDatabase().getInternalName(), table.getInternalName(), size)) .executeQuery(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); history = dataMapper.resultSetToTableHistory(resultSet); @@ -170,14 +170,14 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements @Override public Long getCount(TableDto table, Instant timestamp) throws SQLException, QueryMalformedException { - final ComboPooledDataSource dataSource = getDataSource(table); + final ComboPooledDataSource dataSource = getDataSource(table.getDatabase()); final Connection connection = dataSource.getConnection(); final Long queryResult; try { /* find table data */ final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.selectCountRawQuery( - table.getDatabase(), table.getInternalName(), timestamp)) + table.getDatabase().getInternalName(), table.getInternalName(), timestamp)) .executeQuery(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); queryResult = mariaDbMapper.resultSetToNumber(resultSet); @@ -204,8 +204,8 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements final Dataset<Row> dataset = storageService.loadDataset(columns, data.getLocation(), String.valueOf(data.getSeparator()), data.getHeader()); final Properties properties = new Properties(); - properties.setProperty("user", table.getUsername()); - properties.setProperty("password", table.getPassword()); + properties.setProperty("user", table.getDatabase().getContainer().getUsername()); + properties.setProperty("password", table.getDatabase().getContainer().getPassword()); final String temporaryTable = table.getInternalName() + "_tmp"; try { log.trace("import dataset to temporary table: {}", temporaryTable); @@ -375,10 +375,9 @@ public class TableServiceMariaDbImpl extends DataConnector<TableDto> implements try { return sparkSession.read() .format("jdbc") - .option("user", database.getUsername()) - .option("password", database.getPassword()) - .option("url", getSparkUrl(database.getJdbcMethod(), database.getHost(), database.getPort(), - database.getInternalName())) + .option("user", database.getContainer().getUsername()) + .option("password", database.getContainer().getPassword()) + .option("url", getSparkUrl(database)) .option("query", mariaDbMapper.defaultRawSelectQuery(database.getInternalName(), tableOrView, timestamp, page, size)) .load(); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java index 3224c371a3..fff524047e 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/ViewServiceMariaDbImpl.java @@ -17,7 +17,7 @@ import java.time.Instant; @Log4j2 @Service -public class ViewServiceMariaDbImpl extends DataConnector<ViewDto> implements ViewService { +public class ViewServiceMariaDbImpl extends DataConnector implements ViewService { private final MariaDbMapper mariaDbMapper; @@ -57,7 +57,7 @@ public class ViewServiceMariaDbImpl extends DataConnector<ViewDto> implements Vi /* find view data */ final long start = System.currentTimeMillis(); final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.selectCountRawQuery( - view.getDatabase(), view.getInternalName(), timestamp)) + view.getDatabase().getInternalName(), view.getInternalName(), timestamp)) .executeQuery(); log.trace("executed statement in {} ms", System.currentTimeMillis() - start); queryResult = mariaDbMapper.resultSetToNumber(resultSet); diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java index 4ff6f699d6..e947ece632 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/CacheableDto.java @@ -1,6 +1,5 @@ package at.tuwien.api; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.EqualsAndHashCode; @@ -20,28 +19,4 @@ public abstract class CacheableDto { @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; - @ToString.Exclude - @Schema(example = "mariadb") - private String jdbcMethod; - - @ToString.Exclude - @Schema(example = "data-db") - private String host; - - @ToString.Exclude - @Schema(example = "3306") - private Integer port; - - @ToString.Exclude - @Schema(example = "username") - private String username; - - @ToString.Exclude - @JsonIgnore - private String password; - - @ToString.Exclude - @Schema(example = "air_quality") - private String database; - } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java index 56d0df315f..913ab26d9d 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/container/ContainerDto.java @@ -2,7 +2,6 @@ package at.tuwien.api.container; import at.tuwien.api.CacheableDto; import at.tuwien.api.container.image.ImageDto; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -35,6 +34,12 @@ public class ContainerDto extends CacheableDto { @Schema(example = "air_quality") private String internalName; + @Schema(example = "data-db") + private String host; + + @Schema(example = "3306") + private Integer port; + @JsonProperty("ui_host") @Schema(example = "example.com") private String uiHost; @@ -54,34 +59,18 @@ public class ContainerDto extends CacheableDto { @Schema(example = "10") private Long count; - /* lombok limitations prevent from convenient builder functions */ - - @JsonProperty("last_retrieved") - @Schema(example = "2025-01-23T12:09:01") - private Instant lastRetrieved; - - @ToString.Exclude - @Schema(example = "mariadb") - private String jdbcMethod; - - @ToString.Exclude - @Schema(example = "data-db") - private String host; - - @ToString.Exclude - @Schema(example = "3306") - private Integer port; - @ToString.Exclude @Schema(example = "username") private String username; @ToString.Exclude - @JsonIgnore + @Schema(example = "p4ssw0rd") private String password; - @ToString.Exclude - @Schema(example = "air_quality") - private String database; + /* lombok limitations prevent from convenient builder functions */ + + @JsonProperty("last_retrieved") + @Schema(example = "2025-01-23T12:09:01") + private Instant lastRetrieved; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java index c6866c494b..d6ea2bff9e 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseDto.java @@ -5,7 +5,6 @@ import at.tuwien.api.container.ContainerDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.UserBriefDto; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -67,7 +66,6 @@ public class DatabaseDto extends CacheableDto { @Schema(example = "true") private Boolean isSchemaPublic; - @NotNull private ContainerDto container; @NotNull @@ -94,28 +92,4 @@ public class DatabaseDto extends CacheableDto { @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; - @ToString.Exclude - @Schema(example = "mariadb") - private String jdbcMethod; - - @ToString.Exclude - @Schema(example = "data-db") - private String host; - - @ToString.Exclude - @Schema(example = "3306") - private Integer port; - - @ToString.Exclude - @Schema(example = "username") - private String username; - - @ToString.Exclude - @JsonIgnore - private String password; - - @ToString.Exclude - @Schema(example = "air_quality") - private String database; - } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java index 2785cf7107..5f9373bb12 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java @@ -3,7 +3,6 @@ package at.tuwien.api.database; import at.tuwien.api.CacheableDto; import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.UserBriefDto; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -66,6 +65,8 @@ public class ViewDto extends CacheableDto { @Schema(example = "7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916") private String queryHash; + private DatabaseDto database; + @NotNull private UserBriefDto owner; @@ -78,28 +79,4 @@ public class ViewDto extends CacheableDto { @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; - @ToString.Exclude - @Schema(example = "mariadb") - private String jdbcMethod; - - @ToString.Exclude - @Schema(example = "data-db") - private String host; - - @ToString.Exclude - @Schema(example = "3306") - private Integer port; - - @ToString.Exclude - @Schema(example = "username") - private String username; - - @ToString.Exclude - @JsonIgnore - private String password; - - @ToString.Exclude - @Schema(example = "air_quality") - private String database; - } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java index ba80f473b8..e456908ed6 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java @@ -1,6 +1,8 @@ package at.tuwien.api.database.table; import at.tuwien.api.CacheableDto; +import at.tuwien.api.container.ContainerDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.table.columns.ColumnDto; import at.tuwien.api.database.table.constraints.ConstraintsDto; import at.tuwien.api.identifier.IdentifierDto; @@ -105,6 +107,8 @@ public class TableDto extends CacheableDto { @NotNull private List<ColumnDto> columns; + private DatabaseDto database; + @NotNull private ConstraintsDto constraints; @@ -114,28 +118,4 @@ public class TableDto extends CacheableDto { @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; - @ToString.Exclude - @Schema(example = "mariadb") - private String jdbcMethod; - - @ToString.Exclude - @Schema(example = "data-db") - private String host; - - @ToString.Exclude - @Schema(example = "3306") - private Integer port; - - @ToString.Exclude - @Schema(example = "username") - private String username; - - @ToString.Exclude - @JsonIgnore - private String password; - - @ToString.Exclude - @Schema(example = "air_quality") - private String database; - } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java index 26192f460f..e7367e2fb4 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/user/UserDto.java @@ -1,7 +1,6 @@ package at.tuwien.api.user; import at.tuwien.api.CacheableDto; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; @@ -28,6 +27,15 @@ public class UserDto extends CacheableDto { @Schema(example = "Josiah Carberry") private String name; + @NotNull + @Schema(example = "username") + private String username; + + @NotNull + @ToString.Exclude + @Schema(example = "p4ssw0rd") + private String password; + @JsonProperty("qualified_name") @Schema(example = "Josiah Carberry — @jcarberry") private String qualifiedName; @@ -49,28 +57,4 @@ public class UserDto extends CacheableDto { @Schema(example = "2025-01-23T12:09:01") private Instant lastRetrieved; - @ToString.Exclude - @Schema(example = "mariadb") - private String jdbcMethod; - - @ToString.Exclude - @Schema(example = "data-db") - private String host; - - @ToString.Exclude - @Schema(example = "3306") - private Integer port; - - @ToString.Exclude - @Schema(example = "username") - private String username; - - @ToString.Exclude - @JsonIgnore - private String password; - - @ToString.Exclude - @Schema(example = "air_quality") - private String database; - } 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 75f589e4c5..cd0b90f9f6 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 @@ -2,8 +2,8 @@ package at.tuwien.mapper; import at.tuwien.api.auth.SignupRequestDto; import at.tuwien.api.container.ContainerBriefDto; -import at.tuwien.api.container.CreateContainerDto; import at.tuwien.api.container.ContainerDto; +import at.tuwien.api.container.CreateContainerDto; import at.tuwien.api.container.image.DataTypeDto; import at.tuwien.api.container.image.ImageBriefDto; import at.tuwien.api.container.image.ImageCreateDto; @@ -833,9 +833,6 @@ public interface MetadataMapper { .trim(); } - @Mappings({ - @Mapping(target = "database", ignore = true) - }) ViewDto viewToViewDto(View data); @Mappings({ diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java index 4779a6428e..7ec1471f4d 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java @@ -1,10 +1,8 @@ package at.tuwien.endpoints; +import at.tuwien.api.container.ContainerDto; import at.tuwien.api.user.UserDetailsDto; -import at.tuwien.exception.UserNotFoundException; -import at.tuwien.service.UserService; import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.User; import java.security.Principal; import java.util.UUID; @@ -45,4 +43,11 @@ public abstract class AbstractEndpoint { throw new IllegalArgumentException("Unknown principal instance: " + authentication.getPrincipal().getClass()); } + public void removeInternalData(ContainerDto container) { + container.setPassword(null); + container.setUsername(null); + container.setHost(null); + container.setPort(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 194f79d255..44d3c10c74 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 @@ -549,10 +549,9 @@ public class DatabaseEndpoint extends AbstractEndpoint { if (isSystem(principal)) { headers.set("X-Username", database.getContainer().getPrivilegedUsername()); headers.set("X-Password", database.getContainer().getPrivilegedPassword()); - headers.set("X-Host", database.getContainer().getHost()); - headers.set("X-Port", "" + database.getContainer().getPort()); - headers.set("X-Type", database.getContainer().getImage().getJdbcMethod()); - headers.set("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port"); + headers.set("Access-Control-Expose-Headers", "X-Username X-Password"); + } else { + removeInternalData(dto.getContainer()); } return ResponseEntity.status(HttpStatus.OK) .headers(headers) 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 39d7f61460..61ba0d53e7 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 @@ -358,9 +358,9 @@ public class TableEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<TableDto> create(@NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @Valid @RequestBody TableCreateDto data, - @NotNull Principal principal) throws NotAllowedException, MalformedException, + public ResponseEntity<TableBriefDto> create(@NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @Valid @RequestBody TableCreateDto data, + @NotNull Principal principal) throws NotAllowedException, MalformedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException, AccessNotFoundException, TableNotFoundException, TableExistsException, SearchServiceException, SearchServiceConnectionException, OntologyNotFoundException, SemanticEntityNotFoundException { @@ -370,7 +370,7 @@ public class TableEndpoint extends AbstractEndpoint { endpointValidator.validateOnlyAccess(database, principal, true); endpointValidator.validateColumnCreateConstraints(data); return ResponseEntity.status(HttpStatus.CREATED) - .body(metadataMapper.tableToTableDto( + .body(metadataMapper.tableToTableBriefDto( tableService.createTable(database, data, principal))); } @@ -413,10 +413,10 @@ public class TableEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<TableDto> update(@NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @PathVariable("tableId") Long tableId, - @NotNull @Valid @RequestBody TableUpdateDto data, - @NotNull Principal principal) throws NotAllowedException, + public ResponseEntity<TableBriefDto> update(@NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @PathVariable("tableId") Long tableId, + @NotNull @Valid @RequestBody TableUpdateDto data, + @NotNull Principal principal) throws NotAllowedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, TableNotFoundException, SearchServiceException, SearchServiceConnectionException { log.debug("endpoint update table, databaseId={}, data.is_public={}, data.is_schema_public={}, principal.name={}", @@ -428,7 +428,7 @@ public class TableEndpoint extends AbstractEndpoint { throw new NotAllowedException("Failed to update table: not owner"); } return ResponseEntity.accepted() - .body(metadataMapper.tableToTableDto( + .body(metadataMapper.tableToTableBriefDto( tableService.updateTable(table, data))); } @@ -443,11 +443,6 @@ public class TableEndpoint extends AbstractEndpoint { description = "Find table successfully", headers = {@Header(name = "X-Username", description = "The authentication username", schema = @Schema(implementation = String.class)), @Header(name = "X-Password", description = "The authentication password", schema = @Schema(implementation = String.class)), - @Header(name = "X-Host", description = "The database hostname", schema = @Schema(implementation = String.class)), - @Header(name = "X-Port", description = "The database port number", schema = @Schema(implementation = Integer.class)), - @Header(name = "X-Type", description = "The JDBC connection type", schema = @Schema(implementation = String.class)), - @Header(name = "X-Database", description = "The database internal name", schema = @Schema(implementation = String.class)), - @Header(name = "X-Table", description = "The table internal name", schema = @Schema(implementation = String.class)), @Header(name = "Access-Control-Expose-Headers", description = "Expose custom headers", schema = @Schema(implementation = String.class))}, content = {@Content( mediaType = "application/json", @@ -500,20 +495,18 @@ public class TableEndpoint extends AbstractEndpoint { table.setColumns(List.of()); table.setConstraints(null); } + final TableDto dto = metadataMapper.tableToTableDto(table); final HttpHeaders headers = new HttpHeaders(); if (isSystem(principal)) { headers.set("X-Username", table.getDatabase().getContainer().getPrivilegedUsername()); headers.set("X-Password", table.getDatabase().getContainer().getPrivilegedPassword()); - headers.set("X-Host", table.getDatabase().getContainer().getHost()); - headers.set("X-Port", "" + table.getDatabase().getContainer().getPort()); - headers.set("X-Type", table.getDatabase().getContainer().getImage().getJdbcMethod()); - headers.set("X-Database", table.getDatabase().getInternalName()); - headers.set("X-Table", table.getInternalName()); - headers.set("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port X-Type X-Database X-Table"); + headers.set("Access-Control-Expose-Headers", "X-Username X-Password"); + } else { + removeInternalData(dto.getDatabase().getContainer()); } return ResponseEntity.ok() .headers(headers) - .body(metadataMapper.tableToTableDto(table)); + .body(dto); } @DeleteMapping("/{tableId}") 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 5a349ff378..da979b34f1 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 @@ -139,14 +139,14 @@ public class UserEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<UserDto> create(@NotNull @Valid @RequestBody SignupRequestDto data) + public ResponseEntity<UserBriefDto> create(@NotNull @Valid @RequestBody SignupRequestDto data) throws UserExistsException, EmailExistsException, AuthServiceException, AuthServiceConnectionException, UserNotFoundException, CredentialsInvalidException { log.debug("endpoint create user, data.username={}", data.getUsername()); userService.validateUsernameNotExists(data.getUsername()); userService.validateEmailNotExists(data.getEmail()); return ResponseEntity.status(HttpStatus.CREATED) - .body(userMapper.userToUserDto( + .body(userMapper.userToUserBriefDto( userService.create(data, authenticationService.create(data).getAttributes().getLdapId()[0]))); } @@ -328,7 +328,7 @@ public class UserEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<UserDto> modify(@NotNull @PathVariable("userId") UUID userId, + public ResponseEntity<UserBriefDto> modify(@NotNull @PathVariable("userId") UUID userId, @NotNull @Valid @RequestBody UserUpdateDto data, @NotNull Principal principal) throws NotAllowedException, UserNotFoundException, DatabaseNotFoundException { @@ -339,7 +339,7 @@ public class UserEndpoint extends AbstractEndpoint { throw new NotAllowedException("Failed to modify user: not current user " + user.getId()); } return ResponseEntity.accepted() - .body(userMapper.userToUserDto( + .body(userMapper.userToUserBriefDto( userService.modify(user, data))); } 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 8a4a087da2..abfbdb5d36 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 @@ -202,9 +202,13 @@ public class ViewEndpoint extends AbstractEndpoint { headers.set("X-View", view.getInternalName()); headers.set("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port X-Type X-Database X-View"); } + final ViewDto dto = metadataMapper.viewToViewDto(view); + if (!isSystem(principal)) { + removeInternalData(dto.getDatabase().getContainer()); + } return ResponseEntity.status(HttpStatus.OK) .headers(headers) - .body(metadataMapper.viewToViewDto(view)); + .body(dto); } @DeleteMapping("/{viewId}") @@ -248,9 +252,9 @@ public class ViewEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<?> delete(@NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @PathVariable("viewId") Long viewId, - @NotNull Principal principal) throws NotAllowedException, DataServiceException, + public ResponseEntity<Void> delete(@NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @PathVariable("viewId") Long viewId, + @NotNull Principal principal) throws NotAllowedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, ViewNotFoundException, SearchServiceException, SearchServiceConnectionException, UserNotFoundException { log.debug("endpoint delete view, databaseId={}, viewId={}", databaseId, viewId); @@ -301,10 +305,10 @@ public class ViewEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<ViewDto> update(@NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @PathVariable("viewId") Long viewId, - @NotNull @Valid @RequestBody ViewUpdateDto data, - @NotNull Principal principal) throws NotAllowedException, + public ResponseEntity<ViewBriefDto> update(@NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @PathVariable("viewId") Long viewId, + @NotNull @Valid @RequestBody ViewUpdateDto data, + @NotNull Principal principal) throws NotAllowedException, DataServiceConnectionException, DatabaseNotFoundException, ViewNotFoundException, SearchServiceException, SearchServiceConnectionException, UserNotFoundException { log.debug("endpoint update view, databaseId={}, viewId={}", databaseId, viewId); @@ -315,7 +319,7 @@ public class ViewEndpoint extends AbstractEndpoint { throw new NotAllowedException("Failed to update view: not the database- or view owner"); } return ResponseEntity.accepted() - .body(metadataMapper.viewToViewDto( + .body(metadataMapper.viewToViewBriefDto( viewService.update(database, view, data))); } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java index ef06d7f37f..9cc0c4cc8b 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java @@ -1040,7 +1040,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .build(); /* test */ - final ResponseEntity<TableDto> response = generic_update(request, USER_1_PRINCIPAL); + final ResponseEntity<TableBriefDto> response = generic_update(request, USER_1_PRINCIPAL); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); assertNotNull(response.getBody()); } @@ -1147,7 +1147,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { return tableEndpoint.list(databaseId, principal); } - protected ResponseEntity<TableDto> generic_create(Long databaseId, Database database, TableCreateDto data, + protected ResponseEntity<TableBriefDto> generic_create(Long databaseId, Database database, TableCreateDto data, Principal principal, User user, DatabaseAccess access) throws MalformedException, NotAllowedException, DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, AccessNotFoundException, TableNotFoundException, @@ -1273,7 +1273,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { return tableEndpoint.updateColumn(databaseId, tableId, columnId, data, principal); } - protected ResponseEntity<TableDto> generic_update(TableUpdateDto data, Principal caller) + protected ResponseEntity<TableBriefDto> generic_update(TableUpdateDto data, Principal caller) throws TableNotFoundException, SearchServiceException, NotAllowedException, DataServiceException, DatabaseNotFoundException, SearchServiceConnectionException, DataServiceConnectionException { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java index be0ea28c49..8b653f9356 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java @@ -458,9 +458,9 @@ public class UserEndpointUnitTest extends AbstractUnitTest { .thenReturn(userDto); /* test */ - final ResponseEntity<UserDto> response = userEndpoint.create(data); + final ResponseEntity<UserBriefDto> response = userEndpoint.create(data); assertEquals(HttpStatus.CREATED, response.getStatusCode()); - final UserDto body = response.getBody(); + final UserBriefDto body = response.getBody(); assertNotNull(body); } @@ -496,9 +496,9 @@ public class UserEndpointUnitTest extends AbstractUnitTest { .thenReturn(user); /* test */ - final ResponseEntity<UserDto> response = userEndpoint.modify(userId, data, principal); + final ResponseEntity<UserBriefDto> response = userEndpoint.modify(userId, data, principal); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - final UserDto body = response.getBody(); + final UserBriefDto body = response.getBody(); assertNotNull(body); } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java index 2815dd6c05..4cd9296425 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java @@ -571,7 +571,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { .thenReturn(VIEW_1); /* test */ - final ResponseEntity<ViewDto> response = viewEndpoint.update(DATABASE_1_ID, VIEW_1_ID, request, principal); + final ResponseEntity<ViewBriefDto> response = viewEndpoint.update(DATABASE_1_ID, VIEW_1_ID, request, principal); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); assertNotNull(response.getBody()); } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ImageServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ImageServiceUnitTest.java index 725d956f57..dc77ff263b 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ImageServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ImageServiceUnitTest.java @@ -132,7 +132,7 @@ public class ImageServiceUnitTest extends AbstractUnitTest { when(imageRepository.save(any())) .thenReturn(IMAGE_1); when(mockImageService.update(IMAGE_1, request)) - .thenReturn(CONTAINER_1_IMAGE); + .thenReturn(IMAGE_1); /* test */ final ContainerImage response = mockImageService.update(IMAGE_1, request); @@ -153,7 +153,7 @@ public class ImageServiceUnitTest extends AbstractUnitTest { when(imageRepository.save(any())) .thenReturn(IMAGE_1); when(mockImageService.update(IMAGE_1, request)) - .thenReturn(CONTAINER_1_IMAGE); + .thenReturn(IMAGE_1); /* test */ final ContainerImage response = mockImageService.update(IMAGE_1, request); 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 232b4cd280..9d5ab6b3d7 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 @@ -42,6 +42,8 @@ public abstract class AbstractUnitTest extends BaseTest { TABLE_1.setColumns(new LinkedList<>(TABLE_1_COLUMNS)); TABLE_1.setConstraints(TABLE_1_CONSTRAINTS); TABLE_1_DTO.setColumns(new LinkedList<>(TABLE_1_COLUMNS_DTO)); + TABLE_1_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); + TABLE_2_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); VIEW_1_DTO.setIdentifiers(VIEW_1_DTO_IDENTIFIERS); DATABASE_1.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_1, IDENTIFIER_2, IDENTIFIER_3, IDENTIFIER_4))); IDENTIFIER_1.setDatabase(DATABASE_1); @@ -76,10 +78,13 @@ public abstract class AbstractUnitTest extends BaseTest { VIEW_1.setDatabase(DATABASE_1); VIEW_1.setColumns(new LinkedList<>(VIEW_1_COLUMNS)); VIEW_1.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_3))); + VIEW_1_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); VIEW_2.setDatabase(DATABASE_1); VIEW_2.setColumns(new LinkedList<>(VIEW_2_COLUMNS)); + VIEW_2_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); VIEW_3.setDatabase(DATABASE_1); VIEW_3.setColumns(new LinkedList<>(VIEW_3_COLUMNS)); + VIEW_3_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); IDENTIFIER_1.setDatabase(DATABASE_1); IDENTIFIER_2.setDatabase(DATABASE_1); IDENTIFIER_3.setDatabase(DATABASE_1); 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 d732a35843..8599983687 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 @@ -133,7 +133,7 @@ import static java.time.temporal.ChronoUnit.MINUTES; * <li>Identifier 6 (Title=en, Description=en, Query=3)</li> * </ul> * <p> - * Database 4 (Public Data, Public Schema, User 4) -> Container 4 + * Database 4 (Public Data, Public Schema, User 4) -> Container 2 * <li>Table 9</li> * <li>Identifier 7</li> * <li>Query 7</li> @@ -548,17 +548,6 @@ public abstract class BaseTest { .build()) .build(); - public final static UserDto USER_1_PRIVILEGED_DTO = UserDto.builder() - .id(USER_1_ID) - .username(USER_1_USERNAME) - .password(USER_1_PASSWORD) - .attributes(USER_1_ATTRIBUTES_DTO) - .firstname(USER_1_FIRSTNAME) - .lastname(USER_1_LASTNAME) - .qualifiedName(USER_1_QUALIFIED_NAME) - .lastRetrieved(Instant.now()) - .build(); - public final static User USER_1 = User.builder() .id(USER_1_ID) .username(USER_1_USERNAME) @@ -736,17 +725,6 @@ public abstract class BaseTest { .tags(new String[]{}) .build(); - public final static UserDto USER_2_PRIVILEGED_DTO = UserDto.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .attributes(USER_2_ATTRIBUTES_DTO) - .firstname(USER_2_FIRSTNAME) - .lastname(USER_2_LASTNAME) - .qualifiedName(USER_2_QUALIFIED_NAME) - .lastRetrieved(Instant.now()) - .build(); - public final static Principal USER_2_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_2_DETAILS, USER_2_PASSWORD, USER_2_DETAILS.getAuthorities()); @@ -838,17 +816,6 @@ public abstract class BaseTest { .tags(new String[]{}) .build(); - public final static UserDto USER_3_PRIVILEGED_DTO = UserDto.builder() - .id(USER_3_ID) - .username(USER_3_USERNAME) - .password(USER_3_PASSWORD) - .attributes(USER_3_ATTRIBUTES_DTO) - .firstname(USER_3_FIRSTNAME) - .lastname(USER_3_LASTNAME) - .qualifiedName(USER_3_QUALIFIED_NAME) - .lastRetrieved(Instant.now()) - .build(); - public final static UUID USER_4_ID = UUID.fromString("791d58c5-bfab-4520-b4fc-b44d4ab9feb0"); public final static UUID USER_4_LDAP_ID = UUID.fromString("791d58c5-bfab-4520-b4fc-b44d4ab9feb0"); public final static String USER_4_USERNAME = "junit4"; @@ -920,17 +887,6 @@ public abstract class BaseTest { public final static Principal USER_4_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_4_DETAILS, USER_4_PASSWORD, USER_4_DETAILS.getAuthorities()); - public final static UserDto USER_4_PRIVILEGED_DTO = UserDto.builder() - .id(USER_4_ID) - .username(USER_4_USERNAME) - .password(USER_4_PASSWORD) - .attributes(USER_4_ATTRIBUTES_DTO) - .firstname(USER_4_FIRSTNAME) - .lastname(USER_4_LASTNAME) - .qualifiedName(USER_4_QUALIFIED_NAME) - .lastRetrieved(Instant.now()) - .build(); - public final static UUID USER_5_ID = UUID.fromString("28ff851d-d7bc-4422-959c-edd7a5b15630"); public final static UUID USER_5_LDAP_ID = UUID.fromString("28ff851d-d7bc-4422-959c-edd7a5b15630"); public final static String USER_5_USERNAME = "nobody"; @@ -968,17 +924,6 @@ public abstract class BaseTest { .attributes(USER_5_ATTRIBUTES_DTO) .build(); - public final static UserDto USER_5_PRIVILEGED_DTO = UserDto.builder() - .id(USER_5_ID) - .username(USER_5_USERNAME) - .firstname(USER_5_FIRSTNAME) - .lastname(USER_5_LASTNAME) - .qualifiedName(USER_5_QUALIFIED_NAME) - .password(USER_5_PASSWORD) - .attributes(USER_5_ATTRIBUTES_DTO) - .lastRetrieved(Instant.now()) - .build(); - public final static UserBriefDto USER_5_BRIEF_DTO = UserBriefDto.builder() .id(USER_5_ID) .username(USER_5_USERNAME) @@ -1132,8 +1077,6 @@ public abstract class BaseTest { .build())); public final static Long CONTAINER_1_ID = 1L; - public final static ContainerImage CONTAINER_1_IMAGE = IMAGE_1; - public final static ImageDto CONTAINER_1_IMAGE_DTO = IMAGE_1_DTO; public final static String CONTAINER_1_NAME = "u01"; public final static String CONTAINER_1_INTERNALNAME = "dbrepo-userdb-u01"; public final static String CONTAINER_1_UI_HOST = "localhost"; @@ -1152,7 +1095,7 @@ public abstract class BaseTest { .id(CONTAINER_1_ID) .name(CONTAINER_1_NAME) .internalName(CONTAINER_1_INTERNALNAME) - .image(CONTAINER_1_IMAGE) + .image(IMAGE_1) .created(CONTAINER_1_CREATED) .host(CONTAINER_1_HOST) .port(CONTAINER_1_PORT) @@ -1168,7 +1111,7 @@ public abstract class BaseTest { .id(CONTAINER_1_ID) .name(CONTAINER_1_NAME) .internalName(CONTAINER_1_INTERNALNAME) - .image(CONTAINER_1_IMAGE_DTO) + .image(IMAGE_1_DTO) .host(CONTAINER_1_HOST) .port(CONTAINER_1_PORT) .build(); @@ -1186,12 +1129,12 @@ public abstract class BaseTest { .id(CONTAINER_1_ID) .name(CONTAINER_1_NAME) .internalName(CONTAINER_1_INTERNALNAME) - .image(CONTAINER_1_IMAGE_DTO) + .image(IMAGE_1_DTO) .host(CONTAINER_1_HOST) .port(CONTAINER_1_PORT) + .lastRetrieved(Instant.now()) .username(CONTAINER_1_PRIVILEGED_USERNAME) .password(CONTAINER_1_PRIVILEGED_PASSWORD) - .lastRetrieved(Instant.now()) .build(); public final static Long CONTAINER_2_ID = 2L; @@ -1245,9 +1188,9 @@ public abstract class BaseTest { .image(CONTAINER_2_IMAGE_DTO) .host(CONTAINER_2_HOST) .port(CONTAINER_2_PORT) + .lastRetrieved(Instant.now()) .username(CONTAINER_2_PRIVILEGED_USERNAME) .password(CONTAINER_2_PRIVILEGED_PASSWORD) - .lastRetrieved(Instant.now()) .build(); public final static Long CONTAINER_3_ID = 3L; @@ -1390,6 +1333,21 @@ public abstract class BaseTest { .identifiers(new LinkedList<>()) /* IDENTIFIER_6_DTO */ .build(); + public final static DatabaseDto DATABASE_3_PRIVILEGED_DTO = DatabaseDto.builder() + .id(DATABASE_3_ID) + .isPublic(DATABASE_3_PUBLIC) + .isSchemaPublic(DATABASE_3_SCHEMA_PUBLIC) + .name(DATABASE_3_NAME) + .internalName(DATABASE_3_INTERNALNAME) + .owner(USER_3_BRIEF_DTO) + .container(CONTAINER_1_PRIVILEGED_DTO) + .exchangeName(DATABASE_3_EXCHANGE) + .tables(new LinkedList<>()) /* TABLE_8_DTO */ + .views(new LinkedList<>()) /* VIEW_5_DTO */ + .identifiers(new LinkedList<>()) /* IDENTIFIER_6_DTO */ + .lastRetrieved(Instant.now()) + .build(); + public final static DatabaseBriefDto DATABASE_3_BRIEF_DTO = DatabaseBriefDto.builder() .id(DATABASE_3_ID) .isPublic(DATABASE_3_PUBLIC) @@ -1434,6 +1392,7 @@ public abstract class BaseTest { .isPublic(DATABASE_4_PUBLIC) .isSchemaPublic(DATABASE_4_SCHEMA_PUBLIC) .name(DATABASE_4_NAME) + .container(CONTAINER_2_DTO) .description(DATABASE_4_DESCRIPTION) .internalName(DATABASE_4_INTERNALNAME) .exchangeName(DATABASE_4_EXCHANGE) @@ -1443,6 +1402,22 @@ public abstract class BaseTest { .identifiers(new LinkedList<>()) /* IDENTIFIER_7_DTO */ .build(); + public final static DatabaseDto DATABASE_4_PRIVILEGED_DTO = DatabaseDto.builder() + .id(DATABASE_4_ID) + .isPublic(DATABASE_4_PUBLIC) + .isSchemaPublic(DATABASE_4_SCHEMA_PUBLIC) + .name(DATABASE_4_NAME) + .container(CONTAINER_2_PRIVILEGED_DTO) + .description(DATABASE_4_DESCRIPTION) + .internalName(DATABASE_4_INTERNALNAME) + .exchangeName(DATABASE_4_EXCHANGE) + .owner(USER_4_BRIEF_DTO) + .tables(new LinkedList<>()) /* TABLE_9_DTO */ + .views(new LinkedList<>()) + .identifiers(new LinkedList<>()) /* IDENTIFIER_7_DTO */ + .lastRetrieved(Instant.now()) + .build(); + public final static TableCreateDto TABLE_0_CREATE_DTO = TableCreateDto.builder() .name("full") .description("full example") @@ -1650,6 +1625,7 @@ public abstract class BaseTest { .dataLength(TABLE_1_DATA_LENGTH) .maxDataLength(TABLE_1_MAX_DATA_LENGTH) .lastRetrieved(Instant.now()) + .database(null) /* DATABASE_1_PRIVILEGED_DTO */ .build(); public final static Table TABLE_1 = Table.builder() @@ -1863,6 +1839,7 @@ public abstract class BaseTest { .dataLength(TABLE_2_DATA_LENGTH) .maxDataLength(TABLE_2_MAX_DATA_LENGTH) .lastRetrieved(Instant.now()) + .database(null) /* DATABASE_1_PRIVILEGED_DTO */ .build(); public final static TableDto TABLE_2_DTO = TableDto.builder() @@ -5169,6 +5146,7 @@ public abstract class BaseTest { .queryHash(VIEW_1_QUERY_HASH) .columns(VIEW_1_COLUMNS_DTO) .lastRetrieved(Instant.now()) + .database(null) /* DATABASE_1_PRIVILEGED_DTO */ .build(); public final static ViewBriefDto VIEW_1_BRIEF_DTO = ViewBriefDto.builder() @@ -5331,6 +5309,7 @@ public abstract class BaseTest { .queryHash(VIEW_2_QUERY_HASH) .columns(VIEW_2_COLUMNS_DTO) .lastRetrieved(Instant.now()) + .database(null) /* DATABASE_1_PRIVILEGED_DTO */ .build(); public final static ViewBriefDto VIEW_2_BRIEF_DTO = ViewBriefDto.builder() @@ -5431,6 +5410,7 @@ public abstract class BaseTest { .queryHash(VIEW_3_QUERY_HASH) .columns(VIEW_3_COLUMNS_DTO) .lastRetrieved(Instant.now()) + .database(null) /* DATABASE_1_PRIVILEGED_DTO */ .build(); public final static List<ViewColumn> VIEW_3_COLUMNS = List.of( @@ -7564,6 +7544,20 @@ public abstract class BaseTest { .views(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO))) .build(); + public final static DatabaseDto DATABASE_1_PRIVILEGED_DTO = DatabaseDto.builder() + .id(DATABASE_1_ID) + .isPublic(DATABASE_1_PUBLIC) + .isSchemaPublic(DATABASE_1_SCHEMA_PUBLIC) + .name(DATABASE_1_NAME) + .container(CONTAINER_1_PRIVILEGED_DTO) + .internalName(DATABASE_1_INTERNALNAME) + .exchangeName(DATABASE_1_EXCHANGE) + .identifiers(new LinkedList<>(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO))) + .tables(new LinkedList<>(List.of(TABLE_1_DTO, TABLE_2_DTO, TABLE_3_DTO, TABLE_4_DTO))) + .views(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO))) + .lastRetrieved(Instant.now()) + .build(); + public final static DatabaseBriefDto DATABASE_1_BRIEF_DTO = DatabaseBriefDto.builder() .id(DATABASE_1_ID) .isPublic(DATABASE_1_PUBLIC) @@ -7705,6 +7699,21 @@ public abstract class BaseTest { .build(); public final static DatabaseDto DATABASE_2_DTO = DatabaseDto.builder() + .id(DATABASE_2_ID) + .isPublic(DATABASE_2_PUBLIC) + .isSchemaPublic(DATABASE_2_SCHEMA_PUBLIC) + .name(DATABASE_2_NAME) + .container(CONTAINER_1_DTO) + .internalName(DATABASE_2_INTERNALNAME) + .exchangeName(DATABASE_2_EXCHANGE) + .identifiers(new LinkedList<>(List.of(IDENTIFIER_5_DTO))) + .tables(new LinkedList<>(List.of(TABLE_5_DTO, TABLE_6_DTO, TABLE_7_DTO))) + .views(new LinkedList<>(List.of(VIEW_4_DTO))) + .owner(USER_2_BRIEF_DTO) + .lastRetrieved(Instant.now()) + .build(); + + public final static DatabaseDto DATABASE_2_PRIVILEGED_DTO = DatabaseDto.builder() .id(DATABASE_2_ID) .isPublic(DATABASE_2_PUBLIC) .isSchemaPublic(DATABASE_2_SCHEMA_PUBLIC) diff --git a/lib/python/dbrepo/RestClient.py b/lib/python/dbrepo/RestClient.py index 1f9f778a84..78861ffd04 100644 --- a/lib/python/dbrepo/RestClient.py +++ b/lib/python/dbrepo/RestClient.py @@ -252,7 +252,7 @@ class RestClient: f'201 (CREATED): {response.text}') def update_user(self, user_id: str, theme: str, language: str, firstname: str = None, lastname: str = None, - affiliation: str = None, orcid: str = None) -> User: + affiliation: str = None, orcid: str = None) -> UserBrief: """ Updates a user with given user id. @@ -277,7 +277,7 @@ class RestClient: lastname=lastname, affiliation=affiliation, orcid=orcid)) if response.status_code == 202: body = response.json() - return User.model_validate(body) + return UserBrief.model_validate(body) if response.status_code == 400: raise MalformedError(f'Failed to update user: {response.text}') if response.status_code == 403: @@ -287,15 +287,13 @@ class RestClient: raise ResponseCodeError(f'Failed to update user: response code: {response.status_code} is not ' f'202 (ACCEPTED): {response.text}') - def update_user_password(self, user_id: str, password: str) -> User: + def update_user_password(self, user_id: str, password: str) -> None: """ Updates the password of a user with given user id. :param user_id: The user id of the user that should be updated. :param password: The updated user password. - :returns: The user, if successful. - :raises MalformedError: If the payload was rejected by the service. :raises ForbiddenError: If something went wrong with the authorization. :raises NotExistsError: If the user does not exist. @@ -306,8 +304,7 @@ class RestClient: url = f'/api/user/{user_id}/password' response = self._wrapper(method="put", url=url, force_auth=True, payload=UpdateUserPassword(password=password)) if response.status_code == 202: - body = response.json() - return User.model_validate(body) + return None if response.status_code == 400: raise MalformedError(f'Failed to update user password: {response.text}') if response.status_code == 403: @@ -617,7 +614,7 @@ class RestClient: def create_table(self, database_id: int, name: str, is_public: bool, is_schema_public: bool, columns: List[CreateTableColumn], constraints: CreateTableConstraints, - description: str = None) -> Table: + description: str = None) -> TableBrief: """ Updates the database owner of a database with given database id. @@ -645,7 +642,7 @@ class RestClient: description=description, columns=columns, constraints=constraints)) if response.status_code == 201: body = response.json() - return Table.model_validate(body) + return TableBrief.model_validate(body) if response.status_code == 400: raise MalformedError(f'Failed to create table: {response.text}') if response.status_code == 403: @@ -871,7 +868,7 @@ class RestClient: raise ResponseCodeError(f'Failed to find view: response code: {response.status_code} is not ' f'200 (OK): {response.text}') - def update_view(self, database_id: int, view_id: int, is_public: bool) -> View: + def update_view(self, database_id: int, view_id: int, is_public: bool) -> ViewBrief: """ Get a view of a database with given database id and view id. @@ -889,7 +886,7 @@ class RestClient: response = self._wrapper(method="put", url=url, payload=UpdateView(is_public=is_public)) if response.status_code == 202: body = response.json() - return View.model_validate(body) + return ViewBrief.model_validate(body) if response.status_code == 403: raise ForbiddenError(f'Failed to update view: not allowed') if response.status_code == 404: -- GitLab From 1b76873fad8e2c3a78c90f9ef53625a246b7afde Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Fri, 24 Jan 2025 10:13:57 +0100 Subject: [PATCH 15/19] WIP Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .../java/at/tuwien/api/database/ViewDto.java | 2 + .../tuwien/api/database/table/TableDto.java | 2 + .../java/at/tuwien/mapper/MetadataMapper.java | 6 +++ ...nticationPrivilegedIntegrationMvcTest.java | 35 ++----------- lib/python/tests/test_unit_table.py | 49 ++++--------------- lib/python/tests/test_unit_user.py | 10 ++-- 6 files changed, 27 insertions(+), 77 deletions(-) diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java index 5f9373bb12..82a7081e16 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewDto.java @@ -65,6 +65,8 @@ public class ViewDto extends CacheableDto { @Schema(example = "7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916") private String queryHash; + @ToString.Exclude + @EqualsAndHashCode.Exclude private DatabaseDto database; @NotNull diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java index e456908ed6..1021d167ca 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableDto.java @@ -107,6 +107,8 @@ public class TableDto extends CacheableDto { @NotNull private List<ColumnDto> columns; + @ToString.Exclude + @EqualsAndHashCode.Exclude private DatabaseDto database; @NotNull 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 cd0b90f9f6..bd6b952c17 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 @@ -536,6 +536,8 @@ public interface MetadataMapper { } default TableDto tableToTableDto(Table data) { + data.getDatabase() + .setTables(null); final TableDto table = TableDto.builder() .id(data.getId()) .name(data.getName()) @@ -548,6 +550,7 @@ public interface MetadataMapper { .description(data.getDescription()) .identifiers(new LinkedList<>()) .columns(new LinkedList<>()) + .database(databaseToDatabaseDto(data.getDatabase())) .constraints(constraintsToConstraintsDto(data.getConstraints())) .build(); if (data.getIdentifiers() != null) { @@ -833,6 +836,9 @@ public interface MetadataMapper { .trim(); } + @Mappings({ + @Mapping(target = "database.views", ignore = true) + }) ViewDto viewToViewDto(View data); @Mappings({ diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/AuthenticationPrivilegedIntegrationMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/AuthenticationPrivilegedIntegrationMvcTest.java index eec5aebf4b..fa0b6f64f7 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/AuthenticationPrivilegedIntegrationMvcTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/AuthenticationPrivilegedIntegrationMvcTest.java @@ -10,7 +10,6 @@ import at.tuwien.repository.DatabaseRepository; import at.tuwien.repository.LicenseRepository; import at.tuwien.repository.UserRepository; import at.tuwien.service.AuthenticationService; -import at.tuwien.service.UserService; import at.tuwien.test.AbstractUnitTest; import at.tuwien.utils.KeycloakUtils; import dasniko.testcontainers.keycloak.KeycloakContainer; @@ -31,7 +30,7 @@ import org.testcontainers.junit.jupiter.Testcontainers; import java.util.List; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; @@ -120,9 +119,7 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest .andDo(print()) .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME)) .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD)) - .andExpect(header().string("X-Host", CONTAINER_1_HOST)) - .andExpect(header().string("X-Port", "" + CONTAINER_1_PORT)) - .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port")) + .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password")) .andExpect(status().isOk()); } @@ -138,9 +135,7 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest .andDo(print()) .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME)) .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD)) - .andExpect(header().string("X-Host", CONTAINER_1_HOST)) - .andExpect(header().string("X-Port", "" + CONTAINER_1_PORT)) - .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port")) + .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password")) .andExpect(status().isOk()); } @@ -157,12 +152,7 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest .andDo(print()) .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME)) .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD)) - .andExpect(header().string("X-Host", CONTAINER_1_HOST)) - .andExpect(header().string("X-Port", "" + CONTAINER_1_PORT)) - .andExpect(header().string("X-Type", IMAGE_1_JDBC)) - .andExpect(header().string("X-Database", DATABASE_1_INTERNALNAME)) - .andExpect(header().string("X-Table", TABLE_1_INTERNAL_NAME)) - .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port X-Type X-Database X-Table")) + .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password")) .andExpect(status().isOk()); } @@ -177,11 +167,6 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest .andDo(print()) .andExpect(header().doesNotExist("X-Username")) .andExpect(header().doesNotExist("X-Password")) - .andExpect(header().doesNotExist("X-Host")) - .andExpect(header().doesNotExist("X-Port")) - .andExpect(header().doesNotExist("X-Type")) - .andExpect(header().doesNotExist("X-Database")) - .andExpect(header().doesNotExist("X-Table")) .andExpect(header().doesNotExist("Access-Control-Expose-Headers")) .andExpect(status().isOk()); } @@ -197,12 +182,7 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest .andDo(print()) .andExpect(header().string("X-Username", CONTAINER_1_PRIVILEGED_USERNAME)) .andExpect(header().string("X-Password", CONTAINER_1_PRIVILEGED_PASSWORD)) - .andExpect(header().string("X-Host", CONTAINER_1_HOST)) - .andExpect(header().string("X-Port", "" + CONTAINER_1_PORT)) - .andExpect(header().string("X-Type", IMAGE_1_JDBC)) - .andExpect(header().string("X-Database", DATABASE_1_INTERNALNAME)) - .andExpect(header().string("X-Table", TABLE_1_INTERNAL_NAME)) - .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password X-Host X-Port X-Type X-Database X-Table")) + .andExpect(header().string("Access-Control-Expose-Headers", "X-Username X-Password")) .andExpect(status().isOk()); } @@ -217,11 +197,6 @@ public class AuthenticationPrivilegedIntegrationMvcTest extends AbstractUnitTest .andDo(print()) .andExpect(header().doesNotExist("X-Username")) .andExpect(header().doesNotExist("X-Password")) - .andExpect(header().doesNotExist("X-Host")) - .andExpect(header().doesNotExist("X-Port")) - .andExpect(header().doesNotExist("X-Type")) - .andExpect(header().doesNotExist("X-Database")) - .andExpect(header().doesNotExist("X-View")) .andExpect(header().doesNotExist("Access-Control-Expose-Headers")) .andExpect(status().isOk()); } diff --git a/lib/python/tests/test_unit_table.py b/lib/python/tests/test_unit_table.py index 36391e1fdd..eccf8c2a59 100644 --- a/lib/python/tests/test_unit_table.py +++ b/lib/python/tests/test_unit_table.py @@ -14,46 +14,15 @@ from dbrepo.api.exceptions import MalformedError, ForbiddenError, NotExistsError class TableUnitTest(unittest.TestCase): def test_create_table_succeeds(self): - exp = Table(id=2, - name="Test", - description="Test Table", - database_id=1, - internal_name="test", - owner=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'), - is_versioned=True, - queue_name='test', - routing_key='dbrepo.test_database_1234.test', - is_public=True, - is_schema_public=True, - constraints=Constraints(uniques=[], - foreign_keys=[], - checks=[], - primary_key=[PrimaryKey(id=1, - table=TableBrief(id=2, database_id=1, - name='Other', - internal_name='other', - description=None, - is_versioned=True, - is_public=True, - is_schema_public=True, - owned_by='8638c043-5145-4be8-a3e4-4b79991b0a16'), - column=ColumnBrief(id=1, table_id=2, - database_id=1, - name='id', - alias=None, - internal_name='id', - type=ColumnType.BIGINT))]), - columns=[Column(id=1, - ord=0, - name="ID", - database_id=1, - table_id=2, - internal_name="id", - auto_generated=True, - is_primary_key=True, - type=ColumnType.BIGINT, - is_public=True, - is_null_allowed=False)]) + exp = TableBrief(id=2, + database_id=1, + name="Test", + description="Test Table", + internal_name="test", + owned_by='8638c043-5145-4be8-a3e4-4b79991b0a16', + is_versioned=True, + is_public=True, + is_schema_public=True) with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/table', json=exp.model_dump(), status_code=201) diff --git a/lib/python/tests/test_unit_user.py b/lib/python/tests/test_unit_user.py index 0d1e720885..0e846b1b42 100644 --- a/lib/python/tests/test_unit_user.py +++ b/lib/python/tests/test_unit_user.py @@ -125,8 +125,8 @@ class UserUnitTest(unittest.TestCase): def test_update_user_succeeds(self): with requests_mock.Mocker() as mock: - exp = User(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise', given_name='Martin', - attributes=UserAttributes(theme='dark')) + exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise', given_name='Martin', + attributes=UserAttributes(theme='dark')) # mock mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=202, json=exp.model_dump()) @@ -173,16 +173,12 @@ class UserUnitTest(unittest.TestCase): def test_update_user_password_succeeds(self): with requests_mock.Mocker() as mock: - exp = User(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise', given_name='Martin', - attributes=UserAttributes(theme='dark')) # mock - mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=202, - json=exp.model_dump()) + mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=202) # test client = RestClient(username="a", password="b") response = client.update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', password='s3cr3t1n0rm4t10n') - self.assertEqual(exp, response) def test_update_user_password_not_allowed_fails(self): with requests_mock.Mocker() as mock: -- GitLab From 53a9f3ad10fad93f1963d7b323dff8804fb64af1 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Fri, 24 Jan 2025 14:41:31 +0100 Subject: [PATCH 16/19] Fixed some DTOs Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .docs/.openapi/api-data.yaml | 352 +-- .docs/.openapi/api-metadata.yaml | 1913 +++++++++-------- .docs/.openapi/api.base.yaml | 2 +- .docs/.openapi/swagger-ui.html | 6 +- .../at/tuwien/api/auth/CreateUserDto.java | 15 +- .../at/tuwien/api/auth/SignupRequestDto.java | 35 - ...eCreateDto.java => CreateDatabaseDto.java} | 2 +- ...{ViewCreateDto.java => CreateViewDto.java} | 2 +- ...ableCreateDto.java => CreateTableDto.java} | 10 +- ...eateDto.java => CreateTableColumnDto.java} | 2 +- ...to.java => CreateTableConstraintsDto.java} | 6 +- ...reateDto.java => CreateForeignKeyDto.java} | 2 +- .../table/internal/TableCreateDto.java | 8 +- ...reateDto.java => CreateIdentifierDto.java} | 12 +- .../api/identifier/IdentifierSaveDto.java | 10 +- ...Dto.java => SaveIdentifierCreatorDto.java} | 2 +- ...java => SaveIdentifierDescriptionDto.java} | 2 +- ...eDto.java => SaveIdentifierFunderDto.java} | 2 +- ...leDto.java => SaveIdentifierTitleDto.java} | 2 +- ...Dto.java => SaveRelatedIdentifierDto.java} | 2 +- .../java/at/tuwien/mapper/MetadataMapper.java | 26 +- .../at/tuwien/endpoints/DatabaseEndpoint.java | 2 +- .../tuwien/endpoints/IdentifierEndpoint.java | 2 +- .../at/tuwien/endpoints/TableEndpoint.java | 4 +- .../at/tuwien/endpoints/UserEndpoint.java | 6 +- .../at/tuwien/endpoints/ViewEndpoint.java | 4 +- .../tuwien/validation/EndpointValidator.java | 26 +- .../endpoints/DatabaseEndpointUnitTest.java | 10 +- .../endpoints/TableEndpointUnitTest.java | 68 +- .../endpoints/UserEndpointUnitTest.java | 10 +- .../endpoints/ViewEndpointUnitTest.java | 4 +- .../service/TableServicePersistenceTest.java | 14 +- .../tuwien/service/TableServiceUnitTest.java | 40 +- .../service/UserServicePersistenceTest.java | 6 +- .../tuwien/service/ViewServiceUnitTest.java | 4 +- .../validator/EndpointValidatorUnitTest.java | 16 +- .../at/tuwien/gateway/DataServiceGateway.java | 8 +- .../gateway/impl/DataServiceGatewayImpl.java | 6 +- .../tuwien/service/AuthenticationService.java | 4 +- .../at/tuwien/service/DatabaseService.java | 4 +- .../at/tuwien/service/IdentifierService.java | 4 +- .../java/at/tuwien/service/TableService.java | 4 +- .../java/at/tuwien/service/UserService.java | 4 +- .../java/at/tuwien/service/ViewService.java | 5 +- .../impl/AuthenticationServiceImpl.java | 4 +- .../impl/DataCiteIdentifierServiceImpl.java | 4 +- .../service/impl/DatabaseServiceImpl.java | 7 +- .../service/impl/IdentifierServiceImpl.java | 4 +- .../tuwien/service/impl/TableServiceImpl.java | 8 +- .../tuwien/service/impl/UserServiceImpl.java | 4 +- .../tuwien/service/impl/ViewServiceImpl.java | 4 +- .../main/java/at/tuwien/test/BaseTest.java | 239 +- lib/python/dbrepo/RestClient.py | 16 +- lib/python/dbrepo/api/dto.py | 16 +- lib/python/tests/test_dtos.py | 4 +- lib/python/tests/test_unit_identifier.py | 32 +- 56 files changed, 1490 insertions(+), 1520 deletions(-) delete mode 100644 dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/{DatabaseCreateDto.java => CreateDatabaseDto.java} (96%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/{ViewCreateDto.java => CreateViewDto.java} (96%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/{TableCreateDto.java => CreateTableDto.java} (77%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/{ColumnCreateDto.java => CreateTableColumnDto.java} (97%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/{ConstraintsCreateDto.java => CreateTableConstraintsDto.java} (77%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/{ForeignKeyCreateDto.java => CreateForeignKeyDto.java} (95%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/{IdentifierCreateDto.java => CreateIdentifierDto.java} (84%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/{CreatorSaveDto.java => SaveIdentifierCreatorDto.java} (97%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/{IdentifierSaveDescriptionDto.java => SaveIdentifierDescriptionDto.java} (94%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/{IdentifierFunderSaveDto.java => SaveIdentifierFunderDto.java} (96%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/{IdentifierSaveTitleDto.java => SaveIdentifierTitleDto.java} (95%) rename dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/{RelatedIdentifierSaveDto.java => SaveRelatedIdentifierDto.java} (93%) diff --git a/.docs/.openapi/api-data.yaml b/.docs/.openapi/api-data.yaml index f61466dc6b..fad4c0fd74 100644 --- a/.docs/.openapi/api-data.yaml +++ b/.docs/.openapi/api-data.yaml @@ -74,32 +74,32 @@ paths: application/json: schema: type: string - "503": - description: Failed to establish connection with the metadata service + "400": + description: Request pagination is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find view in metadata database + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "409": - description: View schema could not be mapped + "404": + description: Failed to find view in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Request pagination is malformed + "403": + description: Not allowed to retrieve view data content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to retrieve view data + "409": + description: View schema could not be mapped content: application/json: schema: @@ -162,32 +162,32 @@ paths: application/json: schema: type: string - "503": - description: Failed to establish connection with the metadata service + "400": + description: Request pagination is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find view in metadata database + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "409": - description: View schema could not be mapped + "404": + description: Failed to find view in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Request pagination is malformed + "403": + description: Not allowed to retrieve view data content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to retrieve view data + "409": + description: View schema could not be mapped content: application/json: schema: @@ -237,8 +237,8 @@ paths: type: integer format: int64 responses: - "400": - description: Request pagination or table data select query is malformed + "403": + description: Not allowed to get table data content: application/json: schema: @@ -249,6 +249,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Get table data headers: @@ -264,14 +270,8 @@ paths: application/json: schema: type: string - "404": - description: Failed to find table in metadata database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to get table data + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: @@ -312,8 +312,8 @@ paths: $ref: "#/components/schemas/TupleUpdateDto" required: true responses: - "400": - description: Request pagination or table data select query is malformed + "403": + description: Update table data not allowed content: application/json: schema: @@ -324,20 +324,20 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Update table data not allowed + "202": + description: Updated table data + "404": + description: Failed to find table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find table in metadata database + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Updated table data security: - basicAuth: [] - bearerAuth: [] @@ -374,8 +374,15 @@ paths: $ref: "#/components/schemas/TupleDto" required: true responses: - "400": - description: Request pagination or table data select query is malformed + "503": + description: Failed to establish connection with the metadata service or + storage service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Create table data not allowed content: application/json: schema: @@ -389,15 +396,8 @@ paths: $ref: "#/components/schemas/ApiErrorDto" "201": description: Created table data - "503": - description: Failed to establish connection with the metadata service or - storage service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Create table data not allowed + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: @@ -438,8 +438,8 @@ paths: $ref: "#/components/schemas/TupleDeleteDto" required: true responses: - "400": - description: Request pagination or table data select query is malformed + "403": + description: Delete table data not allowed content: application/json: schema: @@ -450,16 +450,16 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted table data "404": description: Failed to find table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Deleted table data - "403": - description: Delete table data not allowed + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: @@ -508,8 +508,8 @@ paths: type: integer format: int64 responses: - "400": - description: Request pagination or table data select query is malformed + "403": + description: Not allowed to get table data content: application/json: schema: @@ -520,6 +520,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Get table data headers: @@ -535,14 +541,8 @@ paths: application/json: schema: type: string - "404": - description: Failed to find table in metadata database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to get table data + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: @@ -586,14 +586,27 @@ paths: type: integer format: int64 responses: + "403": + description: Not allowed to retrieve subset data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "503": description: Failed to communicate with database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to retrieve subset data + "400": + description: Invalid pagination content: application/json: schema: @@ -619,19 +632,6 @@ paths: application/json: schema: type: string - "404": - description: Failed to find database in metadata database or query in query - store of the data database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Invalid pagination - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -670,14 +670,27 @@ paths: type: integer format: int64 responses: + "403": + description: Not allowed to retrieve subset data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "503": description: Failed to communicate with database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to retrieve subset data + "400": + description: Invalid pagination content: application/json: schema: @@ -703,19 +716,6 @@ paths: application/json: schema: type: string - "404": - description: Failed to find database in metadata database or query in query - store of the data database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Invalid pagination - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -752,8 +752,9 @@ paths: application/json: schema: $ref: "#/components/schemas/QueryDto" - "403": - description: Not allowed to persist subset + "404": + description: Failed to find database in metadata database or query in query + store of the data database content: application/json: schema: @@ -770,15 +771,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Malformed select query + "403": + description: Not allowed to persist subset content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database in metadata database or query in query - store of the data database + "400": + description: Malformed select query content: application/json: schema: @@ -823,26 +823,26 @@ paths: responses: "202": description: Imported dataset successfully - "503": - description: Failed to establish connection with the metadata service + "403": + description: Import table dataset not allowed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find table in metadata database + "400": + description: Dataset and/or query are malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Import table dataset not allowed + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Dataset and/or query are malformed + "404": + description: Failed to find table in metadata database content: application/json: schema: @@ -874,29 +874,29 @@ paths: schema: type: boolean responses: - "200": - description: Found subsets + "404": + description: Failed to find database in metadata database or query in query + store of the data database content: application/json: schema: - type: array - items: - $ref: "#/components/schemas/QueryDto" - "503": - description: Failed to communicate with database + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to find subsets content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database in metadata database or query in query - store of the data database + "200": + description: Found subsets content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to find subsets + type: array + items: + $ref: "#/components/schemas/QueryDto" + "503": + description: Failed to communicate with database content: application/json: schema: @@ -945,32 +945,27 @@ paths: $ref: "#/components/schemas/ExecuteStatementDto" required: true responses: - "403": - description: Not allowed to find subset - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "501": - description: Failed to execute query as it contains non-supported keywords + "417": + description: Failed to insert query into query store of data database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to communicate with database + "404": + description: Failed to find database in metadata database or query in query + store of the data database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Malformed select query + "501": + description: Failed to execute query as it contains non-supported keywords content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "417": - description: Failed to insert query into query store of data database + "403": + description: Not allowed to find subset content: application/json: schema: @@ -981,9 +976,14 @@ paths: application/json: schema: type: string - "404": - description: Failed to find database in metadata database or query in query - store of the data database + "503": + description: Failed to communicate with database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Malformed select query content: application/json: schema: @@ -1020,33 +1020,33 @@ paths: type: string format: date-time responses: - "400": - description: Request pagination or view data select query is malformed + "404": + description: Failed to find view in metadata database or export dataset content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Exported view data + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: - type: string - format: binary + $ref: "#/components/schemas/ApiErrorDto" "403": description: Export view data not allowed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find view in metadata database or export dataset + "200": + description: Exported view data content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + type: string + format: binary + "400": + description: Request pagination or view data select query is malformed content: application/json: schema: @@ -1083,8 +1083,8 @@ paths: type: integer format: int64 responses: - "400": - description: "Invalid pagination size request, must be > 0" + "404": + description: Failed to find table history in data database content: application/json: schema: @@ -1095,8 +1095,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find table history in data database + "403": + description: Find table history not allowed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: "Invalid pagination size request, must be > 0" content: application/json: schema: @@ -1109,12 +1115,6 @@ paths: type: array items: $ref: "#/components/schemas/TableHistoryDto" - "403": - description: Find table history not allowed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - basicAuth: [] - bearerAuth: [] @@ -1147,12 +1147,6 @@ paths: type: string format: date-time responses: - "400": - description: Request pagination or table data select query is malformed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "403": description: Export table data not allowed content: @@ -1178,6 +1172,12 @@ paths: schema: type: string format: binary + "400": + description: Request pagination or table data select query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - basicAuth: [] - bearerAuth: [] @@ -1217,6 +1217,13 @@ paths: type: string format: date-time responses: + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "403": description: Not allowed to find subset content: @@ -1235,12 +1242,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Malformed select query - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Found subset content: @@ -1248,9 +1249,8 @@ paths: schema: $ref: "#/components/schemas/QueryDto" text/csv: {} - "404": - description: Failed to find database in metadata database or query in query - store of the data database + "400": + description: Malformed select query content: application/json: schema: diff --git a/.docs/.openapi/api-metadata.yaml b/.docs/.openapi/api-metadata.yaml index 2696cecd0c..6792f0f0e9 100644 --- a/.docs/.openapi/api-metadata.yaml +++ b/.docs/.openapi/api-metadata.yaml @@ -61,29 +61,35 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/DatabaseCreateDto" + $ref: "#/components/schemas/CreateDatabaseDto" required: true responses: - "503": - description: Failed to save in search service + "409": + description: Query store could not be created content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "201": + description: Created a new database + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Database create query is malformed or image is not supported + "404": + description: Failed to fin container/user/database in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "423": - description: Database quota exceeded + "502": + description: Connection to search service failed content: application/json: schema: @@ -95,20 +101,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Created a new database - content: - application/json: - schema: - $ref: "#/components/schemas/DatabaseBriefDto" - "409": - description: Query store could not be created + "423": + description: Database quota exceeded content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to fin container/user/database in metadata database + "400": + description: Database create query is malformed or image is not supported content: application/json: schema: @@ -173,6 +173,12 @@ paths: type: string format: uuid responses: + "403": + description: No access to this database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "404": description: Database not found content: @@ -185,12 +191,6 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseAccessDto" - "403": - description: No access to this database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -221,36 +221,36 @@ paths: $ref: "#/components/schemas/CreateAccessDto" required: true responses: - "404": - description: Database or user not found + "403": + description: Modify access not permitted when no access is granted in the + first place content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Access could not be updated due to connection error in the - data service + "404": + description: Database or user not found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Access could not be updated in the data service + "400": + description: Modify access query or database connection is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" "202": description: Modified access - "400": - description: Modify access query or database connection is malformed + "502": + description: Access could not be updated due to connection error in the + data service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Modify access not permitted when no access is granted in the - first place + "503": + description: Access could not be updated in the data service content: application/json: schema: @@ -285,14 +285,8 @@ paths: $ref: "#/components/schemas/CreateAccessDto" required: true responses: - "400": - description: Granting access query or database connection is malformed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Failed giving access + "502": + description: Access could not be created due to connection error content: application/json: schema: @@ -303,24 +297,30 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Access could not be created due to connection error + "400": + description: Granting access query or database connection is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Granting access succeeded + "403": + description: Failed giving access content: application/json: schema: - $ref: "#/components/schemas/DatabaseAccessDto" + $ref: "#/components/schemas/ApiErrorDto" "503": description: Access could not be created in the data service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Granting access succeeded + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseAccessDto" security: - bearerAuth: [] - basicAuth: [] @@ -345,34 +345,34 @@ paths: type: string format: uuid responses: - "503": - description: Access could not be revoked in the data service + "502": + description: Access could not be created due to connection error content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Revoke of access not permitted as no access was found + "400": + description: Modify access query or database connection is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Deleted access - "502": - description: Access could not be created due to connection error + "404": + description: "User, database with access was not found" content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "User, database with access was not found" + "202": + description: Deleted access + "503": + description: Access could not be revoked in the data service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Modify access query or database connection is malformed + "403": + description: Revoke of access not permitted as no access was found content: application/json: schema: @@ -404,6 +404,12 @@ paths: type: string format: uuid responses: + "403": + description: No access to this database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "404": description: Database not found content: @@ -416,12 +422,6 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseAccessDto" - "403": - description: No access to this database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -449,18 +449,18 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: User was not found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Found user content: application/json: schema: $ref: "#/components/schemas/UserDto" + "404": + description: User was not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -484,14 +484,14 @@ paths: $ref: "#/components/schemas/UserUpdateDto" required: true responses: - "403": - description: Not allowed to modify user metadata + "400": + description: Modify user query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Modify user query is malformed + "404": + description: Failed to find database/user in metadata database content: application/json: schema: @@ -502,8 +502,8 @@ paths: application/json: schema: $ref: "#/components/schemas/UserDto" - "404": - description: Failed to find database/user in metadata database + "403": + description: Not allowed to modify user metadata content: application/json: schema: @@ -532,34 +532,34 @@ paths: $ref: "#/components/schemas/UserPasswordDto" required: true responses: - "400": - description: Invalid password payload + "403": + description: Not allowed to change foreign user password content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" "202": description: Modified user password - "502": - description: Connection to auth service failed + "404": + description: Failed to find database/user in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to change foreign user password + "503": + description: Failed to get user in auth service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database/user in metadata database + "502": + description: Connection to auth service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to get user in auth service + "400": + description: Invalid password payload content: application/json: schema: @@ -581,14 +581,14 @@ paths: $ref: "#/components/schemas/RefreshTokenRequestDto" required: true responses: - "400": - description: Invalid refresh token + "502": + description: Connection to auth service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed + "400": + description: Invalid refresh token content: application/json: schema: @@ -599,8 +599,8 @@ paths: application/json: schema: $ref: "#/components/schemas/TokenDto" - "502": - description: Connection to auth service failed + "403": + description: Not allowed content: application/json: schema: @@ -618,27 +618,26 @@ paths: $ref: "#/components/schemas/LoginRequestDto" required: true responses: - "428": - description: Account is not fully setup in auth service (requires password - change?) + "202": + description: Obtained user token content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to get token + $ref: "#/components/schemas/TokenDto" + "400": + description: Invalid login request content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Obtained user token + "403": + description: Not allowed to get token content: application/json: schema: - $ref: "#/components/schemas/TokenDto" - "404": - description: Failed to find user in auth database + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to get user in auth service content: application/json: schema: @@ -649,14 +648,15 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Invalid login request + "404": + description: Failed to find user in auth database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to get user in auth service + "428": + description: Account is not fully setup in auth service (requires password + change?) content: application/json: schema: @@ -676,18 +676,18 @@ paths: type: integer format: int64 responses: - "404": - description: Could not find ontology - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Find one ontology content: application/json: schema: $ref: "#/components/schemas/OntologyDto" + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" put: tags: - ontology-endpoint @@ -708,18 +708,18 @@ paths: $ref: "#/components/schemas/OntologyModifyDto" required: true responses: - "404": - description: Could not find ontology - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "202": description: Updated ontology successfully content: application/json: schema: $ref: "#/components/schemas/OntologyDto" + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -737,16 +737,16 @@ paths: type: integer format: int64 responses: + "202": + description: Deleted ontology successfully + content: + application/json: {} "404": description: Could not find ontology content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Deleted ontology successfully - content: - application/json: {} security: - bearerAuth: [] - basicAuth: [] @@ -800,16 +800,16 @@ paths: type: integer format: int64 responses: + "202": + description: Deleted message + content: + application/json: {} "404": description: Could not find message content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Deleted message - content: - application/json: {} security: - bearerAuth: [] - basicAuth: [] @@ -828,18 +828,18 @@ paths: type: integer format: int64 responses: - "200": - description: Found image - content: - application/json: - schema: - $ref: "#/components/schemas/ImageDto" "404": description: Image could not be found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found image + content: + application/json: + schema: + $ref: "#/components/schemas/ImageDto" put: tags: - image-endpoint @@ -923,27 +923,8 @@ paths: schema: type: string responses: - "400": - description: "Identifier could not be exported, the requested style is not\ - \ known" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "409": - description: Exported resource was not found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "406": - description: Failed to find acceptable representation - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to data service failed + "410": + description: Failed to retrieve from S3 endpoint content: application/json: schema: @@ -960,6 +941,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "406": + description: Failed to find acceptable representation + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Found identifier successfully content: @@ -975,8 +962,21 @@ paths: text/bibliography; style=apa: {} text/bibliography; style=ieee: {} text/bibliography; style=bibtex: {} - "410": - description: Failed to retrieve from S3 endpoint + "400": + description: "Identifier could not be exported, the requested style is not\ + \ known" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "409": + description: Exported resource was not found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to data service failed content: application/json: schema: @@ -1028,14 +1028,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Failed to find database, table or view" + "400": + description: Identifier form contains invalid request data content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Identifier form contains invalid request data + "404": + description: "Failed to find database, table or view" content: application/json: schema: @@ -1057,20 +1057,14 @@ paths: type: integer format: int64 responses: - "502": - description: Connection to search service failed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to delete in search service + "403": + description: Deleting identifier not permitted content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Deleting identifier not permitted + "502": + description: Connection to search service failed content: application/json: schema: @@ -1083,6 +1077,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to delete in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1102,12 +1102,6 @@ paths: type: integer format: int64 responses: - "202": - description: Published identifier - content: - application/json: - schema: - $ref: "#/components/schemas/IdentifierDto" "403": description: Insufficient access rights or authorities content: @@ -1126,18 +1120,24 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Failed to find database, table or view" + "202": + description: Published identifier content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" + $ref: "#/components/schemas/IdentifierDto" "400": description: Identifier form contains invalid request data content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Failed to find database, table or view" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1163,38 +1163,38 @@ paths: $ref: "#/components/schemas/DatabaseModifyVisibilityDto" required: true responses: - "202": - description: Visibility modified successfully + "400": + description: The visibility payload is malformed content: application/json: schema: - $ref: "#/components/schemas/DatabaseBriefDto" - "503": - description: Failed to save in search service + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Visibility modification is not permitted content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "404": + description: Failed to find database in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: The visibility payload is malformed + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database in metadata database + "202": + description: Visibility modified successfully content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Visibility modification is not permitted + $ref: "#/components/schemas/DatabaseBriefDto" + "502": + description: Connection to search service failed content: application/json: schema: @@ -1223,12 +1223,6 @@ paths: type: integer format: int64 responses: - "404": - description: "Database, view or user could not be found" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Find view successfully headers: @@ -1266,6 +1260,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, view or user could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1296,6 +1296,12 @@ paths: $ref: "#/components/schemas/ViewUpdateDto" required: true responses: + "400": + description: Update view query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "403": description: Update not allowed content: @@ -1308,8 +1314,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "404": + description: Database or View could not be found content: application/json: schema: @@ -1319,15 +1325,9 @@ paths: content: '*/*': schema: - $ref: "#/components/schemas/ViewDto" - "400": - description: Update view query is malformed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Database or View could not be found + $ref: "#/components/schemas/ViewBriefDto" + "502": + description: Connection to search service failed content: application/json: schema: @@ -1355,14 +1355,16 @@ paths: type: integer format: int64 responses: - "400": - description: Delete view query is malformed + "202": + description: Delete view successfully + "403": + description: Deletion not allowed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Deletion not allowed + "400": + description: Delete view query is malformed content: application/json: schema: @@ -1373,20 +1375,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Delete view successfully - content: - '*/*': - schema: - type: object - "423": - description: Delete view resulted in an invalid query statement + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "423": + description: Delete view resulted in an invalid query statement content: application/json: schema: @@ -1424,45 +1420,36 @@ paths: type: integer format: int64 responses: + "503": + description: Failed to obtain queue information from broker service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Find table successfully headers: X-Username: description: The authentication username style: simple - X-Table: - description: The table internal name - style: simple Access-Control-Expose-Headers: description: Expose custom headers style: simple - X-Type: - description: The JDBC connection type - style: simple - X-Database: - description: The database internal name - style: simple X-Password: description: The authentication password style: simple - X-Host: - description: The database hostname - style: simple - X-Port: - description: The database port number - style: simple content: application/json: schema: $ref: "#/components/schemas/TableDto" - "404": - description: "Table, database or container could not be found" + "403": + description: Access to the database is forbidden content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to obtain queue information from broker service + "404": + description: "Table, database or container could not be found" content: application/json: schema: @@ -1473,12 +1460,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Access to the database is forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1508,42 +1489,42 @@ paths: $ref: "#/components/schemas/TableUpdateDto" required: true responses: - "503": - description: Failed to save in search service + "400": + description: Update table visibility payload is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "403": + description: Update table visibility not permitted content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Updated the table + "404": + description: Table could not be found content: application/json: schema: - $ref: "#/components/schemas/TableBriefDto" - "400": - description: Update table visibility payload is malformed + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Update table visibility not permitted + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Table could not be found + "202": + description: Updated the table content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" + $ref: "#/components/schemas/TableBriefDto" security: - bearerAuth: [] - basicAuth: [] @@ -1569,34 +1550,34 @@ paths: type: integer format: int64 responses: - "404": - description: "Table, database or container could not be found" + "400": + description: Delete table query resulted in an invalid query statement content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "202": + description: Delete table successfully + "403": + description: Access to the database is forbidden content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "404": + description: "Table, database or container could not be found" content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Delete table successfully - "400": - description: Delete table query resulted in an invalid query statement + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Access to the database is forbidden + "502": + description: Connection to search service failed content: application/json: schema: @@ -1627,6 +1608,12 @@ paths: type: integer format: int64 responses: + "503": + description: Failed to save in search service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "403": description: Not the owner content: @@ -1639,8 +1626,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "502": + description: Connection to search service failed content: application/json: schema: @@ -1651,12 +1638,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "202": description: Updated table statistics successfully security: @@ -1698,15 +1679,15 @@ paths: $ref: "#/components/schemas/ColumnSemanticsUpdateDto" required: true responses: - "404": - description: Failed to find user/table/database/ontology in metadata database + "400": + description: Update semantic concept query is malformed or update unit of + measurement query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Update semantic concept query is malformed or update unit of - measurement query is malformed + "403": + description: Access to the database is forbidden content: application/json: schema: @@ -1717,6 +1698,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find user/table/database/ontology in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "502": description: Connection to search service failed content: @@ -1729,12 +1716,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ColumnDto" - "403": - description: Access to the database is forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1760,20 +1741,20 @@ paths: $ref: "#/components/schemas/DatabaseTransferDto" required: true responses: - "503": - description: Failed to save in search service + "400": + description: Owner payload is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "403": + description: Transfer of ownership is not permitted content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Transfer of ownership is not permitted + "404": + description: Database or user could not be found content: application/json: schema: @@ -1784,14 +1765,14 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseBriefDto" - "404": - description: Database or user could not be found + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Owner payload is malformed + "502": + description: Connection to search service failed content: application/json: schema: @@ -1816,36 +1797,36 @@ paths: type: integer format: int64 responses: - "403": - description: Refresh view metadata is not permitted + "404": + description: Failed to find database in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "200": + description: Refreshed database views metadata content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + $ref: "#/components/schemas/DatabaseBriefDto" + "403": + description: Refresh view metadata is not permitted content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database in metadata database + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Refreshed database views metadata + "502": + description: Connection to search service failed content: application/json: schema: - $ref: "#/components/schemas/DatabaseBriefDto" + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1866,20 +1847,20 @@ paths: type: integer format: int64 responses: - "503": - description: Failed to save in search service + "400": + description: Failed to parse payload at search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "403": + description: Not allowed to refresh table metadata content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Failed to parse payload at search service + "404": + description: Failed to fin user/database in metadata database content: application/json: schema: @@ -1890,14 +1871,14 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseBriefDto" - "403": - description: Not allowed to refresh table metadata + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to fin user/database in metadata database + "502": + description: Connection to search service failed content: application/json: schema: @@ -1920,6 +1901,12 @@ paths: type: integer format: int64 responses: + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: View of image was successful content: @@ -1929,12 +1916,6 @@ paths: items: type: string format: byte - "404": - description: Database or user could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1959,38 +1940,38 @@ paths: $ref: "#/components/schemas/DatabaseModifyImageDto" required: true responses: - "404": - description: Database could not be found + "403": + description: Modify of image is not permitted content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "410": + description: File was not found in the Storage Service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "202": + description: Modify of image was successful content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Modify of image is not permitted + $ref: "#/components/schemas/DatabaseBriefDto" + "404": + description: Database could not be found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Modify of image was successful + "503": + description: Failed to save in search service content: application/json: schema: - $ref: "#/components/schemas/DatabaseBriefDto" - "410": - description: File was not found in the Storage Service + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed content: application/json: schema: @@ -2033,55 +2014,55 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/SignupRequestDto" + $ref: "#/components/schemas/CreateUserDto" required: true responses: - "502": - description: Failed to create in auth service + "417": + description: User with e-mail already exists content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "409": - description: User with username already exists + "400": + description: Parameters are not well-formed (likely email) + content: + application/json: {} + "503": + description: Failed to create in auth service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Default role not found + "201": + description: Created user content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Internal authentication to the auth service is invalid + $ref: "#/components/schemas/UserDto" + "404": + description: Default role not found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "417": - description: User with e-mail already exists + "502": + description: Failed to create in auth service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Created user + "403": + description: Internal authentication to the auth service is invalid content: application/json: schema: - $ref: "#/components/schemas/UserDto" - "503": - description: Failed to create in auth service + $ref: "#/components/schemas/ApiErrorDto" + "409": + description: User with username already exists content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Parameters are not well-formed (likely email) - content: - application/json: {} /api/ontology: get: tags: @@ -2198,18 +2179,18 @@ paths: $ref: "#/components/schemas/ImageCreateDto" required: true responses: - "409": - description: Image already exists - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "201": description: Created image content: application/json: schema: $ref: "#/components/schemas/ImageDto" + "409": + description: Image already exists + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "400": description: Image specification is invalid content: @@ -2257,13 +2238,6 @@ paths: schema: type: string responses: - "406": - description: "Identifier could not be exported, the requested style is not\ - \ known" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Found identifiers successfully content: @@ -2277,6 +2251,13 @@ paths: type: array items: $ref: "#/components/schemas/LdDatasetDto" + "406": + description: "Identifier could not be exported, the requested style is not\ + \ known" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" post: tags: - identifier-endpoint @@ -2290,7 +2271,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/IdentifierCreateDto" + $ref: "#/components/schemas/CreateIdentifierDto" required: true responses: "403": @@ -2311,12 +2292,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Failed to find database, table or view" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "400": description: Identifier form contains invalid request data content: @@ -2329,6 +2304,12 @@ paths: application/json: schema: $ref: "#/components/schemas/IdentifierDto" + "404": + description: "Failed to find database, table or view" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2347,6 +2328,12 @@ paths: type: integer format: int64 responses: + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Find views successfully content: @@ -2355,12 +2342,6 @@ paths: type: array items: $ref: "#/components/schemas/ViewBriefDto" - "404": - description: Database or user could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2382,29 +2363,23 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/ViewCreateDto" + $ref: "#/components/schemas/CreateViewDto" required: true responses: - "503": - description: Failed to save in search service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "201": description: Create view successfully content: application/json: schema: $ref: "#/components/schemas/ViewBriefDto" - "423": - description: Create view resulted in an invalid query statement + "400": + description: Create view query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "403": + description: Credentials missing content: application/json: schema: @@ -2415,14 +2390,20 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Create view query is malformed + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Credentials missing + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "423": + description: Create view resulted in an invalid query statement content: application/json: schema: @@ -2487,11 +2468,17 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/TableCreateDto" + $ref: "#/components/schemas/CreateTableDto" required: true responses: - "404": - description: "Database, container or user could not be found" + "400": + description: Create table query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "409": + description: Create table conflicts with existing table name content: application/json: schema: @@ -2514,20 +2501,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "409": - description: Create table conflicts with existing table name - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "201": description: Created a new table content: application/json: schema: $ref: "#/components/schemas/TableBriefDto" - "400": - description: Create table query is malformed + "404": + description: "Database, container or user could not be found" content: application/json: schema: @@ -2571,32 +2552,32 @@ paths: $ref: "#/components/schemas/CreateContainerDto" required: true responses: - "400": - description: Container payload malformed + "409": + description: Container name already exists content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Created a new container - content: - application/json: - schema: - $ref: "#/components/schemas/ContainerDto" "403": description: "Create container not permitted, need authority `create-container`" content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "409": - description: Container name already exists + "404": + description: Container image or user could not be found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Container image or user could not be found + "201": + description: Created a new container + content: + application/json: + schema: + $ref: "#/components/schemas/ContainerDto" + "400": + description: Container payload malformed content: application/json: schema: @@ -2646,24 +2627,12 @@ paths: schema: type: string responses: - "404": - description: Could not find ontology - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "422": description: Ontology does not have rdf or sparql endpoint content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "417": - description: Generated query or uri is malformed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "400": description: Filter params are invalid content: @@ -2678,6 +2647,18 @@ paths: type: array items: $ref: "#/components/schemas/EntityDto" + "404": + description: Could not find ontology + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "417": + description: Generated query or uri is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2758,18 +2739,18 @@ paths: schema: type: string responses: - "200": - description: Retrieved metadata from identifier - content: - application/json: - schema: - $ref: "#/components/schemas/IdentifierDto" "404": description: Failed to find metadata for identifier content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Retrieved metadata from identifier + content: + application/json: + schema: + $ref: "#/components/schemas/IdentifierDto" /api/database/{databaseId}: get: tags: @@ -2785,24 +2766,6 @@ paths: type: integer format: int64 responses: - "403": - description: Not allowed to view database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Database, user or exchange could not be found" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to find queue information in broker service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Database found successfully headers: @@ -2819,12 +2782,30 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseBriefDto" + "404": + description: "Database, user or exchange could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to find queue information in broker service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "502": description: Connection to the broker service could not be established content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to view database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2850,40 +2831,40 @@ paths: type: integer format: int64 responses: - "404": - description: Failed to find database/table in metadata database + "403": + description: Not the table owner. content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Suggested table semantics successfully - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/EntityDto" "422": description: Ontology does not have rdf or sparql endpoint content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not the table owner. + "400": + description: Failed to parse statistic in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "417": - description: Generated query is malformed + "200": + description: Suggested table semantics successfully + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/EntityDto" + "404": + description: Failed to find database/table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Failed to parse statistic in search service + "417": + description: Generated query is malformed content: application/json: schema: @@ -2918,12 +2899,6 @@ paths: type: integer format: int64 responses: - "404": - description: Failed to find database/table in metadata database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "422": description: Ontology does not have rdf or sparql endpoint content: @@ -2936,6 +2911,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database/table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Suggested table column semantics successfully content: @@ -2962,18 +2943,18 @@ paths: type: integer format: int64 responses: - "200": - description: Found container - content: - application/json: - schema: - $ref: "#/components/schemas/ContainerDto" "404": description: Container image could not be found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found container + content: + application/json: + schema: + $ref: "#/components/schemas/ContainerDto" delete: tags: - container-endpoint @@ -2988,16 +2969,16 @@ paths: type: integer format: int64 responses: - "404": - description: Container not found + "403": + description: "Create container not permitted, need authority `delete-container`" content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" "202": description: Deleted container - "403": - description: "Create container not permitted, need authority `delete-container`" + "404": + description: Container not found content: application/json: schema: @@ -3561,24 +3542,10 @@ components: required: - attributes - id + - password + - username type: object properties: - jdbcMethod: - type: string - example: mariadb - host: - type: string - example: data-db - port: - type: integer - format: int32 - example: 3306 - username: - type: string - example: username - database: - type: string - example: air_quality id: type: string format: uuid @@ -3586,6 +3553,12 @@ components: name: type: string example: Josiah Carberry + username: + type: string + example: username + password: + type: string + example: p4ssw0rd attributes: $ref: "#/components/schemas/UserAttributesDto" last_retrieved: @@ -3905,105 +3878,53 @@ components: display_name: type: string example: XOR - CreatorSaveDto: + IdentifierSaveDto: required: - - creator_name + - creators + - database_id - id + - publication_year + - publisher + - titles + - type type: object properties: id: type: integer format: int64 example: 1 - firstname: - type: string - example: Josiah - lastname: - type: string - example: Carberry - affiliation: - type: string - example: Wesleyan University - creator_name: - type: string - example: "Carberry, Josiah" - name_type: + type: type: string - example: Personal + example: database enum: - - Personal - - Organizational - name_identifier: - type: string - example: 0000-0002-1825-0097 - name_identifier_scheme: - type: string - example: ORCID - enum: - - ORCID - - ROR - - ISNI - - GRID - affiliation_identifier: - type: string - example: https://ror.org/04d836q62 - affiliation_identifier_scheme: - type: string - example: ROR - enum: - - ROR - - GRID - - ISNI - IdentifierFunderSaveDto: - required: - - funder_name - - id - type: object - properties: - id: - type: integer - format: int64 - example: 1 - funder_name: - type: string - example: European Commission - funder_identifier: - type: string - example: http://doi.org/10.13039/501100000780 - funder_identifier_type: - type: string - example: Crossref Funder ID - enum: - - Crossref Funder ID - - ROR - - GND - - ISNI - - Other - scheme_uri: - type: string - example: http://doi.org/ - award_number: - type: string - example: "824087" - award_title: + - database + - subset + - table + - view + doi: type: string - example: EOSC-Life - IdentifierSaveDescriptionDto: - required: - - description - - id - type: object - properties: - id: - type: integer - format: int64 - example: 1 - description: + example: 10.1111/11111111 + titles: + type: array + items: + $ref: "#/components/schemas/SaveIdentifierTitleDto" + descriptions: + type: array + items: + $ref: "#/components/schemas/SaveIdentifierDescriptionDto" + funders: + type: array + items: + $ref: "#/components/schemas/SaveIdentifierFunderDto" + licenses: + type: array + items: + $ref: "#/components/schemas/LicenseDto" + publisher: type: string - example: "Air quality reports at Stephansplatz, Vienna" + example: TU Wien language: type: string - example: en enum: - ab - aa @@ -4189,63 +4110,122 @@ components: - yo - za - zu - type: + creators: + type: array + items: + $ref: "#/components/schemas/SaveIdentifierCreatorDto" + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + view_id: + type: integer + format: int64 + table_id: + type: integer + format: int64 + publication_day: + type: integer + format: int32 + example: 15 + publication_month: + type: integer + format: int32 + example: 12 + publication_year: + type: integer + format: int32 + example: 2022 + related_identifiers: + type: array + items: + $ref: "#/components/schemas/SaveRelatedIdentifierDto" + LicenseDto: + required: + - identifier + - uri + type: object + properties: + identifier: type: string - example: Abstract - enum: - - Abstract - - Methods - - SeriesInformation - - TableOfContents - - TechnicalInfo - - Other - IdentifierSaveDto: + example: MIT + uri: + type: string + example: https://opensource.org/licenses/MIT + description: + type: string + example: "A short and simple permissive license with conditions only requiring\ + \ preservation of copyright and license notices. Licensed works, modifications,\ + \ and larger works may be distributed under different terms and without\ + \ source code." + SaveIdentifierCreatorDto: required: - - creators - - database_id + - creator_name - id - - publication_year - - publisher - - titles - - type type: object properties: id: type: integer format: int64 example: 1 - type: + firstname: type: string - example: database + example: Josiah + lastname: + type: string + example: Carberry + affiliation: + type: string + example: Wesleyan University + creator_name: + type: string + example: "Carberry, Josiah" + name_type: + type: string + example: Personal enum: - - database - - subset - - table - - view - doi: + - Personal + - Organizational + name_identifier: type: string - example: 10.1111/11111111 - titles: - type: array - items: - $ref: "#/components/schemas/IdentifierSaveTitleDto" - descriptions: - type: array - items: - $ref: "#/components/schemas/IdentifierSaveDescriptionDto" - funders: - type: array - items: - $ref: "#/components/schemas/IdentifierFunderSaveDto" - licenses: - type: array - items: - $ref: "#/components/schemas/LicenseDto" - publisher: + example: 0000-0002-1825-0097 + name_identifier_scheme: type: string - example: TU Wien + example: ORCID + enum: + - ORCID + - ROR + - ISNI + - GRID + affiliation_identifier: + type: string + example: https://ror.org/04d836q62 + affiliation_identifier_scheme: + type: string + example: ROR + enum: + - ROR + - GRID + - ISNI + SaveIdentifierDescriptionDto: + required: + - description + - id + type: object + properties: + id: + type: integer + format: int64 + example: 1 + description: + type: string + example: "Air quality reports at Stephansplatz, Vienna" language: type: string + example: en enum: - ab - aa @@ -4431,40 +4411,51 @@ components: - yo - za - zu - creators: - type: array - items: - $ref: "#/components/schemas/CreatorSaveDto" - database_id: + type: + type: string + example: Abstract + enum: + - Abstract + - Methods + - SeriesInformation + - TableOfContents + - TechnicalInfo + - Other + SaveIdentifierFunderDto: + required: + - funder_name + - id + type: object + properties: + id: type: integer format: int64 example: 1 - query_id: - type: integer - format: int64 - view_id: - type: integer - format: int64 - table_id: - type: integer - format: int64 - publication_day: - type: integer - format: int32 - example: 15 - publication_month: - type: integer - format: int32 - example: 12 - publication_year: - type: integer - format: int32 - example: 2022 - related_identifiers: - type: array - items: - $ref: "#/components/schemas/RelatedIdentifierSaveDto" - IdentifierSaveTitleDto: + funder_name: + type: string + example: European Commission + funder_identifier: + type: string + example: http://doi.org/10.13039/501100000780 + funder_identifier_type: + type: string + example: Crossref Funder ID + enum: + - Crossref Funder ID + - ROR + - GND + - ISNI + - Other + scheme_uri: + type: string + example: http://doi.org/ + award_number: + type: string + example: "824087" + award_title: + type: string + example: EOSC-Life + SaveIdentifierTitleDto: required: - id - title @@ -4673,25 +4664,7 @@ components: - Subtitle - TranslatedTitle - Other - LicenseDto: - required: - - identifier - - uri - type: object - properties: - identifier: - type: string - example: MIT - uri: - type: string - example: https://opensource.org/licenses/MIT - description: - type: string - example: "A short and simple permissive license with conditions only requiring\ - \ preservation of copyright and license notices. Licensed works, modifications,\ - \ and larger works may be distributed under different terms and without\ - \ source code." - RelatedIdentifierSaveDto: + SaveRelatedIdentifierDto: required: - id - relation @@ -5471,126 +5444,16 @@ components: is_schema_public: type: boolean example: true - ViewColumnDto: - required: - - database_id - - id - - internal_name - - is_null_allowed - - name - - ord - - type - type: object - properties: - id: - type: integer - format: int64 - example: 12 - name: - maxLength: 64 - minLength: 0 - type: string - example: Given Name - size: - type: integer - format: int64 - example: 255 - d: - type: integer - format: int64 - example: 0 - description: - maxLength: 2048 - minLength: 0 - type: string - example: Column comment - database_id: - type: integer - format: int64 - example: 1 - ord: - type: integer - format: int32 - example: 0 - internal_name: - maxLength: 64 - minLength: 0 - type: string - example: given_name - index_length: - type: integer - format: int64 - example: 255 - length: - type: integer - format: int64 - example: 255 - type: - type: string - example: varchar - enum: - - char - - varchar - - binary - - varbinary - - tinyblob - - tinytext - - text - - blob - - mediumtext - - mediumblob - - longtext - - longblob - - enum - - set - - serial - - bit - - tinyint - - bool - - smallint - - mediumint - - int - - bigint - - float - - double - - decimal - - date - - datetime - - timestamp - - time - - year - is_null_allowed: - type: boolean - example: false - ViewDto: + ViewBriefDto: required: - - columns - database_id - id - - identifiers - internal_name - name - - owner - query - query_hash type: object properties: - jdbcMethod: - type: string - example: mariadb - host: - type: string - example: data-db - port: - type: integer - format: int32 - example: 3306 - username: - type: string - example: username - database: - type: string - example: air_quality id: type: integer format: int64 @@ -5598,22 +5461,9 @@ components: name: type: string example: Air Quality - identifiers: - type: array - items: - $ref: "#/components/schemas/IdentifierDto" query: type: string example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC - owner: - $ref: "#/components/schemas/UserBriefDto" - columns: - type: array - items: - $ref: "#/components/schemas/ViewColumnDto" - last_retrieved: - type: string - format: date-time database_id: type: integer format: int64 @@ -5634,6 +5484,10 @@ components: query_hash: type: string example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 + owned_by: + type: string + format: uuid + example: ac750fcf-ea02-4fce-85ac-d73857e18b35 TableUpdateDto: required: - is_public @@ -5908,7 +5762,7 @@ components: - read - write_own - write_all - SignupRequestDto: + CreateUserDto: required: - email - password @@ -6013,7 +5867,7 @@ components: minimum: 1024 type: integer format: int32 - IdentifierCreateDto: + CreateIdentifierDto: required: - creators - database_id @@ -6037,15 +5891,15 @@ components: titles: type: array items: - $ref: "#/components/schemas/IdentifierSaveTitleDto" + $ref: "#/components/schemas/SaveIdentifierTitleDto" descriptions: type: array items: - $ref: "#/components/schemas/IdentifierSaveDescriptionDto" + $ref: "#/components/schemas/SaveIdentifierDescriptionDto" funders: type: array items: - $ref: "#/components/schemas/IdentifierFunderSaveDto" + $ref: "#/components/schemas/SaveIdentifierFunderDto" licenses: type: array items: @@ -6243,7 +6097,7 @@ components: creators: type: array items: - $ref: "#/components/schemas/CreatorSaveDto" + $ref: "#/components/schemas/SaveIdentifierCreatorDto" database_id: type: integer format: int64 @@ -6272,8 +6126,8 @@ components: related_identifiers: type: array items: - $ref: "#/components/schemas/RelatedIdentifierSaveDto" - DatabaseCreateDto: + $ref: "#/components/schemas/SaveRelatedIdentifierDto" + CreateDatabaseDto: required: - container_id - is_public @@ -6294,7 +6148,7 @@ components: is_schema_public: type: boolean example: true - ViewCreateDto: + CreateViewDto: required: - is_public - is_schema_public @@ -6316,51 +6170,40 @@ components: is_schema_public: type: boolean example: true - ViewBriefDto: + CreateForeignKeyDto: required: - - database_id - - id - - internal_name - - name - - query - - query_hash + - columns + - referenced_columns + - referenced_table type: object properties: - id: - type: integer - format: int64 - example: 4 - name: - type: string - example: Air Quality - query: - type: string - example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC - database_id: - type: integer - format: int64 - example: 1 - internal_name: + columns: + type: array + items: + type: string + referenced_table: type: string - example: air_quality - is_public: - type: boolean - example: true - is_schema_public: - type: boolean - example: true - initial_view: - type: boolean - description: True if it is the default view for the database - example: true - query_hash: + referenced_columns: + type: array + items: + type: string + on_update: type: string - example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 - owned_by: + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + on_delete: type: string - format: uuid - example: ac750fcf-ea02-4fce-85ac-d73857e18b35 - ColumnCreateDto: + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + CreateTableColumnDto: required: - name - null_allowed @@ -6439,7 +6282,7 @@ components: type: string unit_uri: type: string - ConstraintsCreateDto: + CreateTableConstraintsDto: required: - checks - foreign_keys @@ -6454,53 +6297,20 @@ components: items: type: string checks: - uniqueItems: true - type: array - items: - type: string - foreign_keys: - type: array - items: - $ref: "#/components/schemas/ForeignKeyCreateDto" - primary_key: - uniqueItems: true - type: array - items: - type: string - ForeignKeyCreateDto: - required: - - columns - - referenced_columns - - referenced_table - type: object - properties: - columns: - type: array - items: - type: string - referenced_table: - type: string - referenced_columns: - type: array - items: - type: string - on_update: - type: string - enum: - - restrict - - cascade - - set_null - - no_action - - set_default - on_delete: - type: string - enum: - - restrict - - cascade - - set_null - - no_action - - set_default - TableCreateDto: + uniqueItems: true + type: array + items: + type: string + foreign_keys: + type: array + items: + $ref: "#/components/schemas/CreateForeignKeyDto" + primary_key: + uniqueItems: true + type: array + items: + type: string + CreateTableDto: required: - columns - constraints @@ -6522,9 +6332,9 @@ components: columns: type: array items: - $ref: "#/components/schemas/ColumnCreateDto" + $ref: "#/components/schemas/CreateTableColumnDto" constraints: - $ref: "#/components/schemas/ConstraintsCreateDto" + $ref: "#/components/schemas/CreateTableConstraintsDto" is_public: type: boolean example: true @@ -6587,9 +6397,13 @@ components: - quota type: object properties: - jdbcMethod: + id: + type: integer + format: int64 + example: 4 + name: type: string - example: mariadb + example: Air Quality host: type: string example: data-db @@ -6597,19 +6411,6 @@ components: type: integer format: int32 example: 3306 - username: - type: string - example: username - database: - type: string - example: air_quality - id: - type: integer - format: int64 - example: 4 - name: - type: string - example: Air Quality image: $ref: "#/components/schemas/ImageDto" quota: @@ -6620,6 +6421,12 @@ components: type: integer format: int64 example: 10 + username: + type: string + example: username + password: + type: string + example: p4ssw0rd last_retrieved: type: string format: date-time @@ -6929,6 +6736,79 @@ components: type: array items: $ref: "#/components/schemas/PrimaryKeyDto" + DatabaseDto: + required: + - accesses + - contact + - exchange_name + - id + - identifiers + - internal_name + - is_public + - is_schema_public + - name + - owner + - subsets + - tables + - views + type: object + properties: + id: + type: integer + format: int64 + example: 3 + name: + type: string + example: Air Quality + description: + type: string + example: Air Quality + tables: + type: array + items: + $ref: "#/components/schemas/TableDto" + views: + type: array + items: + $ref: "#/components/schemas/ViewDto" + container: + $ref: "#/components/schemas/ContainerDto" + accesses: + type: array + items: + $ref: "#/components/schemas/DatabaseAccessDto" + identifiers: + type: array + items: + $ref: "#/components/schemas/IdentifierDto" + subsets: + type: array + items: + $ref: "#/components/schemas/IdentifierDto" + contact: + $ref: "#/components/schemas/UserBriefDto" + owner: + $ref: "#/components/schemas/UserBriefDto" + last_retrieved: + type: string + format: date-time + exchange_name: + type: string + example: dbrepo + exchange_type: + type: string + example: topic + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + preview_image: + type: string ForeignKeyBriefDto: type: object properties: @@ -7024,22 +6904,6 @@ components: - routing_key type: object properties: - jdbcMethod: - type: string - example: mariadb - host: - type: string - example: data-db - port: - type: integer - format: int32 - example: 3306 - username: - type: string - example: username - database: - type: string - example: air_quality id: type: integer format: int64 @@ -7065,6 +6929,8 @@ components: type: array items: $ref: "#/components/schemas/ColumnDto" + database: + $ref: "#/components/schemas/DatabaseDto" constraints: $ref: "#/components/schemas/ConstraintsDto" last_retrieved: @@ -7135,6 +7001,155 @@ components: type: array items: $ref: "#/components/schemas/ColumnBriefDto" + ViewColumnDto: + required: + - database_id + - id + - internal_name + - is_null_allowed + - name + - ord + - type + type: object + properties: + id: + type: integer + format: int64 + example: 12 + name: + maxLength: 64 + minLength: 0 + type: string + example: Given Name + size: + type: integer + format: int64 + example: 255 + d: + type: integer + format: int64 + example: 0 + description: + maxLength: 2048 + minLength: 0 + type: string + example: Column comment + database_id: + type: integer + format: int64 + example: 1 + ord: + type: integer + format: int32 + example: 0 + internal_name: + maxLength: 64 + minLength: 0 + type: string + example: given_name + index_length: + type: integer + format: int64 + example: 255 + length: + type: integer + format: int64 + example: 255 + type: + type: string + example: varchar + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + is_null_allowed: + type: boolean + example: false + ViewDto: + required: + - columns + - database_id + - id + - identifiers + - internal_name + - name + - owner + - query + - query_hash + type: object + properties: + id: + type: integer + format: int64 + example: 4 + name: + type: string + example: Air Quality + identifiers: + type: array + items: + $ref: "#/components/schemas/IdentifierDto" + query: + type: string + example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC + database: + $ref: "#/components/schemas/DatabaseDto" + owner: + $ref: "#/components/schemas/UserBriefDto" + columns: + type: array + items: + $ref: "#/components/schemas/ViewColumnDto" + last_retrieved: + type: string + format: date-time + database_id: + type: integer + format: int64 + example: 1 + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + initial_view: + type: boolean + description: True if it is the default view for the database + example: true + query_hash: + type: string + example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 TableColumnEntityDto: required: - column_id diff --git a/.docs/.openapi/api.base.yaml b/.docs/.openapi/api.base.yaml index fc0a733ccc..ee32aad2ff 100644 --- a/.docs/.openapi/api.base.yaml +++ b/.docs/.openapi/api.base.yaml @@ -24,7 +24,7 @@ info: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0 title: DBRepo REST API - version: 1.6.1 + version: 1.6.2 openapi: 3.1.0 servers: - description: Test Instance diff --git a/.docs/.openapi/swagger-ui.html b/.docs/.openapi/swagger-ui.html index 0bb08a1c07..e84dd2ca47 100644 --- a/.docs/.openapi/swagger-ui.html +++ b/.docs/.openapi/swagger-ui.html @@ -5,7 +5,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="description" content="DBRepo REST API description in OpenAPI 3.0"/> <title>DBRepo REST API</title> - <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.17.12/swagger-ui.css"/> + <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.18.2/swagger-ui.css"/> <link rel="stylesheet" href="./custom.css"/> <link rel="icon" href="https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/master/.docs/images/logos/favicon.png" /> </head> @@ -17,8 +17,8 @@ </div> </div> <div id="swagger-ui"></div> -<script src="https://unpkg.com/swagger-ui-dist@5.17.12/swagger-ui-bundle.js" crossorigin></script> -<script src="https://unpkg.com/swagger-ui-dist@5.17.12/swagger-ui-standalone-preset.js" crossorigin></script> +<script src="https://unpkg.com/swagger-ui-dist@5.18.2/swagger-ui-bundle.js" crossorigin></script> +<script src="https://unpkg.com/swagger-ui-dist@5.18.2/swagger-ui-standalone-preset.js" crossorigin></script> <script> window.onload = () => { window.ui = SwaggerUIBundle({ diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java index 0beb8a3794..a30208bad0 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java @@ -4,11 +4,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; import lombok.*; import lombok.extern.jackson.Jacksonized; -import java.util.List; - @Getter @Setter @Builder @@ -19,11 +18,8 @@ import java.util.List; @ToString public class CreateUserDto { - @NotNull - @Schema(example = "true") - private Boolean enabled; - @NotBlank + @Pattern(regexp = "^[a-z0-9]{3,}$") @Schema(example = "user") private String username; @@ -32,11 +28,8 @@ public class CreateUserDto { @Schema(example = "user@example.com") private String email; - private String firstName; - - private String lastName; - @NotNull - private List<CredentialDto> credentials; + @ToString.Exclude + private String password; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java deleted file mode 100644 index c9110e041a..0000000000 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java +++ /dev/null @@ -1,35 +0,0 @@ -package at.tuwien.api.auth; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; -import lombok.*; -import lombok.extern.jackson.Jacksonized; - -@Getter -@Setter -@Builder -@EqualsAndHashCode -@NoArgsConstructor -@AllArgsConstructor -@Jacksonized -@ToString -public class SignupRequestDto { - - @NotBlank - @Pattern(regexp = "^[a-z0-9]{3,}$") - @Schema(example = "user") - private String username; - - @NotBlank - @Email - @Schema(example = "user@example.com") - private String email; - - @NotNull - @ToString.Exclude - private String password; - -} diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/CreateDatabaseDto.java similarity index 96% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseCreateDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/CreateDatabaseDto.java index f87673764e..c10d6b0b5e 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/CreateDatabaseDto.java @@ -15,7 +15,7 @@ import lombok.extern.jackson.Jacksonized; @AllArgsConstructor @Jacksonized @ToString -public class DatabaseCreateDto { +public class CreateDatabaseDto { @NotNull @JsonProperty("container_id") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/CreateViewDto.java similarity index 96% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewCreateDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/CreateViewDto.java index 142a751ec4..366845d083 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/ViewCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/CreateViewDto.java @@ -16,7 +16,7 @@ import lombok.extern.jackson.Jacksonized; @AllArgsConstructor @Jacksonized @ToString -public class ViewCreateDto { +public class CreateViewDto { @NotBlank @Size(min = 1, max = 63) diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/CreateTableDto.java similarity index 77% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/CreateTableDto.java index 7c3defcc0b..15a798ee2d 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/CreateTableDto.java @@ -1,7 +1,7 @@ package at.tuwien.api.database.table; -import at.tuwien.api.database.table.columns.ColumnCreateDto; -import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; +import at.tuwien.api.database.table.constraints.CreateTableConstraintsDto; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -20,7 +20,7 @@ import java.util.List; @AllArgsConstructor @Jacksonized @ToString -public class TableCreateDto { +public class CreateTableDto { @NotBlank @Size(min = 1, max = 64) @@ -42,8 +42,8 @@ public class TableCreateDto { private Boolean isSchemaPublic; @NotNull - private List<ColumnCreateDto> columns; + private List<CreateTableColumnDto> columns; @NotNull - private ConstraintsCreateDto constraints; + private CreateTableConstraintsDto constraints; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/CreateTableColumnDto.java similarity index 97% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/CreateTableColumnDto.java index ca9bb10569..ca7f3b8d58 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/ColumnCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/columns/CreateTableColumnDto.java @@ -18,7 +18,7 @@ import java.util.List; @AllArgsConstructor @Jacksonized @ToString -public class ColumnCreateDto { +public class CreateTableColumnDto { @NotBlank @Schema(example = "Date") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/CreateTableConstraintsDto.java similarity index 77% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsCreateDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/CreateTableConstraintsDto.java index 3d35a20b0c..7b223372d2 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/ConstraintsCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/CreateTableConstraintsDto.java @@ -1,6 +1,6 @@ package at.tuwien.api.database.table.constraints; -import at.tuwien.api.database.table.constraints.foreign.ForeignKeyCreateDto; +import at.tuwien.api.database.table.constraints.foreign.CreateForeignKeyDto; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotNull; import lombok.*; @@ -17,14 +17,14 @@ import java.util.Set; @AllArgsConstructor @Jacksonized @ToString -public class ConstraintsCreateDto { +public class CreateTableConstraintsDto { @NotNull private List<List<String>> uniques; @NotNull @JsonProperty("foreign_keys") - private List<ForeignKeyCreateDto> foreignKeys; + private List<CreateForeignKeyDto> foreignKeys; @NotNull private Set<String> checks; diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/CreateForeignKeyDto.java similarity index 95% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyCreateDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/CreateForeignKeyDto.java index 8e2a110997..31e43b0c6a 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/CreateForeignKeyDto.java @@ -14,7 +14,7 @@ import java.util.List; @AllArgsConstructor @Jacksonized @ToString -public class ForeignKeyCreateDto { +public class CreateForeignKeyDto { @NotNull private List<String> columns; diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/TableCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/TableCreateDto.java index 777a870bb9..f8db928e8e 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/TableCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/TableCreateDto.java @@ -1,7 +1,7 @@ package at.tuwien.api.database.table.internal; -import at.tuwien.api.database.table.columns.ColumnCreateDto; -import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; +import at.tuwien.api.database.table.constraints.CreateTableConstraintsDto; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -41,8 +41,8 @@ public class TableCreateDto { private Boolean isSchemaPublic; @NotNull - private List<ColumnCreateDto> columns; + private List<CreateTableColumnDto> columns; @NotNull - private ConstraintsCreateDto constraints; + private CreateTableConstraintsDto constraints; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreateIdentifierDto.java similarity index 84% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreateIdentifierDto.java index 46eb1bbc7d..db55272383 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreateIdentifierDto.java @@ -19,7 +19,7 @@ import java.util.List; @AllArgsConstructor @Jacksonized @ToString -public class IdentifierCreateDto { +public class CreateIdentifierDto { @NotNull @JsonProperty("database_id") @@ -47,11 +47,11 @@ public class IdentifierCreateDto { @NotNull @NotEmpty - private List<IdentifierSaveTitleDto> titles; + private List<SaveIdentifierTitleDto> titles; - private List<IdentifierSaveDescriptionDto> descriptions; + private List<SaveIdentifierDescriptionDto> descriptions; - private List<IdentifierFunderSaveDto> funders; + private List<SaveIdentifierFunderDto> funders; private List<LicenseDto> licenses; @@ -76,9 +76,9 @@ public class IdentifierCreateDto { @NotNull @NotEmpty - private List<CreatorSaveDto> creators; + private List<SaveIdentifierCreatorDto> creators; @JsonProperty("related_identifiers") - private List<RelatedIdentifierSaveDto> relatedIdentifiers; + private List<SaveRelatedIdentifierDto> relatedIdentifiers; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierSaveDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierSaveDto.java index 8591cdc8c2..ed4d445de0 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierSaveDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierSaveDto.java @@ -51,11 +51,11 @@ public class IdentifierSaveDto { @NotNull @NotEmpty - private List<IdentifierSaveTitleDto> titles; + private List<SaveIdentifierTitleDto> titles; - private List<IdentifierSaveDescriptionDto> descriptions; + private List<SaveIdentifierDescriptionDto> descriptions; - private List<IdentifierFunderSaveDto> funders; + private List<SaveIdentifierFunderDto> funders; private List<LicenseDto> licenses; @@ -80,9 +80,9 @@ public class IdentifierSaveDto { @NotNull @NotEmpty - private List<CreatorSaveDto> creators; + private List<SaveIdentifierCreatorDto> creators; @JsonProperty("related_identifiers") - private List<RelatedIdentifierSaveDto> relatedIdentifiers; + private List<SaveRelatedIdentifierDto> relatedIdentifiers; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorSaveDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierCreatorDto.java similarity index 97% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorSaveDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierCreatorDto.java index 86d51e7b4c..c87ff81d3c 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/CreatorSaveDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierCreatorDto.java @@ -15,7 +15,7 @@ import lombok.extern.jackson.Jacksonized; @AllArgsConstructor @Jacksonized @ToString -public class CreatorSaveDto { +public class SaveIdentifierCreatorDto { @NotNull @Schema(example = "1") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierSaveDescriptionDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierDescriptionDto.java similarity index 94% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierSaveDescriptionDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierDescriptionDto.java index 76f4f4b7bc..1d251db634 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierSaveDescriptionDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierDescriptionDto.java @@ -15,7 +15,7 @@ import lombok.extern.jackson.Jacksonized; @AllArgsConstructor @Jacksonized @ToString -public class IdentifierSaveDescriptionDto { +public class SaveIdentifierDescriptionDto { @NotNull @Schema(example = "1") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderSaveDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierFunderDto.java similarity index 96% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderSaveDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierFunderDto.java index 81fd7c91ab..89bea57a1c 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierFunderSaveDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierFunderDto.java @@ -14,7 +14,7 @@ import lombok.extern.jackson.Jacksonized; @AllArgsConstructor @Jacksonized @ToString -public class IdentifierFunderSaveDto { +public class SaveIdentifierFunderDto { @NotNull @Schema(example = "1") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierSaveTitleDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierTitleDto.java similarity index 95% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierSaveTitleDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierTitleDto.java index 9da7e7ec8b..0832a77cee 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/IdentifierSaveTitleDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveIdentifierTitleDto.java @@ -15,7 +15,7 @@ import lombok.extern.jackson.Jacksonized; @AllArgsConstructor @Jacksonized @ToString -public class IdentifierSaveTitleDto { +public class SaveIdentifierTitleDto { @NotNull @Schema(example = "1") diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierSaveDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveRelatedIdentifierDto.java similarity index 93% rename from dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierSaveDto.java rename to dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveRelatedIdentifierDto.java index f72d5b02d2..b3a95eee06 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/RelatedIdentifierSaveDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/identifier/SaveRelatedIdentifierDto.java @@ -13,7 +13,7 @@ import jakarta.validation.constraints.NotNull; @AllArgsConstructor @Jacksonized @ToString -public class RelatedIdentifierSaveDto { +public class SaveRelatedIdentifierDto { @NotNull @Schema(example = "1") 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 bd6b952c17..ac6cacf64f 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,6 +1,6 @@ package at.tuwien.mapper; -import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.container.ContainerBriefDto; import at.tuwien.api.container.ContainerDto; import at.tuwien.api.container.CreateContainerDto; @@ -13,13 +13,13 @@ 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.ColumnBriefDto; -import at.tuwien.api.database.table.columns.ColumnCreateDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; 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.CreateTableConstraintsDto; import at.tuwien.api.database.table.constraints.ConstraintsDto; import at.tuwien.api.database.table.constraints.foreign.ForeignKeyBriefDto; import at.tuwien.api.database.table.constraints.foreign.ForeignKeyDto; @@ -363,19 +363,19 @@ public interface MetadataMapper { .build(); } - Identifier identifierCreateDtoToIdentifier(IdentifierCreateDto data); + Identifier identifierCreateDtoToIdentifier(CreateIdentifierDto data); Identifier identifierUpdateDtoToIdentifier(IdentifierSaveDto data); License licenseDtoToLicense(LicenseDto data); - IdentifierTitle identifierCreateTitleDtoToIdentifierTitle(IdentifierSaveTitleDto data); + IdentifierTitle identifierCreateTitleDtoToIdentifierTitle(SaveIdentifierTitleDto data); - IdentifierDescription identifierCreateDescriptionDtoToIdentifierDescription(IdentifierSaveDescriptionDto data); + IdentifierDescription identifierCreateDescriptionDtoToIdentifierDescription(SaveIdentifierDescriptionDto data); - IdentifierFunder identifierFunderSaveDtoToIdentifierFunder(IdentifierFunderSaveDto data); + IdentifierFunder identifierFunderSaveDtoToIdentifierFunder(SaveIdentifierFunderDto data); - IdentifierSaveDto identifierCreateDtoToIdentifierSaveDto(IdentifierCreateDto data); + IdentifierSaveDto identifierCreateDtoToIdentifierSaveDto(CreateIdentifierDto data); RelatedIdentifierDto relatedIdentifierToRelatedIdentifierDto(RelatedIdentifier data); @@ -389,9 +389,9 @@ public interface MetadataMapper { @Mapping(target = "nameIdentifierSchemeUri", source = "nameIdentifierScheme", qualifiedByName = "nameSchemaMapper"), @Mapping(target = "affiliationIdentifierSchemeUri", source = "affiliationIdentifierScheme", qualifiedByName = "affiliationSchemaMapper"), }) - Creator creatorCreateDtoToCreator(CreatorSaveDto data); + Creator creatorCreateDtoToCreator(SaveIdentifierCreatorDto data); - RelatedIdentifier relatedIdentifierCreateDtoToRelatedIdentifier(RelatedIdentifierSaveDto data); + RelatedIdentifier relatedIdentifierCreateDtoToRelatedIdentifier(SaveRelatedIdentifierDto data); IdentifierType identifierTypeDtoToIdentifierType(IdentifierTypeDto data); @@ -637,7 +637,7 @@ public interface MetadataMapper { ReferenceType referenceTypeDtoToReferenceType(ReferenceTypeDto data); /* keep */ - default Constraints constraintsCreateDtoToConstraints(ConstraintsCreateDto data, Database database, Table table) { + default Constraints constraintsCreateDtoToConstraints(CreateTableConstraintsDto data, Database database, Table table) { final int[] idx = new int[]{0, 0}; final Constraints constrains = Constraints.builder() .checks(data.getChecks()) @@ -744,7 +744,7 @@ public interface MetadataMapper { @Mapping(target = "name", source = "data.name"), @Mapping(target = "internalName", expression = "java(nameToInternalName(data.getName()))"), }) - TableColumn columnCreateDtoToTableColumn(ColumnCreateDto data, ContainerImage image); + TableColumn columnCreateDtoToTableColumn(CreateTableColumnDto data, ContainerImage image); default UpdateCredentialsDto passwordToUpdateCredentialsDto(String password) { return UpdateCredentialsDto.builder() @@ -756,7 +756,7 @@ public interface MetadataMapper { .build(); } - default UserCreateDto signupRequestDtoToUserCreateDto(SignupRequestDto data) { + default UserCreateDto signupRequestDtoToUserCreateDto(CreateUserDto data) { return UserCreateDto.builder() .username(data.getUsername()) .email(data.getEmail()) 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 44d3c10c74..6ee9fa6c9e 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 @@ -153,7 +153,7 @@ public class DatabaseEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<DatabaseBriefDto> create(@Valid @RequestBody DatabaseCreateDto data, + public ResponseEntity<DatabaseBriefDto> create(@Valid @RequestBody CreateDatabaseDto data, @NotNull Principal principal) throws DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, ContainerNotFoundException, SearchServiceException, SearchServiceConnectionException, 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 2830f9714a..b70516fa66 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 @@ -475,7 +475,7 @@ public class IdentifierEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<IdentifierDto> create(@NotNull @Valid @RequestBody IdentifierCreateDto data, + public ResponseEntity<IdentifierDto> create(@NotNull @Valid @RequestBody CreateIdentifierDto data, @NotNull Principal principal) throws DatabaseNotFoundException, UserNotFoundException, NotAllowedException, MalformedException, DataServiceConnectionException, SearchServiceException, DataServiceException, QueryNotFoundException, SearchServiceConnectionException, 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 61ba0d53e7..08535fde69 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 @@ -1,7 +1,7 @@ package at.tuwien.endpoints; import at.tuwien.api.database.table.TableBriefDto; -import at.tuwien.api.database.table.TableCreateDto; +import at.tuwien.api.database.table.CreateTableDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.TableUpdateDto; import at.tuwien.api.database.table.columns.ColumnDto; @@ -359,7 +359,7 @@ public class TableEndpoint extends AbstractEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<TableBriefDto> create(@NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @Valid @RequestBody TableCreateDto data, + @NotNull @Valid @RequestBody CreateTableDto data, @NotNull Principal principal) throws NotAllowedException, MalformedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException, AccessNotFoundException, TableNotFoundException, TableExistsException, SearchServiceException, 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 da979b34f1..ade963c255 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 @@ -2,7 +2,7 @@ package at.tuwien.endpoints; import at.tuwien.api.auth.LoginRequestDto; import at.tuwien.api.auth.RefreshTokenRequestDto; -import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.api.keycloak.TokenDto; import at.tuwien.api.user.UserBriefDto; @@ -139,7 +139,7 @@ public class UserEndpoint extends AbstractEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), }) - public ResponseEntity<UserBriefDto> create(@NotNull @Valid @RequestBody SignupRequestDto data) + public ResponseEntity<UserBriefDto> create(@NotNull @Valid @RequestBody CreateUserDto data) throws UserExistsException, EmailExistsException, AuthServiceException, AuthServiceConnectionException, UserNotFoundException, CredentialsInvalidException { log.debug("endpoint create user, data.username={}", data.getUsername()); @@ -201,7 +201,7 @@ public class UserEndpoint extends AbstractEndpoint { } catch (UserNotFoundException e) { /* need to sync */ log.warn("User with username {} does not exist in metadata database yet", data.getUsername()); - final SignupRequestDto request = SignupRequestDto.builder() + final CreateUserDto request = CreateUserDto.builder() .username(data.getUsername()) .email("noreply@example.com") .password(data.getPassword()) 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 abfbdb5d36..e8b40de8b2 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 @@ -1,7 +1,7 @@ package at.tuwien.endpoints; import at.tuwien.api.database.ViewBriefDto; -import at.tuwien.api.database.ViewCreateDto; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.ViewUpdateDto; import at.tuwien.api.error.ApiErrorDto; @@ -137,7 +137,7 @@ public class ViewEndpoint extends AbstractEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<ViewBriefDto> create(@NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @Valid @RequestBody ViewCreateDto data, + @NotNull @Valid @RequestBody CreateViewDto data, @NotNull Principal principal) throws NotAllowedException, MalformedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException, SearchServiceException, SearchServiceConnectionException { diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java index 1f7c391bd2..3fc9c7216e 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java @@ -1,8 +1,8 @@ package at.tuwien.validation; import at.tuwien.SortType; -import at.tuwien.api.database.table.TableCreateDto; -import at.tuwien.api.database.table.columns.ColumnCreateDto; +import at.tuwien.api.database.table.CreateTableDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; import at.tuwien.api.database.table.columns.ColumnTypeDto; import at.tuwien.api.identifier.IdentifierSaveDto; import at.tuwien.endpoints.AbstractEndpoint; @@ -87,12 +87,12 @@ public class EndpointValidator extends AbstractEndpoint { } } - public void validateColumnCreateConstraints(TableCreateDto data) throws MalformedException { + public void validateColumnCreateConstraints(CreateTableDto data) throws MalformedException { if (data == null) { throw new MalformedException("Validation failed: table data is null"); } /* check size */ - final Optional<ColumnCreateDto> optional0 = data.getColumns() + final Optional<CreateTableColumnDto> optional0 = data.getColumns() .stream() .filter(c -> Objects.isNull(c.getSize())) .filter(c -> NEED_SIZE.contains(c.getType())) @@ -101,7 +101,7 @@ public class EndpointValidator extends AbstractEndpoint { log.error("Validation failed: column {} need size parameter", optional0.get().getName()); throw new MalformedException("Validation failed: column " + optional0.get().getName() + " need size parameter"); } - final Optional<ColumnCreateDto> optional0a = data.getColumns() + final Optional<CreateTableColumnDto> optional0a = data.getColumns() .stream() .filter(c -> !Objects.isNull(c.getSize())) .filter(c -> CAN_HAVE_SIZE.contains(c.getType()) || CAN_HAVE_SIZE_AND_D.contains(c.getType())) @@ -111,7 +111,7 @@ public class EndpointValidator extends AbstractEndpoint { log.error("Validation failed: column {} needs positive size parameter", optional0a.get().getName()); throw new MalformedException("Validation failed: column " + optional0a.get().getName() + " needs positive size parameter"); } - final Optional<ColumnCreateDto> optional0b = data.getColumns() + final Optional<CreateTableColumnDto> optional0b = data.getColumns() .stream() .filter(c -> !Objects.isNull(c.getD())) .filter(c -> CAN_HAVE_SIZE_AND_D.contains(c.getType())) @@ -122,7 +122,7 @@ public class EndpointValidator extends AbstractEndpoint { throw new MalformedException("Validation failed: column " + optional0b.get().getName() + " needs positive d parameter"); } /* check size and d */ - final Optional<ColumnCreateDto> optional1 = data.getColumns() + final Optional<CreateTableColumnDto> optional1 = data.getColumns() .stream() .filter(c -> Objects.isNull(c.getSize()) ^ Objects.isNull(c.getD())) .filter(c -> CAN_HAVE_SIZE_AND_D.contains(c.getType())) @@ -132,7 +132,7 @@ public class EndpointValidator extends AbstractEndpoint { throw new MalformedException("Validation failed: column " + optional1.get().getName() + " either needs both size and d parameter or none (use default)"); } /* check enum */ - final Optional<ColumnCreateDto> optional2 = data.getColumns() + final Optional<CreateTableColumnDto> optional2 = data.getColumns() .stream() .filter(c -> c.getType().equals(ColumnTypeDto.ENUM)) .filter(c -> c.getEnums() == null || c.getEnums().isEmpty()) @@ -142,7 +142,7 @@ public class EndpointValidator extends AbstractEndpoint { throw new MalformedException("Validation failed: column " + optional2.get().getName() + " needs at least 1 allowed enum value"); } /* check set */ - final Optional<ColumnCreateDto> optional3 = data.getColumns() + final Optional<CreateTableColumnDto> optional3 = data.getColumns() .stream() .filter(c -> c.getType().equals(ColumnTypeDto.SET)) .filter(c -> c.getEnums() == null || c.getSets().isEmpty()) @@ -152,7 +152,7 @@ public class EndpointValidator extends AbstractEndpoint { throw new MalformedException("Validation failed: column " + optional3.get().getName() + " needs at least 1 allowed set value"); } /* check serial */ - final List<ColumnCreateDto> list4a = data.getColumns() + final List<CreateTableColumnDto> list4a = data.getColumns() .stream() .filter(c -> c.getType().equals(ColumnTypeDto.SERIAL)) .toList(); @@ -160,16 +160,16 @@ public class EndpointValidator extends AbstractEndpoint { log.error("Validation failed: only one column of type serial allowed"); throw new MalformedException("Validation failed: only one column of type serial allowed"); } - final Optional<ColumnCreateDto> optional4a = data.getColumns() + final Optional<CreateTableColumnDto> optional4a = data.getColumns() .stream() .filter(c -> c.getType().equals(ColumnTypeDto.SERIAL)) - .filter(ColumnCreateDto::getNullAllowed) + .filter(CreateTableColumnDto::getNullAllowed) .findFirst(); if (optional4a.isPresent()) { log.error("Validation failed: column {} type serial demands non-null", optional4a.get().getName()); throw new MalformedException("Validation failed: column " + optional4a.get().getName() + " type serial demands non-null"); } - final Optional<ColumnCreateDto> optional4b = data.getColumns() + final Optional<CreateTableColumnDto> optional4b = data.getColumns() .stream() .filter(c -> c.getType().equals(ColumnTypeDto.SERIAL) && data.getConstraints() .getUniques() diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java index 371e710fae..5118aec776 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java @@ -68,7 +68,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser public void create_anonymous_fails() { - final DatabaseCreateDto request = DatabaseCreateDto.builder() + final CreateDatabaseDto request = CreateDatabaseDto.builder() .cid(CONTAINER_1_ID) .name(DATABASE_1_NAME) .isPublic(DATABASE_1_PUBLIC) @@ -83,7 +83,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_4_USERNAME) public void create_noRole_fails() { - final DatabaseCreateDto request = DatabaseCreateDto.builder() + final CreateDatabaseDto request = CreateDatabaseDto.builder() .cid(CONTAINER_3_ID) .name(DATABASE_3_NAME) .isPublic(DATABASE_3_PUBLIC) @@ -101,7 +101,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { DatabaseNotFoundException, ContainerNotFoundException, SearchServiceException, SearchServiceConnectionException, AuthServiceException, AuthServiceConnectionException, BrokerServiceException, BrokerServiceConnectionException, ContainerQuotaException { - final DatabaseCreateDto request = DatabaseCreateDto.builder() + final CreateDatabaseDto request = CreateDatabaseDto.builder() .cid(CONTAINER_1_ID) .name(DATABASE_1_NAME) .isPublic(DATABASE_1_PUBLIC) @@ -124,7 +124,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"create-database"}) public void create_quotaExceeded_fails() throws UserNotFoundException, ContainerNotFoundException { - final DatabaseCreateDto request = DatabaseCreateDto.builder() + final CreateDatabaseDto request = CreateDatabaseDto.builder() .cid(CONTAINER_4_ID) .name(DATABASE_1_NAME) .isPublic(DATABASE_1_PUBLIC) @@ -669,7 +669,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { assertEquals(expectedSize, body.size()); } - public void create_generic(DatabaseCreateDto data, Principal principal, User user) throws DataServiceException, + public void create_generic(CreateDatabaseDto data, Principal principal, User user) throws DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, ContainerNotFoundException, SearchServiceException, SearchServiceConnectionException, BrokerServiceException, BrokerServiceConnectionException, ContainerQuotaException { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java index 9cc0c4cc8b..a17d31649e 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java @@ -1,14 +1,14 @@ package at.tuwien.endpoints; import at.tuwien.api.database.table.TableBriefDto; -import at.tuwien.api.database.table.TableCreateDto; +import at.tuwien.api.database.table.CreateTableDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.TableUpdateDto; -import at.tuwien.api.database.table.columns.ColumnCreateDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; import at.tuwien.api.database.table.columns.ColumnDto; import at.tuwien.api.database.table.columns.ColumnTypeDto; import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto; -import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; +import at.tuwien.api.database.table.constraints.CreateTableConstraintsDto; import at.tuwien.api.semantics.EntityDto; import at.tuwien.api.semantics.TableColumnEntityDto; import at.tuwien.entities.database.Database; @@ -203,10 +203,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicDecimalColumnSizeTooSmall_fails() { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(ColumnTypeDto.DECIMAL) .size(-1L) // <<< @@ -224,10 +224,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicDecimalColumnDTooSmall_fails() { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(ColumnTypeDto.DECIMAL) .size(0L) @@ -247,10 +247,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicOptionalSizeNone_succeeds(ColumnTypeDto columnType) throws UserNotFoundException, SearchServiceException, NotAllowedException, SemanticEntityNotFoundException, DataServiceConnectionException, TableNotFoundException, MalformedException, DataServiceException, DatabaseNotFoundException, AccessNotFoundException, OntologyNotFoundException, TableExistsException, SearchServiceConnectionException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(columnType) .size(null) // <<< @@ -278,10 +278,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicOptionalSize_succeeds(ColumnTypeDto columnType) throws UserNotFoundException, SearchServiceException, NotAllowedException, SemanticEntityNotFoundException, DataServiceConnectionException, TableNotFoundException, MalformedException, DataServiceException, DatabaseNotFoundException, AccessNotFoundException, OntologyNotFoundException, TableExistsException, SearchServiceConnectionException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(columnType) .size(40L) @@ -303,15 +303,15 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicNeedNothing_succeeds(ColumnTypeDto columnType) throws UserNotFoundException, SearchServiceException, NotAllowedException, SemanticEntityNotFoundException, DataServiceConnectionException, TableNotFoundException, MalformedException, DataServiceException, DatabaseNotFoundException, AccessNotFoundException, OntologyNotFoundException, TableExistsException, SearchServiceConnectionException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(columnType) .nullAllowed(false) .build())) - .constraints(ConstraintsCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() .uniques(List.of(List.of("ID"))) .build()) .build(); @@ -329,10 +329,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicNeedSize_succeeds(ColumnTypeDto columnType) throws UserNotFoundException, SearchServiceException, NotAllowedException, SemanticEntityNotFoundException, DataServiceConnectionException, TableNotFoundException, MalformedException, DataServiceException, DatabaseNotFoundException, AccessNotFoundException, OntologyNotFoundException, TableExistsException, SearchServiceConnectionException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(columnType) .size(40L) @@ -353,10 +353,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @MethodSource("needSize_parameters") @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicNeedSizeNone_fails(ColumnTypeDto columnType) { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(columnType) .size(null) // <<< @@ -375,10 +375,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @MethodSource("canHaveSizeAndD_parameters") @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicCanHaveSizeAndDSizeNone_fails(ColumnTypeDto columnType) { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(columnType) .size(null) // <<< @@ -397,10 +397,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @MethodSource("canHaveSizeAndD_parameters") @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicCanHaveSizeAndDDNone_fails(ColumnTypeDto columnType) { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(columnType) .size(0L) @@ -423,10 +423,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { DataServiceConnectionException, TableNotFoundException, MalformedException, DataServiceException, DatabaseNotFoundException, AccessNotFoundException, OntologyNotFoundException, TableExistsException, SearchServiceConnectionException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(columnType) .size(null) // <<< @@ -446,20 +446,20 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicHasMultipleSerial_fails() { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(ColumnTypeDto.SERIAL) .nullAllowed(false) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Counter") .type(ColumnTypeDto.SERIAL) .nullAllowed(false) .build())) - .constraints(ConstraintsCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() .uniques(List.of(List.of("ID"), List.of("Counter"))) .build()) @@ -474,15 +474,15 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) public void create_publicSerialNullAllowed_fails() { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(ColumnTypeDto.SERIAL) .nullAllowed(true) // <<< .build())) - .constraints(ConstraintsCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() .uniques(List.of(List.of("ID"))) .build()) .build(); @@ -501,10 +501,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { DataServiceConnectionException, TableNotFoundException, MalformedException, DataServiceException, DatabaseNotFoundException, AccessNotFoundException, OntologyNotFoundException, TableExistsException, SearchServiceConnectionException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("Some Table") .description("Some Description") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("ID") .type(columnType) .size(0L) // <<< @@ -1147,7 +1147,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { return tableEndpoint.list(databaseId, principal); } - protected ResponseEntity<TableBriefDto> generic_create(Long databaseId, Database database, TableCreateDto data, + protected ResponseEntity<TableBriefDto> generic_create(Long databaseId, Database database, CreateTableDto data, Principal principal, User user, DatabaseAccess access) throws MalformedException, NotAllowedException, DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, AccessNotFoundException, TableNotFoundException, diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java index 8b653f9356..152c17c461 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java @@ -1,7 +1,7 @@ package at.tuwien.endpoints; import at.tuwien.api.auth.LoginRequestDto; -import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.keycloak.UserAttributesDto; import at.tuwien.api.user.UserBriefDto; import at.tuwien.api.user.UserDto; @@ -107,7 +107,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void create_anonymous_succeeds() throws UserExistsException, EmailExistsException, UserNotFoundException, AuthServiceException, AuthServiceConnectionException, CredentialsInvalidException { - final SignupRequestDto request = SignupRequestDto.builder() + final CreateUserDto request = CreateUserDto.builder() .email(USER_1_EMAIL) .username(USER_1_USERNAME) .password(USER_1_PASSWORD) @@ -120,7 +120,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME) public void create_isAuthenticated_fails() { - final SignupRequestDto request = SignupRequestDto.builder() + final CreateUserDto request = CreateUserDto.builder() .email(USER_2_EMAIL) .username(USER_2_USERNAME) .password(USER_2_PASSWORD) @@ -312,7 +312,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest { /* mock */ when(authenticationService.findByUsername(USER_1_USERNAME)) .thenReturn(USER_1_KEYCLOAK_DTO); - when(userService.create(any(SignupRequestDto.class), any(UUID.class))) + when(userService.create(any(CreateUserDto.class), any(UUID.class))) .thenReturn(USER_1); /* test */ @@ -445,7 +445,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest { return response.getBody(); } - protected void create_generic(SignupRequestDto data, User user, at.tuwien.api.keycloak.UserDto userDto, UUID id) + protected void create_generic(CreateUserDto data, User user, at.tuwien.api.keycloak.UserDto userDto, UUID id) throws UserExistsException, EmailExistsException, UserNotFoundException, AuthServiceException, AuthServiceConnectionException, CredentialsInvalidException { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java index 4cd9296425..4f1a3da45c 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java @@ -1,7 +1,7 @@ package at.tuwien.endpoints; import at.tuwien.api.database.ViewBriefDto; -import at.tuwien.api.database.ViewCreateDto; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.ViewUpdateDto; import at.tuwien.entities.database.Database; @@ -461,7 +461,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { DatabaseAccess access) throws MalformedException, DataServiceException, DataServiceConnectionException, NotAllowedException, UserNotFoundException, DatabaseNotFoundException, AccessNotFoundException, SearchServiceException, SearchServiceConnectionException { - final ViewCreateDto request = ViewCreateDto.builder() + final CreateViewDto request = CreateViewDto.builder() .name(VIEW_1_NAME) .query(VIEW_1_QUERY) .isPublic(VIEW_1_PUBLIC) diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServicePersistenceTest.java index 5250f7e106..b0efccab9a 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServicePersistenceTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServicePersistenceTest.java @@ -1,9 +1,9 @@ package at.tuwien.service; -import at.tuwien.api.database.table.TableCreateDto; -import at.tuwien.api.database.table.columns.ColumnCreateDto; +import at.tuwien.api.database.table.CreateTableDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; import at.tuwien.api.database.table.columns.ColumnTypeDto; -import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; +import at.tuwien.api.database.table.constraints.CreateTableConstraintsDto; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.table.Table; import at.tuwien.entities.database.table.columns.TableColumn; @@ -82,22 +82,22 @@ public class TableServicePersistenceTest extends AbstractUnitTest { @Transactional public void create_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException, UserNotFoundException, TableNotFoundException, DatabaseNotFoundException, TableExistsException, SearchServiceException, SearchServiceConnectionException, OntologyNotFoundException, SemanticEntityNotFoundException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("New Table") .description("A wonderful table") .isPublic(true) .isSchemaPublic(true) - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("id") .nullAllowed(false) .type(ColumnTypeDto.BIGINT) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("date") .nullAllowed(true) .type(ColumnTypeDto.DATE) .build())) - .constraints(ConstraintsCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() .checks(Set.of()) .uniques(List.of(List.of("date"))) .foreignKeys(List.of()) 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 5fb8e9ad7e..8cb5081f6d 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 @@ -1,13 +1,13 @@ package at.tuwien.service; -import at.tuwien.api.database.table.TableCreateDto; +import at.tuwien.api.database.table.CreateTableDto; import at.tuwien.api.database.table.TableStatisticDto; -import at.tuwien.api.database.table.columns.ColumnCreateDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; import at.tuwien.api.database.table.columns.ColumnStatisticDto; import at.tuwien.api.database.table.columns.ColumnTypeDto; import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto; -import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; -import at.tuwien.api.database.table.constraints.foreign.ForeignKeyCreateDto; +import at.tuwien.api.database.table.constraints.CreateTableConstraintsDto; +import at.tuwien.api.database.table.constraints.foreign.CreateForeignKeyDto; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.table.Table; import at.tuwien.entities.database.table.columns.TableColumn; @@ -275,7 +275,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { .thenReturn(USER_1); doNothing() .when(dataServiceGateway) - .createTable(eq(DATABASE_1_ID), any(TableCreateDto.class)); + .createTable(eq(DATABASE_1_ID), any(CreateTableDto.class)); when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) @@ -291,15 +291,15 @@ public class TableServiceUnitTest extends AbstractUnitTest { DataServiceConnectionException, UserNotFoundException, TableNotFoundException, DatabaseNotFoundException, TableExistsException, SearchServiceException, SearchServiceConnectionException, MalformedException, OntologyNotFoundException, SemanticEntityNotFoundException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("New Table") .description("A wonderful table") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("I Am Späshül") .nullAllowed(true) .type(ColumnTypeDto.TEXT) .build())) - .constraints(ConstraintsCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() .checks(Set.of()) .uniques(List.of(List.of("I Am Späshül"))) .foreignKeys(List.of()) @@ -312,7 +312,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { .thenReturn(USER_1); doNothing() .when(dataServiceGateway) - .createTable(eq(DATABASE_1_ID), any(TableCreateDto.class)); + .createTable(eq(DATABASE_1_ID), any(CreateTableDto.class)); when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) @@ -344,15 +344,15 @@ public class TableServiceUnitTest extends AbstractUnitTest { public void createTable_dateFormatNotFound_fails() throws DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, TableExistsException, SearchServiceException, SearchServiceConnectionException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name("New Table") .description("A wonderful table") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("date") .nullAllowed(true) .type(ColumnTypeDto.DATE) .build())) - .constraints(ConstraintsCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() .checks(Set.of()) .uniques(List.of(List.of("date"))) .foreignKeys(List.of()) @@ -365,7 +365,7 @@ public class TableServiceUnitTest extends AbstractUnitTest { .thenReturn(USER_1); doNothing() .when(dataServiceGateway) - .createTable(eq(DATABASE_1_ID), any(TableCreateDto.class)); + .createTable(eq(DATABASE_1_ID), any(CreateTableDto.class)); when(databaseRepository.save(any(Database.class))) .thenReturn(DATABASE_1); when(searchServiceGateway.update(any(Database.class))) @@ -423,11 +423,11 @@ public class TableServiceUnitTest extends AbstractUnitTest { @Test public void createTable_primaryKeyMalformed_fails() throws UserNotFoundException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name(TABLE_5_NAME) .description(TABLE_5_DESCRIPTION) .columns(TABLE_5_COLUMNS_CREATE) - .constraints(ConstraintsCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() .foreignKeys(new LinkedList<>()) .checks(new LinkedHashSet<>()) .primaryKey(Set.of("i_do_not_exist")) @@ -447,11 +447,11 @@ public class TableServiceUnitTest extends AbstractUnitTest { @Test public void createTable_uniquesMalformed_fails() throws UserNotFoundException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name(TABLE_5_NAME) .description(TABLE_5_DESCRIPTION) .columns(TABLE_5_COLUMNS_CREATE) - .constraints(ConstraintsCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() .foreignKeys(new LinkedList<>()) .checks(new LinkedHashSet<>()) .primaryKey(new LinkedHashSet<>()) @@ -473,12 +473,12 @@ public class TableServiceUnitTest extends AbstractUnitTest { @Test public void createTable_foreignKeyMalformed_fails() throws UserNotFoundException { - final TableCreateDto request = TableCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() .name(TABLE_5_NAME) .description(TABLE_5_DESCRIPTION) .columns(TABLE_5_COLUMNS_CREATE) - .constraints(ConstraintsCreateDto.builder() - .foreignKeys(List.of(ForeignKeyCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() + .foreignKeys(List.of(CreateForeignKeyDto.builder() .columns(List.of("some_column")) .referencedColumns(List.of("some_foreign_column")) .referencedTable("some_referenced_table") diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java index 09a372a6eb..8724e08be1 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java @@ -1,6 +1,6 @@ package at.tuwien.service; -import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.user.UserPasswordDto; import at.tuwien.api.user.UserUpdateDto; import at.tuwien.entities.user.User; @@ -69,7 +69,7 @@ public class UserServicePersistenceTest extends AbstractUnitTest { @Test public void create_succeeds() throws UserExistsException, UserNotFoundException, EmailExistsException { - final SignupRequestDto request = SignupRequestDto.builder() + final CreateUserDto request = CreateUserDto.builder() .username(USER_2_USERNAME) .password(USER_2_PASSWORD) .email(USER_2_EMAIL) @@ -109,7 +109,7 @@ public class UserServicePersistenceTest extends AbstractUnitTest { .build(); /* mock */ - final User user = userService.create(SignupRequestDto.builder() + final User user = userService.create(CreateUserDto.builder() .username(USER_3_USERNAME) .password(USER_3_PASSWORD) .email(USER_3_EMAIL) diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceUnitTest.java index c63d207e6e..4ba217a60c 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ViewServiceUnitTest.java @@ -2,7 +2,7 @@ package at.tuwien.service; import at.tuwien.repository.DatabaseRepository; import at.tuwien.test.AbstractUnitTest; -import at.tuwien.api.database.ViewCreateDto; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.View; import at.tuwien.exception.*; @@ -50,7 +50,7 @@ public class ViewServiceUnitTest extends AbstractUnitTest { @Test public void create_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException { - final ViewCreateDto request = ViewCreateDto.builder() + final CreateViewDto request = CreateViewDto.builder() .name(VIEW_1_NAME) .query(VIEW_1_QUERY) .isPublic(VIEW_1_PUBLIC) diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java index 342a9e328e..fe650589ad 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java @@ -1,8 +1,8 @@ package at.tuwien.validator; import at.tuwien.SortType; -import at.tuwien.api.database.table.TableCreateDto; -import at.tuwien.api.database.table.columns.ColumnCreateDto; +import at.tuwien.api.database.table.CreateTableDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; import at.tuwien.api.database.table.columns.ColumnTypeDto; import at.tuwien.api.identifier.IdentifierSaveDto; import at.tuwien.entities.database.Database; @@ -264,8 +264,8 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest { @ParameterizedTest @MethodSource("needSize_parameters") public void validateColumnCreateConstraints_needSize_fails(ColumnTypeDto type) { - final TableCreateDto request = TableCreateDto.builder() - .columns(List.of(ColumnCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .type(type) .size(null) // <<<<<< .build())) @@ -279,8 +279,8 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest { @Test public void validateColumnCreateConstraints_needEnum_fails() { - final TableCreateDto request = TableCreateDto.builder() - .columns(List.of(ColumnCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .type(ColumnTypeDto.ENUM) .enums(null) // <<<<<<< .build())) @@ -294,8 +294,8 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest { @Test public void validateColumnCreateConstraints_needSet_fails() { - final TableCreateDto request = TableCreateDto.builder() - .columns(List.of(ColumnCreateDto.builder() + final CreateTableDto request = CreateTableDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .type(ColumnTypeDto.SET) .sets(null) // <<<<<<< .build())) diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java index 976830cebb..4ee76c36bf 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java @@ -3,11 +3,11 @@ package at.tuwien.gateway; import at.tuwien.ExportResourceDto; import at.tuwien.api.database.AccessTypeDto; import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.database.ViewCreateDto; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.internal.CreateDatabaseDto; import at.tuwien.api.database.query.QueryDto; -import at.tuwien.api.database.table.TableCreateDto; +import at.tuwien.api.database.table.CreateTableDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.TableStatisticDto; import at.tuwien.api.database.table.TableUpdateDto; @@ -94,7 +94,7 @@ public interface DataServiceGateway { * @throws DatabaseNotFoundException Some of the privileged parameters of the given database were not provided by the metadata service. * @throws TableExistsException A table with this internal name exists already in the database. */ - void createTable(Long databaseId, TableCreateDto data) throws DataServiceConnectionException, DataServiceException, + void createTable(Long databaseId, CreateTableDto data) throws DataServiceConnectionException, DataServiceException, DatabaseNotFoundException, TableExistsException; /** @@ -118,7 +118,7 @@ public interface DataServiceGateway { * @throws DataServiceConnectionException The connection to the data service could not be established. * @throws DataServiceException The data service responded unexpectedly. */ - ViewDto createView(Long databaseId, ViewCreateDto data) throws DataServiceConnectionException, DataServiceException; + ViewDto createView(Long databaseId, CreateViewDto data) throws DataServiceConnectionException, DataServiceException; /** * Deletes a given view in the given database. diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java index 191f42fea3..6ee2ef084b 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java @@ -4,7 +4,7 @@ import at.tuwien.ExportResourceDto; import at.tuwien.api.database.*; import at.tuwien.api.database.internal.CreateDatabaseDto; import at.tuwien.api.database.query.QueryDto; -import at.tuwien.api.database.table.TableCreateDto; +import at.tuwien.api.database.table.CreateTableDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.TableStatisticDto; import at.tuwien.api.database.table.TableUpdateDto; @@ -188,7 +188,7 @@ public class DataServiceGatewayImpl implements DataServiceGateway { } @Override - public void createTable(Long databaseId, TableCreateDto data) throws DataServiceConnectionException, DataServiceException, + public void createTable(Long databaseId, CreateTableDto data) throws DataServiceConnectionException, DataServiceException, DatabaseNotFoundException, TableExistsException { final ResponseEntity<Void> response; final String path = "/api/database/" + databaseId + "/table"; @@ -239,7 +239,7 @@ public class DataServiceGatewayImpl implements DataServiceGateway { } @Override - public ViewDto createView(Long databaseId, ViewCreateDto data) throws DataServiceConnectionException, DataServiceException { + public ViewDto createView(Long databaseId, CreateViewDto data) throws DataServiceConnectionException, DataServiceException { final ResponseEntity<ViewDto> response; final String path = "/api/database/" + databaseId + "/view"; log.trace("create view at endpoint {} with path {}", gatewayConfig.getDataEndpoint(), path); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java index eb378290aa..a288d1d6e0 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java @@ -1,7 +1,7 @@ package at.tuwien.service; import at.tuwien.api.auth.LoginRequestDto; -import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.keycloak.TokenDto; import at.tuwien.api.keycloak.UserDto; import at.tuwien.api.user.UserPasswordDto; @@ -22,7 +22,7 @@ public interface AuthenticationService { * @throws AuthServiceConnectionException The connection with the auth service could not be established. * @throws EmailExistsException The user email already exists in the metadata database. */ - UserDto create(SignupRequestDto data) throws UserExistsException, AuthServiceException, AuthServiceConnectionException, + UserDto create(CreateUserDto data) throws UserExistsException, AuthServiceException, AuthServiceConnectionException, EmailExistsException, CredentialsInvalidException; /** diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java index 3000ece11e..4e3765fd6e 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/DatabaseService.java @@ -1,6 +1,6 @@ package at.tuwien.service; -import at.tuwien.api.database.DatabaseCreateDto; +import at.tuwien.api.database.CreateDatabaseDto; import at.tuwien.api.database.DatabaseModifyVisibilityDto; import at.tuwien.entities.container.Container; import at.tuwien.entities.database.Database; @@ -68,7 +68,7 @@ public interface DatabaseService { * @throws DataServiceException If the data service returned non-successfully. * @throws DataServiceConnectionException If failing to connect to the data service/search service. */ - Database create(Container container, DatabaseCreateDto createDto, User user, List<User> internalUsers) throws UserNotFoundException, + Database create(Container container, CreateDatabaseDto createDto, User user, List<User> internalUsers) throws UserNotFoundException, ContainerNotFoundException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException; diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java index ded8204f32..47183700f9 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java @@ -1,7 +1,7 @@ package at.tuwien.service; import at.tuwien.api.identifier.BibliographyTypeDto; -import at.tuwien.api.identifier.IdentifierCreateDto; +import at.tuwien.api.identifier.CreateIdentifierDto; import at.tuwien.api.identifier.IdentifierSaveDto; import at.tuwien.api.identifier.IdentifierTypeDto; import at.tuwien.entities.database.Database; @@ -139,7 +139,7 @@ public interface IdentifierService { * @throws SearchServiceException * @throws SearchServiceConnectionException */ - Identifier create(Database database, User user, IdentifierCreateDto data) throws DataServiceException, + Identifier create(Database database, User user, CreateIdentifierDto data) throws DataServiceException, DataServiceConnectionException, IdentifierNotFoundException, MalformedException, ViewNotFoundException, DatabaseNotFoundException, QueryNotFoundException, SearchServiceException, SearchServiceConnectionException, ExternalServiceException; diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/TableService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/TableService.java index 299283e68b..c0880c07dc 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/TableService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/TableService.java @@ -1,6 +1,6 @@ package at.tuwien.service; -import at.tuwien.api.database.table.TableCreateDto; +import at.tuwien.api.database.table.CreateTableDto; import at.tuwien.api.database.table.TableUpdateDto; import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto; import at.tuwien.entities.database.Database; @@ -39,7 +39,7 @@ public interface TableService { * @param principal The principal. * @return The created table. */ - Table createTable(Database database, TableCreateDto createDto, Principal principal) + Table createTable(Database database, CreateTableDto createDto, Principal principal) throws TableNotFoundException, DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, TableExistsException, SearchServiceException, SearchServiceConnectionException, MalformedException, OntologyNotFoundException, SemanticEntityNotFoundException; diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java index 6416da9b80..c2f57c4e53 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java @@ -1,6 +1,6 @@ package at.tuwien.service; -import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.user.UserPasswordDto; import at.tuwien.api.user.UserUpdateDto; import at.tuwien.entities.user.User; @@ -47,7 +47,7 @@ public interface UserService { * @param id The user id. * @return The user, if successful. */ - User create(SignupRequestDto data, UUID id); + User create(CreateUserDto data, UUID id); /** * Updates the user information for a user with given id in the metadata database. diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/ViewService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/ViewService.java index 9ec30b74f2..4d183f1cc4 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/ViewService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/ViewService.java @@ -1,12 +1,11 @@ package at.tuwien.service; -import at.tuwien.api.database.ViewCreateDto; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.api.database.ViewUpdateDto; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.View; import at.tuwien.entities.user.User; import at.tuwien.exception.*; -import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -53,7 +52,7 @@ public interface ViewService { * @throws SearchServiceConnectionException * @throws ViewNotFoundException */ - View create(Database database, User user, ViewCreateDto data) throws MalformedException, DataServiceException, + View create(Database database, User user, CreateViewDto data) throws MalformedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException; 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 52aa504889..24ebeb1665 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 @@ -1,7 +1,7 @@ package at.tuwien.service.impl; import at.tuwien.api.auth.LoginRequestDto; -import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.keycloak.TokenDto; import at.tuwien.api.keycloak.UserDto; import at.tuwien.api.user.UserPasswordDto; @@ -30,7 +30,7 @@ public class AuthenticationServiceImpl implements AuthenticationService { } @Override - public UserDto create(SignupRequestDto data) throws UserExistsException, AuthServiceException, + public UserDto create(CreateUserDto data) throws UserExistsException, AuthServiceException, AuthServiceConnectionException, EmailExistsException, CredentialsInvalidException { keycloakGateway.createUser(metadataMapper.signupRequestDtoToUserCreateDto(data)); try { 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 0151ea92f7..b4e42a67e1 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 @@ -6,7 +6,7 @@ import at.tuwien.api.datacite.doi.DataCiteCreateDoi; import at.tuwien.api.datacite.doi.DataCiteDoi; import at.tuwien.api.datacite.doi.DataCiteDoiEvent; import at.tuwien.api.identifier.BibliographyTypeDto; -import at.tuwien.api.identifier.IdentifierCreateDto; +import at.tuwien.api.identifier.CreateIdentifierDto; import at.tuwien.api.identifier.IdentifierSaveDto; import at.tuwien.api.identifier.IdentifierTypeDto; import at.tuwien.config.DataCiteConfig; @@ -105,7 +105,7 @@ public class DataCiteIdentifierServiceImpl implements IdentifierService { @Override @Transactional(rollbackFor = {Exception.class}) - public Identifier create(Database database, User user, IdentifierCreateDto data) throws DataServiceException, + public Identifier create(Database database, User user, CreateIdentifierDto data) throws DataServiceException, DataServiceConnectionException, IdentifierNotFoundException, MalformedException, ViewNotFoundException, DatabaseNotFoundException, QueryNotFoundException, SearchServiceException, SearchServiceConnectionException, ExternalServiceException { 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 7415ded56a..17ba0bd60b 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 @@ -1,10 +1,9 @@ package at.tuwien.service.impl; -import at.tuwien.api.database.DatabaseCreateDto; +import at.tuwien.api.database.CreateDatabaseDto; import at.tuwien.api.database.DatabaseDto; 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.user.internal.UpdateUserPasswordDto; import at.tuwien.entities.container.Container; @@ -96,7 +95,7 @@ public class DatabaseServiceImpl implements DatabaseService { @Override @Transactional - public Database create(Container container, DatabaseCreateDto data, User user, List<User> internalUsers) + public Database create(Container container, CreateDatabaseDto data, User user, List<User> internalUsers) throws UserNotFoundException, ContainerNotFoundException, DataServiceException, SearchServiceException, DataServiceConnectionException, DatabaseNotFoundException, SearchServiceConnectionException { final Database entity = Database.builder() @@ -116,7 +115,7 @@ public class DatabaseServiceImpl implements DatabaseService { .identifiers(new LinkedList<>()) .build(); /* create in data database */ - final CreateDatabaseDto payload = CreateDatabaseDto.builder() + final at.tuwien.api.database.internal.CreateDatabaseDto payload = at.tuwien.api.database.internal.CreateDatabaseDto.builder() .containerId(data.getCid()) .userId(user.getId()) .username(user.getUsername()) 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 39e4824706..932a1a598c 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 @@ -3,7 +3,7 @@ package at.tuwien.service.impl; import at.tuwien.ExportResourceDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.identifier.BibliographyTypeDto; -import at.tuwien.api.identifier.IdentifierCreateDto; +import at.tuwien.api.identifier.CreateIdentifierDto; import at.tuwien.api.identifier.IdentifierSaveDto; import at.tuwien.api.identifier.IdentifierTypeDto; import at.tuwien.config.MetadataConfig; @@ -228,7 +228,7 @@ public class IdentifierServiceImpl implements IdentifierService { @Override @Transactional - public Identifier create(Database database, User user, IdentifierCreateDto data) throws SearchServiceException, + public Identifier create(Database database, User user, CreateIdentifierDto data) throws SearchServiceException, DataServiceException, QueryNotFoundException, DataServiceConnectionException, DatabaseNotFoundException, SearchServiceConnectionException, IdentifierNotFoundException, ViewNotFoundException { final Identifier identifier = metadataMapper.identifierCreateDtoToIdentifier(data); 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 52a9a63667..da92fb7ef5 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 @@ -1,9 +1,9 @@ package at.tuwien.service.impl; -import at.tuwien.api.database.table.TableCreateDto; +import at.tuwien.api.database.table.CreateTableDto; import at.tuwien.api.database.table.TableStatisticDto; import at.tuwien.api.database.table.TableUpdateDto; -import at.tuwien.api.database.table.columns.ColumnCreateDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; import at.tuwien.api.database.table.columns.ColumnStatisticDto; import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto; import at.tuwien.config.RabbitConfig; @@ -90,7 +90,7 @@ public class TableServiceImpl implements TableService { @Override @Transactional - public Table createTable(Database database, TableCreateDto data, Principal principal) throws DataServiceException, + public Table createTable(Database database, CreateTableDto data, Principal principal) throws DataServiceException, DataServiceConnectionException, UserNotFoundException, TableNotFoundException, DatabaseNotFoundException, TableExistsException, SearchServiceException, SearchServiceConnectionException, MalformedException, OntologyNotFoundException, SemanticEntityNotFoundException { @@ -117,7 +117,7 @@ public class TableServiceImpl implements TableService { /* set the ordinal position for the columns */ final int[] idx = new int[]{0}; for (int i = 0; i < data.getColumns().size(); i++) { - final ColumnCreateDto c = data.getColumns().get(i); + final CreateTableColumnDto c = data.getColumns().get(i); final TableColumn column = metadataMapper.columnCreateDtoToTableColumn(c, database.getContainer().getImage()); column.setOrdinalPosition(idx[0]++); column.setTable(table); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java index 6f9f43aeda..042684f8c9 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java @@ -1,6 +1,6 @@ package at.tuwien.service.impl; -import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.user.UserPasswordDto; import at.tuwien.api.user.UserUpdateDto; import at.tuwien.config.KeycloakConfig; @@ -64,7 +64,7 @@ public class UserServiceImpl implements UserService { } @Override - public User create(SignupRequestDto data, UUID id) { + public User create(CreateUserDto data, UUID id) { /* create at authentication service */ final User entity = User.builder() .id(id) 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 b0a8f01796..8ca688b1ed 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 @@ -1,6 +1,6 @@ package at.tuwien.service.impl; -import at.tuwien.api.database.ViewCreateDto; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.ViewUpdateDto; import at.tuwien.entities.database.Database; @@ -87,7 +87,7 @@ public class ViewServiceImpl implements ViewService { @Override @Transactional - public View create(Database database, User creator, ViewCreateDto data) throws MalformedException, + public View create(Database database, User creator, CreateViewDto data) throws MalformedException, DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException { /* create in metadata database */ 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 8599983687..21e7e0519b 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 @@ -7,21 +7,20 @@ import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto; import at.tuwien.api.amqp.QueueDto; import at.tuwien.api.auth.LoginRequestDto; import at.tuwien.api.auth.RefreshTokenRequestDto; -import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.container.ContainerBriefDto; import at.tuwien.api.container.ContainerDto; import at.tuwien.api.container.image.*; import at.tuwien.api.database.*; -import at.tuwien.api.database.internal.CreateDatabaseDto; import at.tuwien.api.database.query.QueryBriefDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.table.TableBriefDto; -import at.tuwien.api.database.table.TableCreateDto; +import at.tuwien.api.database.table.CreateTableDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.TableStatisticDto; 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.CreateTableConstraintsDto; import at.tuwien.api.database.table.constraints.ConstraintsDto; import at.tuwien.api.database.table.constraints.foreign.*; import at.tuwien.api.database.table.constraints.primary.PrimaryKeyDto; @@ -619,7 +618,7 @@ public abstract class BaseTest { public final static Principal USER_1_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_1_DETAILS, USER_1_PASSWORD, USER_1_DETAILS.getAuthorities()); - public final static SignupRequestDto USER_1_SIGNUP_REQUEST_DTO = SignupRequestDto.builder() + public final static CreateUserDto USER_1_SIGNUP_REQUEST_DTO = CreateUserDto.builder() .username(USER_1_USERNAME) .password(USER_1_PASSWORD) .email(USER_1_EMAIL) @@ -697,7 +696,7 @@ public abstract class BaseTest { .qualifiedName(USER_2_QUALIFIED_NAME) .build(); - public final static SignupRequestDto USER_2_SIGNUP_REQUEST_DTO = SignupRequestDto.builder() + public final static CreateUserDto USER_2_SIGNUP_REQUEST_DTO = CreateUserDto.builder() .username(USER_2_USERNAME) .password(USER_2_PASSWORD) .email(USER_2_EMAIL) @@ -1274,13 +1273,13 @@ public abstract class BaseTest { public final static UserDto DATABASE_1_CREATOR_DTO = USER_1_DTO; public final static UserDto DATABASE_1_OWNER_DTO = USER_1_DTO; - public final static DatabaseCreateDto DATABASE_1_CREATE = DatabaseCreateDto.builder() + public final static CreateDatabaseDto DATABASE_1_CREATE = CreateDatabaseDto.builder() .name(DATABASE_1_NAME) .isPublic(DATABASE_1_PUBLIC) .cid(CONTAINER_1_ID) .build(); - public final static CreateDatabaseDto DATABASE_1_CREATE_INTERNAL = CreateDatabaseDto.builder() + public final static at.tuwien.api.database.internal.CreateDatabaseDto DATABASE_1_CREATE_INTERNAL = at.tuwien.api.database.internal.CreateDatabaseDto.builder() .internalName(DATABASE_1_INTERNALNAME) .containerId(CONTAINER_1_ID) .username(USER_1_USERNAME) @@ -1302,7 +1301,7 @@ public abstract class BaseTest { public final static UUID DATABASE_2_OWNER = USER_2_ID; public final static UUID DATABASE_2_CREATOR = USER_2_ID; - public final static DatabaseCreateDto DATABASE_2_CREATE = DatabaseCreateDto.builder() + public final static CreateDatabaseDto DATABASE_2_CREATE = CreateDatabaseDto.builder() .name(DATABASE_2_NAME) .isPublic(DATABASE_2_PUBLIC) .cid(CONTAINER_1_ID) @@ -1358,7 +1357,7 @@ public abstract class BaseTest { .identifiers(new LinkedList<>()) .build(); - public final static DatabaseCreateDto DATABASE_3_CREATE = DatabaseCreateDto.builder() + public final static CreateDatabaseDto DATABASE_3_CREATE = CreateDatabaseDto.builder() .name(DATABASE_3_NAME) .isPublic(DATABASE_3_PUBLIC) .cid(CONTAINER_1_ID) @@ -1418,169 +1417,169 @@ public abstract class BaseTest { .lastRetrieved(Instant.now()) .build(); - public final static TableCreateDto TABLE_0_CREATE_DTO = TableCreateDto.builder() + public final static CreateTableDto TABLE_0_CREATE_DTO = CreateTableDto.builder() .name("full") .description("full example") - .constraints(ConstraintsCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() .uniques(new LinkedList<>()) .foreignKeys(new LinkedList<>()) .build()) - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("col1a") .type(ColumnTypeDto.CHAR) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col1b") .type(ColumnTypeDto.CHAR) .nullAllowed(true) .size(50L) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col2a") .type(ColumnTypeDto.VARCHAR) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col2b") .type(ColumnTypeDto.VARCHAR) .nullAllowed(true) .size(1024L) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col3") .type(ColumnTypeDto.BINARY) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col4") .type(ColumnTypeDto.VARBINARY) .nullAllowed(true) .size(200L) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col5") .type(ColumnTypeDto.TINYBLOB) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col6") .type(ColumnTypeDto.TINYTEXT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col7") .type(ColumnTypeDto.TEXT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col8") .type(ColumnTypeDto.BLOB) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col9") .type(ColumnTypeDto.MEDIUMTEXT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col10") .type(ColumnTypeDto.MEDIUMBLOB) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col11") .type(ColumnTypeDto.LONGTEXT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col12") .type(ColumnTypeDto.LONGBLOB) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col13") .type(ColumnTypeDto.ENUM) .nullAllowed(true) .enums(new LinkedList<>(List.of("val1", "val2"))) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col14") .type(ColumnTypeDto.SET) .nullAllowed(true) .sets(new LinkedList<>(List.of("val1", "val2"))) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col15") .type(ColumnTypeDto.BIT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col16") .type(ColumnTypeDto.TINYINT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col17") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col18") .type(ColumnTypeDto.SMALLINT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col19") .type(ColumnTypeDto.MEDIUMINT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col20") .type(ColumnTypeDto.INT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col21") .type(ColumnTypeDto.BIGINT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col22") .type(ColumnTypeDto.FLOAT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col23") .type(ColumnTypeDto.DOUBLE) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col24") .type(ColumnTypeDto.DECIMAL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col25") .type(ColumnTypeDto.DATE) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col26") .type(ColumnTypeDto.DATETIME) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col27") .type(ColumnTypeDto.TIMESTAMP) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col28") .type(ColumnTypeDto.TIME) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("col29") .type(ColumnTypeDto.YEAR) .nullAllowed(true) @@ -1946,32 +1945,32 @@ public abstract class BaseTest { .ownedBy(USER_3_ID) .build(); - public final static ConstraintsCreateDto TABLE_3_CONSTRAINTS_CREATE_DTO = ConstraintsCreateDto.builder() + public final static CreateTableConstraintsDto TABLE_3_CONSTRAINTS_CREATE_DTO = CreateTableConstraintsDto.builder() .checks(new LinkedHashSet<>()) .primaryKey(new LinkedHashSet<>()) .foreignKeys(new LinkedList<>()) .uniques(new LinkedList<>()) .build(); - public final static ConstraintsCreateDto TABLE_3_CONSTRAINTS_INVALID_CREATE_DTO = ConstraintsCreateDto.builder() + public final static CreateTableConstraintsDto TABLE_3_CONSTRAINTS_INVALID_CREATE_DTO = CreateTableConstraintsDto.builder() .checks(new LinkedHashSet<>()) .primaryKey(new LinkedHashSet<>()) // <<<< .uniques(new LinkedList<>()) - .foreignKeys(List.of(ForeignKeyCreateDto.builder() + .foreignKeys(List.of(CreateForeignKeyDto.builder() .referencedTable("weather_location") .columns(new LinkedList<>(List.of("fahrzeug"))) .referencedColumns(new LinkedList<>(List.of("doesnotexist"))) .build())) .build(); - public final static TableCreateDto TABLE_3_CREATE_DTO = TableCreateDto.builder() + public final static CreateTableDto TABLE_3_CREATE_DTO = CreateTableDto.builder() .name(TABLE_3_NAME) .description(TABLE_3_DESCRIPTION) .columns(new LinkedList<>()) .constraints(TABLE_3_CONSTRAINTS_CREATE_DTO) .build(); - public final static TableCreateDto TABLE_3_INVALID_CREATE_DTO = TableCreateDto.builder() + public final static CreateTableDto TABLE_3_INVALID_CREATE_DTO = CreateTableDto.builder() .name(TABLE_3_NAME) .description(TABLE_3_DESCRIPTION) .columns(new LinkedList<>()) @@ -2286,12 +2285,12 @@ public abstract class BaseTest { .isNullAllowed(true) .build()); - public final static List<ColumnCreateDto> TABLE_4_COLUMNS_CREATE_DTO = List.of(ColumnCreateDto.builder() + public final static List<CreateTableColumnDto> TABLE_4_COLUMNS_CREATE_DTO = List.of(CreateTableColumnDto.builder() .name("Timestamp") .type(ColumnTypeDto.TIMESTAMP) .nullAllowed(false) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Value") .type(ColumnTypeDto.DECIMAL) .nullAllowed(true) @@ -2299,14 +2298,14 @@ public abstract class BaseTest { .d(10L) .build()); - public final static ConstraintsCreateDto TABLE_4_CONSTRAINTS_CREATE_DTO = ConstraintsCreateDto.builder() + public final static CreateTableConstraintsDto TABLE_4_CONSTRAINTS_CREATE_DTO = CreateTableConstraintsDto.builder() .checks(new LinkedHashSet<>()) .primaryKey(new LinkedHashSet<>(Set.of("Timestamp"))) .foreignKeys(new LinkedList<>()) .uniques(new LinkedList<>(List.of(List.of("Timestamp")))) .build(); - public final static TableCreateDto TABLE_4_CREATE_DTO = TableCreateDto.builder() + public final static CreateTableDto TABLE_4_CREATE_DTO = CreateTableDto.builder() .name(TABLE_4_NAME) .description(TABLE_4_DESCRIPTION) .columns(TABLE_4_COLUMNS_CREATE_DTO) @@ -3183,32 +3182,32 @@ public abstract class BaseTest { .isNullAllowed(true) .build()); - public final static List<ColumnCreateDto> TABLE_1_COLUMNS_CREATE_DTO = List.of(ColumnCreateDto.builder() + public final static List<CreateTableColumnDto> TABLE_1_COLUMNS_CREATE_DTO = List.of(CreateTableColumnDto.builder() .name("id") .type(ColumnTypeDto.BIGINT) .nullAllowed(false) .enums(null) .sets(null) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Date") .type(ColumnTypeDto.DATE) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Location") .type(ColumnTypeDto.VARCHAR) .size(255L) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("MinTemp") .type(ColumnTypeDto.DECIMAL) .size(10L) .d(0L) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Rainfall") .type(ColumnTypeDto.DECIMAL) .size(10L) @@ -3218,21 +3217,21 @@ public abstract class BaseTest { .unitUri(UNIT_1_URI) .build()); - public final static ConstraintsCreateDto TABLE_1_CONSTRAINTS_CREATE_DTO = ConstraintsCreateDto.builder() + public final static CreateTableConstraintsDto TABLE_1_CONSTRAINTS_CREATE_DTO = CreateTableConstraintsDto.builder() .checks(new LinkedHashSet<>()) .primaryKey(new LinkedHashSet<>(List.of("id"))) .foreignKeys(new LinkedList<>()) .uniques(new LinkedList<>(List.of(List.of("date")))) .build(); - public final static ConstraintsCreateDto TABLE_1_CONSTRAINTS_CREATE_INVALID_DTO = ConstraintsCreateDto.builder() + public final static CreateTableConstraintsDto TABLE_1_CONSTRAINTS_CREATE_INVALID_DTO = CreateTableConstraintsDto.builder() .checks(new LinkedHashSet<>()) .primaryKey(new LinkedHashSet<>()) .foreignKeys(new LinkedList<>()) .uniques(new LinkedList<>(List.of(List.of("date")))) .build(); - public final static TableCreateDto TABLE_1_CREATE_DTO = TableCreateDto.builder() + public final static CreateTableDto TABLE_1_CREATE_DTO = CreateTableDto.builder() .name(TABLE_1_NAME) .description(TABLE_1_DESCRIPTION) .columns(TABLE_1_COLUMNS_CREATE_DTO) @@ -4655,137 +4654,137 @@ public abstract class BaseTest { .isNullAllowed(true) .build()); - public final static List<ForeignKeyCreateDto> TABLE_5_FOREIGN_KEYS_INVALID_CREATE = List.of(ForeignKeyCreateDto.builder() + public final static List<CreateForeignKeyDto> TABLE_5_FOREIGN_KEYS_INVALID_CREATE = List.of(CreateForeignKeyDto.builder() .columns(new LinkedList<>(List.of("somecolumn"))) .referencedTable("sometable") .referencedColumns(new LinkedList<>(List.of("someothercolumn"))) .build()); - public final static ConstraintsCreateDto TABLE_5_CONSTRAINTS_INVALID_CREATE = ConstraintsCreateDto.builder() + public final static CreateTableConstraintsDto TABLE_5_CONSTRAINTS_INVALID_CREATE = CreateTableConstraintsDto.builder() .foreignKeys(TABLE_5_FOREIGN_KEYS_INVALID_CREATE) .build(); - public final static List<ColumnCreateDto> TABLE_5_COLUMNS_CREATE = List.of(ColumnCreateDto.builder() + public final static List<CreateTableColumnDto> TABLE_5_COLUMNS_CREATE = List.of(CreateTableColumnDto.builder() .name("id") .type(ColumnTypeDto.BIGINT) .nullAllowed(false) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Animal Name") .type(ColumnTypeDto.VARCHAR) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Hair") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Feathers") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Bread") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Eggs") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Milk") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Water") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Airborne") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Waterborne") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Aquantic") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Predator") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Backbone") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Breathes") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Venomous") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Fin") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Legs") .type(ColumnTypeDto.INT) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Tail") .type(ColumnTypeDto.DECIMAL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Domestic") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Catsize") .type(ColumnTypeDto.BOOL) .nullAllowed(true) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("Class Type") .type(ColumnTypeDto.DECIMAL) .nullAllowed(true) .build()); - public final static ConstraintsCreateDto TABLE_5_CREATE_CONSTRAINTS_DTO = ConstraintsCreateDto.builder() + public final static CreateTableConstraintsDto TABLE_5_CREATE_CONSTRAINTS_DTO = CreateTableConstraintsDto.builder() .primaryKey(Set.of("id")) .uniques(new LinkedList<>(List.of(List.of("id")))) .checks(new LinkedHashSet<>()) .foreignKeys(new LinkedList<>()) .build(); - public final static TableCreateDto TABLE_5_CREATE_DTO = TableCreateDto.builder() + public final static CreateTableDto TABLE_5_CREATE_DTO = CreateTableDto.builder() .name(TABLE_5_NAME) .description(TABLE_5_DESCRIPTION) .columns(TABLE_5_COLUMNS_CREATE) .constraints(TABLE_5_CREATE_CONSTRAINTS_DTO) .build(); - public final static TableCreateDto TABLE_5_INVALID_CREATE_DTO = TableCreateDto.builder() + public final static CreateTableDto TABLE_5_INVALID_CREATE_DTO = CreateTableDto.builder() .name(TABLE_5_NAME) .description(TABLE_5_DESCRIPTION) .columns(TABLE_5_COLUMNS_CREATE) @@ -4912,7 +4911,7 @@ public abstract class BaseTest { public final static List<List<String>> TABLE_6_UNIQUES_CREATE = List.of( List.of("firstname", "lastname")); - public final static List<ForeignKeyCreateDto> TABLE_6_FOREIGN_KEYS_CREATE = List.of(ForeignKeyCreateDto.builder() + public final static List<CreateForeignKeyDto> TABLE_6_FOREIGN_KEYS_CREATE = List.of(CreateForeignKeyDto.builder() .columns(new LinkedList<>(List.of("ref_id"))) .referencedTable("zoo") .referencedColumns(new LinkedList<>(List.of("id"))) @@ -4920,27 +4919,27 @@ public abstract class BaseTest { public final static Set<String> TABLE_6_CHECKS_CREATE = Set.of("firstname != lastname"); - public final static ConstraintsCreateDto TABLE_6_CONSTRAINTS_CREATE = ConstraintsCreateDto.builder() + public final static CreateTableConstraintsDto TABLE_6_CONSTRAINTS_CREATE = CreateTableConstraintsDto.builder() .uniques(TABLE_6_UNIQUES_CREATE) .foreignKeys(TABLE_6_FOREIGN_KEYS_CREATE) .checks(TABLE_6_CHECKS_CREATE) .primaryKey(Set.of("id")) .build(); - public final static List<ColumnCreateDto> TABLE_6_COLUMNS_CREATE = List.of( - ColumnCreateDto.builder() + public final static List<CreateTableColumnDto> TABLE_6_COLUMNS_CREATE = List.of( + CreateTableColumnDto.builder() .name("name_id") .type(ColumnTypeDto.BIGINT) .nullAllowed(false) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("zoo_id") .type(ColumnTypeDto.BIGINT) .size(255L) .nullAllowed(false) .build()); - public final static TableCreateDto TABLE_6_CREATE_DTO = TableCreateDto.builder() + public final static CreateTableDto TABLE_6_CREATE_DTO = CreateTableDto.builder() .name(TABLE_6_NAME) .description(TABLE_6_DESCRIPTION) .columns(TABLE_6_COLUMNS_CREATE) @@ -5162,7 +5161,7 @@ public abstract class BaseTest { .queryHash(VIEW_1_QUERY_HASH) .build(); - public final static ViewCreateDto VIEW_1_CREATE_DTO = ViewCreateDto.builder() + public final static CreateViewDto VIEW_1_CREATE_DTO = CreateViewDto.builder() .isPublic(VIEW_1_PUBLIC) .name(VIEW_1_NAME) .query(VIEW_1_QUERY) @@ -6128,13 +6127,13 @@ public abstract class BaseTest { .language(IDENTIFIER_1_TITLE_1_LANG_DTO) .build(); - public final static IdentifierSaveTitleDto IDENTIFIER_1_TITLE_1_CREATE_DTO = IdentifierSaveTitleDto.builder() + public final static SaveIdentifierTitleDto IDENTIFIER_1_TITLE_1_CREATE_DTO = SaveIdentifierTitleDto.builder() .title(IDENTIFIER_1_TITLE_1_TITLE) .titleType(IDENTIFIER_1_TITLE_1_TYPE_DTO) .language(IDENTIFIER_1_TITLE_1_LANG_DTO) .build(); - public final static IdentifierSaveTitleDto IDENTIFIER_1_TITLE_1_UPDATE_DTO = IdentifierSaveTitleDto.builder() + public final static SaveIdentifierTitleDto IDENTIFIER_1_TITLE_1_UPDATE_DTO = SaveIdentifierTitleDto.builder() .title(IDENTIFIER_1_TITLE_1_TITLE_MODIFY) .titleType(IDENTIFIER_1_TITLE_1_TYPE_DTO) .language(IDENTIFIER_1_TITLE_1_LANG_DTO) @@ -6170,13 +6169,13 @@ public abstract class BaseTest { .language(IDENTIFIER_1_TITLE_2_LANG_DTO) .build(); - public final static IdentifierSaveTitleDto IDENTIFIER_1_TITLE_2_CREATE_DTO = IdentifierSaveTitleDto.builder() + public final static SaveIdentifierTitleDto IDENTIFIER_1_TITLE_2_CREATE_DTO = SaveIdentifierTitleDto.builder() .title(IDENTIFIER_1_TITLE_2_TITLE) .titleType(IDENTIFIER_1_TITLE_2_TYPE_DTO) .language(IDENTIFIER_1_TITLE_2_LANG_DTO) .build(); - public final static IdentifierSaveTitleDto IDENTIFIER_1_TITLE_2_UPDATE_DTO = IdentifierSaveTitleDto.builder() + public final static SaveIdentifierTitleDto IDENTIFIER_1_TITLE_2_UPDATE_DTO = SaveIdentifierTitleDto.builder() .title(IDENTIFIER_1_TITLE_2_TITLE_MODIFY) .titleType(IDENTIFIER_1_TITLE_2_TYPE_DTO) .language(IDENTIFIER_1_TITLE_2_LANG_DTO) @@ -6212,7 +6211,7 @@ public abstract class BaseTest { .language(IDENTIFIER_1_DESCRIPTION_1_LANG_DTO) .build(); - public final static IdentifierSaveDescriptionDto IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO = IdentifierSaveDescriptionDto.builder() + public final static SaveIdentifierDescriptionDto IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO = SaveIdentifierDescriptionDto.builder() .id(null) .description(IDENTIFIER_1_DESCRIPTION_1_DESCRIPTION) .descriptionType(IDENTIFIER_1_DESCRIPTION_1_TYPE_DTO) @@ -6260,7 +6259,7 @@ public abstract class BaseTest { .affiliationIdentifierSchemeUri(IDENTIFIER_1_CREATOR_1_AFFILIATION_IDENTIFIER_SCHEME_URI) .build(); - public final static CreatorSaveDto IDENTIFIER_1_CREATOR_1_CREATE_DTO = CreatorSaveDto.builder() + public final static SaveIdentifierCreatorDto IDENTIFIER_1_CREATOR_1_CREATE_DTO = SaveIdentifierCreatorDto.builder() .id(null) .firstname(IDENTIFIER_1_CREATOR_1_FIRSTNAME) .lastname(IDENTIFIER_1_CREATOR_1_LASTNAME) @@ -6297,7 +6296,7 @@ public abstract class BaseTest { .awardTitle(FUNDER_1_AWARD_TITLE) .build(); - public final static IdentifierFunderSaveDto IDENTIFIER_1_FUNDER_1_CREATE_DTO = IdentifierFunderSaveDto.builder() + public final static SaveIdentifierFunderDto IDENTIFIER_1_FUNDER_1_CREATE_DTO = SaveIdentifierFunderDto.builder() .funderName(FUNDER_1_NAME) .funderIdentifier(FUNDER_1_IDENTIFIER) .funderIdentifierType(FUNDER_1_IDENTIFIER_TYPE_DTO) @@ -6402,7 +6401,7 @@ public abstract class BaseTest { .status(IDENTIFIER_1_STATUS_TYPE_DTO) .build(); - public final static IdentifierCreateDto IDENTIFIER_1_CREATE_DTO = IdentifierCreateDto.builder() + public final static CreateIdentifierDto IDENTIFIER_1_CREATE_DTO = CreateIdentifierDto.builder() .databaseId(IDENTIFIER_1_DATABASE_ID) .type(IDENTIFIER_1_TYPE_DTO) .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR) @@ -6419,7 +6418,7 @@ public abstract class BaseTest { .funders(new LinkedList<>(List.of(IDENTIFIER_1_FUNDER_1_CREATE_DTO))) .build(); - public final static IdentifierCreateDto IDENTIFIER_1_CREATE_WITH_DOI_DTO = IdentifierCreateDto.builder() + public final static CreateIdentifierDto IDENTIFIER_1_CREATE_WITH_DOI_DTO = CreateIdentifierDto.builder() .databaseId(IDENTIFIER_1_DATABASE_ID) .type(IDENTIFIER_1_TYPE_DTO) .doi(IDENTIFIER_1_DOI) @@ -6511,7 +6510,7 @@ public abstract class BaseTest { .titleType(IDENTIFIER_5_TITLE_1_TYPE_DTO) .build(); - public final static IdentifierSaveTitleDto IDENTIFIER_5_TITLE_1_CREATE_DTO = IdentifierSaveTitleDto.builder() + public final static SaveIdentifierTitleDto IDENTIFIER_5_TITLE_1_CREATE_DTO = SaveIdentifierTitleDto.builder() .title(IDENTIFIER_5_TITLE_1_TITLE) .language(IDENTIFIER_5_TITLE_1_LANG_DTO) .titleType(IDENTIFIER_5_TITLE_1_TYPE_DTO) @@ -6539,7 +6538,7 @@ public abstract class BaseTest { .descriptionType(IDENTIFIER_5_DESCRIPTION_1_TYPE_DTO) .build(); - public final static IdentifierSaveDescriptionDto IDENTIFIER_5_DESCRIPTION_1_CREATE_DTO = IdentifierSaveDescriptionDto.builder() + public final static SaveIdentifierDescriptionDto IDENTIFIER_5_DESCRIPTION_1_CREATE_DTO = SaveIdentifierDescriptionDto.builder() .id(null) .description(IDENTIFIER_5_DESCRIPTION_1_DESCRIPTION) .language(IDENTIFIER_5_DESCRIPTION_1_LANG_DTO) @@ -6574,7 +6573,7 @@ public abstract class BaseTest { .affiliationIdentifierSchemeUri(CREATOR_1_AFFIL_URI) .build(); - public final static CreatorSaveDto IDENTIFIER_5_CREATOR_1_CREATE_DTO = CreatorSaveDto.builder() + public final static SaveIdentifierCreatorDto IDENTIFIER_5_CREATOR_1_CREATE_DTO = SaveIdentifierCreatorDto.builder() .firstname(CREATOR_1_FIRSTNAME) .lastname(CREATOR_1_LASTNAME) .creatorName(CREATOR_1_NAME) @@ -6583,7 +6582,7 @@ public abstract class BaseTest { .affiliation(CREATOR_1_AFFIL) .build(); - public final static CreatorSaveDto IDENTIFIER_5_CREATOR_1_MODIFY_DTO = CreatorSaveDto.builder() + public final static SaveIdentifierCreatorDto IDENTIFIER_5_CREATOR_1_MODIFY_DTO = SaveIdentifierCreatorDto.builder() .firstname(CREATOR_1_FIRSTNAME) .lastname(CREATOR_1_LASTNAME) .creatorName(CREATOR_1_NAME) @@ -6614,7 +6613,7 @@ public abstract class BaseTest { .affiliation(CREATOR_2_AFFIL) .build(); - public final static CreatorSaveDto IDENTIFIER_5_CREATOR_2_CREATE_DTO = CreatorSaveDto.builder() + public final static SaveIdentifierCreatorDto IDENTIFIER_5_CREATOR_2_CREATE_DTO = SaveIdentifierCreatorDto.builder() .firstname(CREATOR_2_FIRSTNAME) .lastname(CREATOR_2_LASTNAME) .creatorName(CREATOR_2_NAME) @@ -6623,7 +6622,7 @@ public abstract class BaseTest { .affiliation(CREATOR_2_AFFIL) .build(); - public final static CreatorSaveDto IDENTIFIER_5_CREATOR_2_MODIFY_DTO = CreatorSaveDto.builder() + public final static SaveIdentifierCreatorDto IDENTIFIER_5_CREATOR_2_MODIFY_DTO = SaveIdentifierCreatorDto.builder() .firstname(CREATOR_2_FIRSTNAME) .lastname(CREATOR_2_LASTNAME) .creatorName(CREATOR_2_NAME) @@ -6707,13 +6706,13 @@ public abstract class BaseTest { .value(RELATED_IDENTIFIER_5_VALUE) .build(); - public final static RelatedIdentifierSaveDto IDENTIFIER_1_RELATED_IDENTIFIER_5_CREATE_DTO = RelatedIdentifierSaveDto.builder() + public final static SaveRelatedIdentifierDto IDENTIFIER_1_RELATED_IDENTIFIER_5_CREATE_DTO = SaveRelatedIdentifierDto.builder() .value(RELATED_IDENTIFIER_5_VALUE) .type(RELATED_IDENTIFIER_5_TYPE_DTO) .relation(RELATED_IDENTIFIER_5_RELATION_TYPE_DTO) .build(); - public final static IdentifierCreateDto IDENTIFIER_5_CREATE_DTO = IdentifierCreateDto.builder() + public final static CreateIdentifierDto IDENTIFIER_5_CREATE_DTO = CreateIdentifierDto.builder() .databaseId(IDENTIFIER_5_DATABASE_ID) .publicationYear(IDENTIFIER_5_PUBLICATION_YEAR) .publisher(IDENTIFIER_5_PUBLISHER) @@ -6782,7 +6781,7 @@ public abstract class BaseTest { .language(IDENTIFIER_6_TITLE_1_LANG_DTO) .build(); - public final static IdentifierSaveTitleDto IDENTIFIER_6_TITLE_1_CREATE_DTO = IdentifierSaveTitleDto.builder() + public final static SaveIdentifierTitleDto IDENTIFIER_6_TITLE_1_CREATE_DTO = SaveIdentifierTitleDto.builder() .title(IDENTIFIER_6_TITLE_1_TITLE_MODIFY) .language(IDENTIFIER_6_TITLE_1_LANG_DTO) .build(); @@ -6812,7 +6811,7 @@ public abstract class BaseTest { .language(IDENTIFIER_6_DESCRIPTION_1_LANG_DTO) .build(); - public final static IdentifierSaveDescriptionDto IDENTIFIER_6_DESCRIPTION_1_CREATE_DTO = IdentifierSaveDescriptionDto.builder() + public final static SaveIdentifierDescriptionDto IDENTIFIER_6_DESCRIPTION_1_CREATE_DTO = SaveIdentifierDescriptionDto.builder() .id(null) .description(IDENTIFIER_6_DESCRIPTION_1_DESCRIPTION_MODIFY) .language(IDENTIFIER_6_DESCRIPTION_1_LANG_DTO) @@ -6846,7 +6845,7 @@ public abstract class BaseTest { .affiliationIdentifierSchemeUri(CREATOR_1_AFFIL_URI) .build(); - public final static CreatorSaveDto IDENTIFIER_6_CREATOR_1_CREATE_DTO = CreatorSaveDto.builder() + public final static SaveIdentifierCreatorDto IDENTIFIER_6_CREATOR_1_CREATE_DTO = SaveIdentifierCreatorDto.builder() .firstname(CREATOR_1_FIRSTNAME) .lastname(CREATOR_1_LASTNAME) .creatorName(CREATOR_1_NAME) @@ -6857,7 +6856,7 @@ public abstract class BaseTest { .affiliationIdentifierScheme(CREATOR_1_AFFIL_TYPE_DTO) .build(); - public final static CreatorSaveDto IDENTIFIER_6_CREATOR_1_MODIFY_DTO = CreatorSaveDto.builder() + public final static SaveIdentifierCreatorDto IDENTIFIER_6_CREATOR_1_MODIFY_DTO = SaveIdentifierCreatorDto.builder() .firstname(CREATOR_1_FIRSTNAME) .lastname(CREATOR_1_LASTNAME) .creatorName(CREATOR_1_NAME) @@ -6981,7 +6980,7 @@ public abstract class BaseTest { .status(IDENTIFIER_6_STATUS_TYPE_DTO) .build(); - public final static IdentifierCreateDto IDENTIFIER_6_CREATE_DTO = IdentifierCreateDto.builder() + public final static CreateIdentifierDto IDENTIFIER_6_CREATE_DTO = CreateIdentifierDto.builder() .databaseId(IDENTIFIER_6_DATABASE_ID) .publicationYear(IDENTIFIER_6_PUBLICATION_YEAR) .publisher(IDENTIFIER_6_PUBLISHER) @@ -7076,7 +7075,7 @@ public abstract class BaseTest { .status(IDENTIFIER_7_STATUS_TYPE_DTO) .build(); - public final static CreatorSaveDto IDENTIFIER_7_CREATOR_1_CREATE_DTO = CreatorSaveDto.builder() + public final static SaveIdentifierCreatorDto IDENTIFIER_7_CREATOR_1_CREATE_DTO = SaveIdentifierCreatorDto.builder() .firstname(CREATOR_1_FIRSTNAME) .lastname(CREATOR_1_LASTNAME) .creatorName(CREATOR_1_NAME) @@ -7086,7 +7085,7 @@ public abstract class BaseTest { .affiliationIdentifier(CREATOR_1_AFFIL_ROR) .build(); - public final static IdentifierCreateDto IDENTIFIER_7_CREATE_DTO = IdentifierCreateDto.builder() + public final static CreateIdentifierDto IDENTIFIER_7_CREATE_DTO = CreateIdentifierDto.builder() .databaseId(IDENTIFIER_7_DATABASE_ID) .publicationYear(IDENTIFIER_7_PUBLICATION_YEAR) .publisher(IDENTIFIER_7_PUBLISHER) @@ -7129,7 +7128,7 @@ public abstract class BaseTest { public final static IdentifierStatusTypeDto IDENTIFIER_2_STATUS_TYPE_DTO = IdentifierStatusTypeDto.PUBLISHED; public final static UUID IDENTIFIER_2_CREATED_BY = USER_1_ID; - public final static IdentifierCreateDto IDENTIFIER_2_CREATE_DTO = IdentifierCreateDto.builder() + public final static CreateIdentifierDto IDENTIFIER_2_CREATE_DTO = CreateIdentifierDto.builder() .databaseId(IDENTIFIER_2_DATABASE_ID) .queryId(IDENTIFIER_2_QUERY_ID) .type(IDENTIFIER_2_TYPE_DTO) @@ -7301,7 +7300,7 @@ public abstract class BaseTest { .status(IDENTIFIER_3_STATUS_TYPE_DTO) .build(); - public final static IdentifierCreateDto IDENTIFIER_3_CREATE_DTO = IdentifierCreateDto.builder() + public final static CreateIdentifierDto IDENTIFIER_3_CREATE_DTO = CreateIdentifierDto.builder() .databaseId(IDENTIFIER_3_DATABASE_ID) .viewId(IDENTIFIER_3_VIEW_ID) .type(IDENTIFIER_3_TYPE_DTO) @@ -7400,7 +7399,7 @@ public abstract class BaseTest { .status(IDENTIFIER_4_STATUS_TYPE_DTO) .build(); - public final static IdentifierCreateDto IDENTIFIER_4_CREATE_DTO = IdentifierCreateDto.builder() + public final static CreateIdentifierDto IDENTIFIER_4_CREATE_DTO = CreateIdentifierDto.builder() .databaseId(IDENTIFIER_4_DATABASE_ID) .publicationYear(IDENTIFIER_4_PUBLICATION_YEAR) .publisher(IDENTIFIER_4_PUBLISHER) diff --git a/lib/python/dbrepo/RestClient.py b/lib/python/dbrepo/RestClient.py index 78861ffd04..a0b4bf60c4 100644 --- a/lib/python/dbrepo/RestClient.py +++ b/lib/python/dbrepo/RestClient.py @@ -1767,13 +1767,13 @@ class RestClient: raise ResponseCodeError(f'Failed to update query: response code: {response.status_code} is not ' f'202 (ACCEPTED): {response.text}') - def create_identifier(self, database_id: int, type: IdentifierType, titles: List[CreateIdentifierTitle], + def create_identifier(self, database_id: int, type: IdentifierType, titles: List[SaveIdentifierTitle], publisher: str, creators: List[CreateIdentifierCreator], publication_year: int, - descriptions: List[CreateIdentifierDescription] = None, - funders: List[CreateIdentifierFunder] = None, licenses: List[License] = None, + descriptions: List[SaveIdentifierDescription] = None, + funders: List[SaveIdentifierFunder] = None, licenses: List[License] = None, language: Language = None, subset_id: int = None, view_id: int = None, table_id: int = None, publication_day: int = None, publication_month: int = None, - related_identifiers: List[CreateRelatedIdentifier] = None) -> Identifier: + related_identifiers: List[SaveRelatedIdentifier] = None) -> Identifier: """ Create an identifier draft. @@ -1828,12 +1828,12 @@ class RestClient: f'201 (CREATED): {response.text}') def save_identifier(self, identifier_id: int, database_id: int, type: IdentifierType, - titles: List[CreateIdentifierTitle], publisher: str, creators: List[CreateIdentifierCreator], - publication_year: int, descriptions: List[CreateIdentifierDescription] = None, - funders: List[CreateIdentifierFunder] = None, licenses: List[License] = None, + titles: List[SaveIdentifierTitle], publisher: str, creators: List[CreateIdentifierCreator], + publication_year: int, descriptions: List[SaveIdentifierDescription] = None, + funders: List[SaveIdentifierFunder] = None, licenses: List[License] = None, language: Language = None, subset_id: int = None, view_id: int = None, table_id: int = None, publication_day: int = None, publication_month: int = None, - related_identifiers: List[CreateRelatedIdentifier] = None) -> Identifier: + related_identifiers: List[SaveRelatedIdentifier] = None) -> Identifier: """ Save an existing identifier and update the metadata attached to it. diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py index b7556a03a5..50cd161bc3 100644 --- a/lib/python/dbrepo/api/dto.py +++ b/lib/python/dbrepo/api/dto.py @@ -419,7 +419,7 @@ class IdentifierTitle(BaseModel): type: Optional[TitleType] = None -class CreateIdentifierTitle(BaseModel): +class SaveIdentifierTitle(BaseModel): title: str language: Optional[Language] = None type: Optional[TitleType] = None @@ -432,7 +432,7 @@ class IdentifierDescription(BaseModel): type: Optional[DescriptionType] = None -class CreateIdentifierDescription(BaseModel): +class SaveIdentifierDescription(BaseModel): description: str language: Optional[Language] = None type: Optional[DescriptionType] = None @@ -448,7 +448,7 @@ class IdentifierFunder(BaseModel): award_title: Optional[str] = None -class CreateIdentifierFunder(BaseModel): +class SaveIdentifierFunder(BaseModel): funder_name: str funder_identifier: Optional[str] = None funder_identifier_type: Optional[str] = None @@ -594,7 +594,7 @@ class RelatedIdentifier(BaseModel): relation: RelatedIdentifierRelation -class CreateRelatedIdentifier(BaseModel): +class SaveRelatedIdentifier(BaseModel): value: str type: RelatedIdentifierType relation: RelatedIdentifierRelation @@ -606,9 +606,9 @@ class CreateIdentifier(BaseModel): creators: List[CreateIdentifierCreator] publication_year: int publisher: str - titles: List[CreateIdentifierTitle] - descriptions: List[CreateIdentifierDescription] - funders: Optional[List[CreateIdentifierFunder]] = field(default_factory=list) + titles: List[SaveIdentifierTitle] + descriptions: List[SaveIdentifierDescription] + funders: Optional[List[SaveIdentifierFunder]] = field(default_factory=list) doi: Optional[str] = None language: Optional[str] = None licenses: Optional[List[License]] = field(default_factory=list) @@ -618,7 +618,7 @@ class CreateIdentifier(BaseModel): query: Optional[str] = None query_normalized: Optional[str] = None execution: Optional[str] = None - related_identifiers: Optional[List[CreateRelatedIdentifier]] = field(default_factory=list) + related_identifiers: Optional[List[SaveRelatedIdentifier]] = field(default_factory=list) result_hash: Optional[str] = None result_number: Optional[int] = None publication_day: Optional[int] = None diff --git a/lib/python/tests/test_dtos.py b/lib/python/tests/test_dtos.py index 0a90949dba..3f05e24081 100644 --- a/lib/python/tests/test_dtos.py +++ b/lib/python/tests/test_dtos.py @@ -42,8 +42,8 @@ class AnalyseUnitTest(unittest.TestCase): skipped: [str] = [] def setUp(self): - with open('../../.docs/.openapi/api.yaml', 'r') as f: - self.schemas = safe_load(f)['components']['schemas'] + with open('../../.docs/.openapi/api.yaml') as fh: + self.schemas = safe_load(fh)['components']['schemas'] for name, obj in inspect.getmembers(sys.modules[dto.__name__]): self.found += 1 if not inspect.isclass(obj): diff --git a/lib/python/tests/test_unit_identifier.py b/lib/python/tests/test_unit_identifier.py index 0c71c0216f..8726d05ef2 100644 --- a/lib/python/tests/test_unit_identifier.py +++ b/lib/python/tests/test_unit_identifier.py @@ -3,8 +3,8 @@ import unittest import requests_mock from dbrepo.RestClient import RestClient -from dbrepo.api.dto import Identifier, IdentifierType, CreateIdentifierTitle, Creator, IdentifierTitle, \ - IdentifierDescription, CreateIdentifierDescription, Language, CreateIdentifierFunder, CreateRelatedIdentifier, \ +from dbrepo.api.dto import Identifier, IdentifierType, SaveIdentifierTitle, Creator, IdentifierTitle, \ + IdentifierDescription, SaveIdentifierDescription, Language, SaveIdentifierFunder, SaveRelatedIdentifier, \ RelatedIdentifierRelation, RelatedIdentifierType, IdentifierFunder, RelatedIdentifier, UserBrief, \ IdentifierStatusType, CreateIdentifierCreator from dbrepo.api.exceptions import MalformedError, ForbiddenError, NotExistsError, AuthenticationError @@ -36,14 +36,14 @@ class IdentifierUnitTest(unittest.TestCase): client = RestClient(username="a", password="b") response = client.create_identifier( database_id=1, type=IdentifierType.VIEW, - titles=[CreateIdentifierTitle(title='Test Title')], + titles=[SaveIdentifierTitle(title='Test Title')], publisher='TU Wien', publication_year=2024, language=Language.EN, - funders=[CreateIdentifierFunder(funder_name='FWF')], - related_identifiers=[CreateRelatedIdentifier(value='10.12345/abc', - relation=RelatedIdentifierRelation.CITES, - type=RelatedIdentifierType.DOI)], - descriptions=[CreateIdentifierDescription(description='Test Description')], + funders=[SaveIdentifierFunder(funder_name='FWF')], + related_identifiers=[SaveRelatedIdentifier(value='10.12345/abc', + relation=RelatedIdentifierRelation.CITES, + type=RelatedIdentifierType.DOI)], + descriptions=[SaveIdentifierDescription(description='Test Description')], creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) self.assertEqual(exp, response) @@ -56,8 +56,8 @@ class IdentifierUnitTest(unittest.TestCase): client = RestClient(username="a", password="b") response = client.create_identifier( database_id=1, type=IdentifierType.VIEW, - titles=[CreateIdentifierTitle(title='Test Title')], - descriptions=[CreateIdentifierDescription(description='Test')], + titles=[SaveIdentifierTitle(title='Test Title')], + descriptions=[SaveIdentifierDescription(description='Test')], publisher='TU Wien', publication_year=2024, creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) except MalformedError: @@ -72,8 +72,8 @@ class IdentifierUnitTest(unittest.TestCase): client = RestClient(username="a", password="b") response = client.create_identifier( database_id=1, type=IdentifierType.VIEW, - titles=[CreateIdentifierTitle(title='Test Title')], - descriptions=[CreateIdentifierDescription(description='Test')], + titles=[SaveIdentifierTitle(title='Test Title')], + descriptions=[SaveIdentifierDescription(description='Test')], publisher='TU Wien', publication_year=2024, creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) except ForbiddenError: @@ -88,8 +88,8 @@ class IdentifierUnitTest(unittest.TestCase): client = RestClient(username="a", password="b") response = client.create_identifier( database_id=1, type=IdentifierType.VIEW, - titles=[CreateIdentifierTitle(title='Test Title')], - descriptions=[CreateIdentifierDescription(description='Test')], + titles=[SaveIdentifierTitle(title='Test Title')], + descriptions=[SaveIdentifierDescription(description='Test')], publisher='TU Wien', publication_year=2024, creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) except NotExistsError: @@ -103,8 +103,8 @@ class IdentifierUnitTest(unittest.TestCase): try: response = RestClient().create_identifier( database_id=1, type=IdentifierType.VIEW, - titles=[CreateIdentifierTitle(title='Test Title')], - descriptions=[CreateIdentifierDescription(description='Test')], + titles=[SaveIdentifierTitle(title='Test Title')], + descriptions=[SaveIdentifierDescription(description='Test')], publisher='TU Wien', publication_year=2024, creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) except AuthenticationError: -- GitLab From 4fe1424843b0255f5ed015bdaa1ba93a1a001b67 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Fri, 24 Jan 2025 14:44:20 +0100 Subject: [PATCH 17/19] Need the correct container Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .../tuwien/service/DatabaseServiceIntegrationTest.java | 4 ++-- .../at/tuwien/service/QueueServiceIntegrationTest.java | 4 ++-- .../tuwien/service/SubsetServiceIntegrationTest.java | 4 ++-- .../at/tuwien/service/TableServiceIntegrationTest.java | 10 +++++----- .../at/tuwien/service/ViewServiceIntegrationTest.java | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java index ae1917202d..2b7476e50f 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java @@ -106,7 +106,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { .build(); /* mock */ - MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); MariaDbConfig.grantWriteAccess(DATABASE_1_DTO, USER_1_USERNAME); /* pre-condition */ @@ -131,7 +131,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { .build(); /* mock */ - MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); /* test */ assertThrows(DatabaseMalformedException.class, () -> { diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java index a2ff1716e5..fc9860909f 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java @@ -52,8 +52,8 @@ public class QueueServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); } @Test diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java index 886d492cd7..ea58aa1da4 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java @@ -44,8 +44,8 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); } @Test diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java index cfab2600e5..e29e418ca8 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java @@ -82,11 +82,11 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_2_INTERNALNAME); - MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_3_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); - MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_3_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_3_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_3_DTO); /* s3 */ if (s3Client.listBuckets().buckets().stream().noneMatch(b -> b.name().equals(s3Config.getS3Bucket()))) { s3Client.createBucket(CreateBucketRequest.builder() diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java index 658c099724..74e2bd3d68 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java @@ -33,8 +33,8 @@ public class ViewServiceIntegrationTest extends AbstractUnitTest { public void beforeEach() throws SQLException { genesis(); /* metadata database */ - MariaDbConfig.dropDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_DTO, DATABASE_1_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); } @Test -- GitLab From 4c00c298dcdf91c7c2279a3e1f95e5abd90a76db Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Fri, 24 Jan 2025 15:48:46 +0100 Subject: [PATCH 18/19] Removed the temporary views Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .../at/tuwien/endpoints/SubsetEndpoint.java | 43 ++++++++------ .../at/tuwien/endpoints/TableEndpoint.java | 24 +++++--- .../at/tuwien/endpoints/ViewEndpoint.java | 28 ++++++--- .../endpoint/SubsetEndpointUnitTest.java | 58 +++++++++---------- .../endpoint/TableEndpointUnitTest.java | 16 +++-- .../tuwien/endpoint/ViewEndpointUnitTest.java | 6 +- .../tuwien/mvc/PrometheusEndpointMvcTest.java | 2 +- .../DatabaseServiceIntegrationTest.java | 18 +++--- .../java/at/tuwien/mapper/MariaDbMapper.java | 36 ++++++++++-- .../at/tuwien/service/DatabaseService.java | 4 +- .../java/at/tuwien/service/SubsetService.java | 32 +++++----- .../java/at/tuwien/service/TableService.java | 8 --- .../at/tuwien/service/impl/DataConnector.java | 8 +-- .../impl/DatabaseServiceMariaDbImpl.java | 4 +- .../impl/SubsetServiceMariaDbImpl.java | 52 ++++++++--------- .../service/impl/TableServiceMariaDbImpl.java | 40 ++----------- 16 files changed, 196 insertions(+), 183 deletions(-) diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java index ed867715e1..878502f1bc 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java @@ -9,6 +9,7 @@ import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.query.QueryPersistDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.exception.*; +import at.tuwien.mapper.MariaDbMapper; import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.*; import at.tuwien.validation.EndpointValidator; @@ -48,6 +49,7 @@ import java.util.UUID; @RequestMapping(path = "/api/database/{databaseId}/subset") public class SubsetEndpoint extends RestEndpoint { + private final MariaDbMapper mariaDbMapper; private final SubsetService subsetService; private final MetadataMapper metadataMapper; private final MetricsService metricsService; @@ -57,9 +59,10 @@ public class SubsetEndpoint extends RestEndpoint { private final EndpointValidator endpointValidator; @Autowired - public SubsetEndpoint(SubsetService subsetService, MetadataMapper metadataMapper, MetricsService metricsService, - StorageService storageService, DatabaseService databaseService, + public SubsetEndpoint(MariaDbMapper mariaDbMapper, SubsetService subsetService, MetadataMapper metadataMapper, + MetricsService metricsService, StorageService storageService, DatabaseService databaseService, CredentialService credentialService, EndpointValidator endpointValidator) { + this.mariaDbMapper = mariaDbMapper; this.subsetService = subsetService; this.metadataMapper = metadataMapper; this.metricsService = metricsService; @@ -188,20 +191,16 @@ public class SubsetEndpoint extends RestEndpoint { return ResponseEntity.ok(subset); case "text/csv": log.trace("accept header matches csv"); - try { - final Dataset<Row> dataset = subsetService.getData(database, subset, null, null); - metricsService.countSubsetGetData(databaseId, subsetId); - final ExportResourceDto resource = storageService.transformDataset(dataset); - final HttpHeaders headers = new HttpHeaders(); - headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\""); - log.trace("export table resulted in resource {}", resource); - return ResponseEntity.ok() - .headers(headers) - .body(resource.getResource()); - } catch (SQLException e) { - log.error("Failed to find data: {}", e.getMessage()); - throw new DatabaseUnavailableException("Failed to find data: " + e.getMessage(), e); - } + final String query = mariaDbMapper.rawSelectQuery(subset.getQuery(), timestamp, null, null); + final Dataset<Row> dataset = subsetService.getData(database, query, timestamp, null, null, null, null); + metricsService.countSubsetGetData(databaseId, subsetId); + final ExportResourceDto resource = storageService.transformDataset(dataset); + final HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\""); + log.trace("export table resulted in resource {}", resource); + return ResponseEntity.ok() + .headers(headers) + .body(resource.getResource()); } throw new FormatNotAvailableException("Must provide either application/json or text/csv value for header 'Accept': provided " + accept + " instead"); } @@ -290,7 +289,7 @@ public class SubsetEndpoint extends RestEndpoint { endpointValidator.validateOnlyPrivateSchemaAccess(database, principal); try { final Long subsetId = subsetService.create(database, data.getStatement(), timestamp, userId); - return getData(databaseId, subsetId, principal, request, page, size); + return getData(databaseId, subsetId, principal, request, timestamp, page, size); } catch (SQLException e) { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database: " + e.getMessage(), e); @@ -337,11 +336,12 @@ public class SubsetEndpoint extends RestEndpoint { @NotNull @PathVariable("subsetId") Long subsetId, Principal principal, @NotNull HttpServletRequest request, + @RequestParam(required = false) Instant timestamp, @RequestParam(required = false) Long page, @RequestParam(required = false) Long size) throws PaginationException, DatabaseNotFoundException, RemoteUnavailableException, NotAllowedException, QueryNotFoundException, DatabaseUnavailableException, TableMalformedException, QueryMalformedException, - UserNotFoundException, MetadataServiceException, TableNotFoundException, ViewMalformedException, ViewNotFoundException { + UserNotFoundException, MetadataServiceException, TableNotFoundException, ViewNotFoundException { log.debug("endpoint get subset data, databaseId={}, subsetId={}, principal.name={} page={}, size={}", databaseId, subsetId, principal != null ? principal.getName() : null, page, size); endpointValidator.validateDataParams(page, size); @@ -363,6 +363,10 @@ public class SubsetEndpoint extends RestEndpoint { size = 10L; log.debug("size not set: default to {}", size); } + if (timestamp == null) { + timestamp = Instant.now(); + log.debug("timestamp not set: default to {}", timestamp); + } try { final HttpHeaders headers = new HttpHeaders(); headers.set("X-Id", "" + subsetId); @@ -375,7 +379,8 @@ public class SubsetEndpoint extends RestEndpoint { .headers(headers) .build(); } - final Dataset<Row> dataset = subsetService.getData(database, subset, page, size); + final String query = mariaDbMapper.rawSelectQuery(subset.getQuery(), timestamp, page, size); + final Dataset<Row> dataset = subsetService.getData(database, query, timestamp, page, size, null, null); metricsService.countSubsetGetData(databaseId, subsetId); final String viewName = metadataMapper.queryDtoToViewName(subset); final ViewDto view = databaseService.inspectView(database, viewName); 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 92ba523f36..82ed0a96f7 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 @@ -10,6 +10,7 @@ import at.tuwien.api.database.table.internal.TableCreateDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; +import at.tuwien.mapper.MariaDbMapper; import at.tuwien.service.*; import at.tuwien.validation.EndpointValidator; import io.micrometer.observation.annotation.Observed; @@ -48,6 +49,8 @@ import java.util.Map; public class TableEndpoint extends RestEndpoint { private final TableService tableService; + private final MariaDbMapper mariaDbMapper; + private final SubsetService subsetService; private final MetricsService metricsService; private final StorageService storageService; private final DatabaseService databaseService; @@ -56,10 +59,13 @@ public class TableEndpoint extends RestEndpoint { private final MetadataServiceGateway metadataServiceGateway; @Autowired - public TableEndpoint(TableService tableService, MetricsService metricsService, StorageService storageService, - DatabaseService databaseService, CredentialService credentialService, - EndpointValidator endpointValidator, MetadataServiceGateway metadataServiceGateway) { + public TableEndpoint(TableService tableService, MariaDbMapper mariaDbMapper, SubsetService subsetService, + MetricsService metricsService, StorageService storageService, DatabaseService databaseService, + CredentialService credentialService, EndpointValidator endpointValidator, + MetadataServiceGateway metadataServiceGateway) { this.tableService = tableService; + this.mariaDbMapper = mariaDbMapper; + this.subsetService = subsetService; this.metricsService = metricsService; this.storageService = storageService; this.databaseService = databaseService; @@ -287,8 +293,10 @@ public class TableEndpoint extends RestEndpoint { } headers.set("Access-Control-Expose-Headers", "X-Headers"); headers.set("X-Headers", String.join(",", table.getColumns().stream().map(ColumnDto::getInternalName).toList())); - final Dataset<Row> dataset = tableService.getData(credentialService.getDatabase(table.getTdbid()), - table.getInternalName(), timestamp, page, size, null, null); + final String query = mariaDbMapper.defaultRawSelectQuery(table.getDatabase().getInternalName(), + table.getInternalName(), timestamp, page, size); + final Dataset<Row> dataset = subsetService.getData(credentialService.getDatabase(table.getTdbid()), + query, timestamp, page, size, null, null); metricsService.countTableGetData(databaseId, tableId); return ResponseEntity.ok() .headers(headers) @@ -624,8 +632,10 @@ public class TableEndpoint extends RestEndpoint { } credentialService.getAccess(databaseId, getId(principal)); } - final Dataset<Row> dataset = tableService.getData(credentialService.getDatabase(table.getTdbid()), - table.getInternalName(), timestamp, null, null, null, null); + final String query = mariaDbMapper.defaultRawSelectQuery(table.getDatabase().getInternalName(), + table.getInternalName(), timestamp, null, null); + final Dataset<Row> dataset = subsetService.getData(credentialService.getDatabase(table.getTdbid()), + query, timestamp, null, null, null, null); metricsService.countTableGetData(databaseId, tableId); final ExportResourceDto resource = storageService.transformDataset(dataset); final HttpHeaders headers = new HttpHeaders(); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java index c662bf49c1..d4eccd0772 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java @@ -1,12 +1,13 @@ package at.tuwien.endpoints; import at.tuwien.ExportResourceDto; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.ViewColumnDto; -import at.tuwien.api.database.ViewCreateDto; import at.tuwien.api.database.ViewDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.exception.*; +import at.tuwien.mapper.MariaDbMapper; import at.tuwien.service.*; import at.tuwien.validation.EndpointValidator; import io.micrometer.observation.annotation.Observed; @@ -45,6 +46,8 @@ public class ViewEndpoint extends RestEndpoint { private final ViewService viewService; private final TableService tableService; + private final MariaDbMapper mariaDbMapper; + private final SubsetService subsetService; private final MetricsService metricsService; private final StorageService storageService; private final DatabaseService databaseService; @@ -52,11 +55,14 @@ public class ViewEndpoint extends RestEndpoint { private final EndpointValidator endpointValidator; @Autowired - public ViewEndpoint(ViewService viewService, TableService tableService, MetricsService metricsService, - StorageService storageService, DatabaseService databaseService, - CredentialService credentialService, EndpointValidator endpointValidator) { + public ViewEndpoint(ViewService viewService, TableService tableService, MariaDbMapper mariaDbMapper, + SubsetService subsetService, MetricsService metricsService, StorageService storageService, + DatabaseService databaseService, CredentialService credentialService, + EndpointValidator endpointValidator) { this.viewService = viewService; this.tableService = tableService; + this.mariaDbMapper = mariaDbMapper; + this.subsetService = subsetService; this.metricsService = metricsService; this.storageService = storageService; this.databaseService = databaseService; @@ -147,7 +153,7 @@ public class ViewEndpoint extends RestEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<ViewDto> create(@NotNull @PathVariable("databaseId") Long databaseId, - @Valid @RequestBody ViewCreateDto data) throws DatabaseUnavailableException, + @Valid @RequestBody CreateViewDto data) throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, ViewMalformedException, MetadataServiceException { log.debug("endpoint create view, databaseId={}, data.name={}", databaseId, data.getName()); final DatabaseDto database = credentialService.getDatabase(databaseId); @@ -288,8 +294,10 @@ public class ViewEndpoint extends RestEndpoint { } headers.set("Access-Control-Expose-Headers", "X-Headers"); headers.set("X-Headers", String.join(",", view.getColumns().stream().map(ViewColumnDto::getInternalName).toList())); - final Dataset<Row> dataset = tableService.getData(credentialService.getDatabase(databaseId), - view.getInternalName(), timestamp, page, size, null, null); + final String query = mariaDbMapper.defaultRawSelectQuery(view.getDatabase().getInternalName(), + view.getInternalName(), timestamp, page, size); + final Dataset<Row> dataset = subsetService.getData(credentialService.getDatabase(databaseId), + query, timestamp, page, size, null, null); metricsService.countViewGetData(databaseId, viewId); return ResponseEntity.ok() .headers(headers) @@ -353,8 +361,10 @@ public class ViewEndpoint extends RestEndpoint { } credentialService.getAccess(databaseId, getId(principal)); } - final Dataset<Row> dataset = tableService.getData(credentialService.getDatabase(databaseId), - view.getInternalName(), timestamp, null, null, null, null); + final String query = mariaDbMapper.defaultRawSelectQuery(view.getDatabase().getInternalName(), + view.getInternalName(), timestamp, null, null); + final Dataset<Row> dataset = subsetService.getData(credentialService.getDatabase(databaseId), + query, timestamp, null, null, null, null); metricsService.countViewGetData(databaseId, viewId); final ExportResourceDto resource = storageService.transformDataset(dataset); final HttpHeaders headers = new HttpHeaders(); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java index 002374fe9d..53393be407 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java @@ -1,5 +1,6 @@ package at.tuwien.endpoint; +import at.tuwien.api.SortTypeDto; import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.ExecuteStatementDto; import at.tuwien.api.database.query.QueryDto; @@ -206,7 +207,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(QUERY_5_DTO); when(storageService.transformDataset(any(Dataset.class))) .thenReturn(EXPORT_RESOURCE_DTO); - when(subsetService.getData(any(DatabaseDto.class), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) .thenReturn(mock); /* test */ @@ -232,7 +233,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void findById_publicDataPublicSchemaAnonymous_fails() throws DatabaseNotFoundException, SQLException, RemoteUnavailableException, UserNotFoundException, QueryMalformedException, StorageUnavailableException, - QueryNotFoundException, MetadataServiceException, TableNotFoundException, ViewMalformedException { + QueryNotFoundException, MetadataServiceException, TableNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ @@ -240,7 +241,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_DTO); when(subsetService.findById(DATABASE_4_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(any(DatabaseDto.class), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) .thenReturn(mock); when(storageService.transformDataset(any(Dataset.class))) .thenReturn(EXPORT_RESOURCE_DTO); @@ -289,8 +290,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_1_USERNAME) public void findById_publicDataPrivateSchemaUnavailableExport_fails() throws DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException, SQLException, QueryMalformedException, - UserNotFoundException, QueryNotFoundException, TableNotFoundException, ViewMalformedException, - StorageUnavailableException { + UserNotFoundException, QueryNotFoundException, TableNotFoundException, StorageUnavailableException { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) @@ -301,7 +301,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(EXPORT_RESOURCE_DTO); doThrow(SQLException.class) .when(subsetService) - .getData(eq(DATABASE_3_DTO), eq(QUERY_5_DTO), eq(0L), eq(10L)); + .getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString()); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -324,7 +324,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) .thenReturn(DATABASE_3_DTO); - when(subsetService.getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) .thenReturn(mock); when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) .thenReturn(QUERY_5_DTO); @@ -370,7 +370,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_DTO); when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -394,7 +394,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_DTO); doThrow(SQLException.class) .when(subsetService) - .getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(0L), eq(10L)); + .getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString()); when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) .thenReturn(QUERY_5_DTO); when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_1_ID)) @@ -447,7 +447,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) .thenReturn(DATABASE_3_DTO); - when(subsetService.getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) .thenReturn(mock); when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) .thenReturn(QUERY_5_DTO); @@ -475,7 +475,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_4_DTO); when(subsetService.findById(eq(DATABASE_4_DTO), anyLong())) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(eq(DATABASE_4_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -501,7 +501,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_1_DTO); when(subsetService.findById(eq(DATABASE_1_DTO), anyLong())) .thenReturn(QUERY_1_DTO); - when(subsetService.getData(eq(DATABASE_1_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -514,7 +514,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void create_privateDataPublicSchemaAnonymous_fails() throws DatabaseNotFoundException, SQLException, MetadataServiceException, UserNotFoundException, QueryNotFoundException, QueryMalformedException, - TableNotFoundException, ViewMalformedException, ViewNotFoundException, RemoteUnavailableException { + TableNotFoundException, RemoteUnavailableException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); final ExecuteStatementDto request = ExecuteStatementDto.builder() .statement(QUERY_5_STATEMENT) @@ -525,7 +525,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_2_DTO); when(subsetService.findById(eq(DATABASE_2_DTO), anyLong())) .thenReturn(QUERY_2_DTO); - when(subsetService.getData(eq(DATABASE_2_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -540,7 +540,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void getData_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, NotAllowedException, SQLException, QueryNotFoundException, TableMalformedException, QueryMalformedException, DatabaseUnavailableException, PaginationException, MetadataServiceException, TableNotFoundException, - ViewMalformedException, ViewNotFoundException { + ViewNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ @@ -550,13 +550,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(QUERY_5_DTO); when(subsetService.reExecuteCount(DATABASE_3_DTO, QUERY_5_DTO)) .thenReturn(QUERY_5_RESULT_NUMBER); - when(subsetService.getData(eq(DATABASE_3_DTO), any(QueryDto.class), eq(0L), eq(10L))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); /* test */ - final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_3_ID, QUERY_5_ID, null, httpServletRequest, null, null); + final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_3_ID, QUERY_5_ID, null, httpServletRequest, null, null, null); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } @@ -565,7 +565,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void getData_head_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, NotAllowedException, SQLException, QueryNotFoundException, TableMalformedException, QueryMalformedException, DatabaseUnavailableException, PaginationException, MetadataServiceException, - TableNotFoundException, ViewMalformedException, ViewNotFoundException { + TableNotFoundException, ViewNotFoundException { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) @@ -578,7 +578,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn("HEAD"); /* test */ - final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_3_ID, QUERY_5_ID, null, httpServletRequest, null, null); + final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_3_ID, QUERY_5_ID, null, httpServletRequest, null, null, null); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getHeaders().get("X-Count")); assertEquals(1, response.getHeaders().get("X-Count").size()); @@ -590,7 +590,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void getData_private_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, NotAllowedException, TableMalformedException, QueryMalformedException, QueryNotFoundException, PaginationException, SQLException, - MetadataServiceException, TableNotFoundException, ViewMalformedException, ViewNotFoundException { + MetadataServiceException, TableNotFoundException, ViewNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ @@ -600,13 +600,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(QUERY_1_DTO); when(subsetService.reExecuteCount(DATABASE_1_DTO, QUERY_1_DTO)) .thenReturn(QUERY_1_RESULT_NUMBER); - when(subsetService.getData(DATABASE_1_DTO, QUERY_1_DTO, 0L, 10L)) + when(subsetService.getData(DATABASE_1_DTO, QUERY_1_STATEMENT, Instant.now(), 0L, 10L, null, null)) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); /* test */ - final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null); + final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null, null); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } @@ -622,7 +622,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, null, httpServletRequest, null, null); + subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, null, httpServletRequest, null, null, null); }); } @@ -640,7 +640,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null); + subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null, null); }); } @@ -649,7 +649,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void getData_privateHead_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, NotAllowedException, TableMalformedException, QueryMalformedException, QueryNotFoundException, PaginationException, SQLException, - MetadataServiceException, TableNotFoundException, ViewMalformedException, ViewNotFoundException { + MetadataServiceException, TableNotFoundException, ViewNotFoundException { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) @@ -662,7 +662,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn("HEAD"); /* test */ - final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null); + final ResponseEntity<List<Map<String, Object>>> response = subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null, null); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getHeaders().get("X-Count")); assertEquals(1, response.getHeaders().get("X-Count").size()); @@ -673,7 +673,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_1_USERNAME) public void getData_unavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException, SQLException, UserNotFoundException, QueryNotFoundException, MetadataServiceException, QueryMalformedException, - TableNotFoundException, ViewMalformedException { + TableNotFoundException { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) @@ -684,11 +684,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn("GET"); doThrow(SQLException.class) .when(subsetService) - .getData(DATABASE_1_DTO, QUERY_1_DTO, 0L, 10L); + .getData(DATABASE_1_DTO, QUERY_1_STATEMENT, Instant.now(), 0L, 10L, null, null); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { - subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null); + subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null, null); }); } 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 c7a74de17f..ecd745e3ee 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 @@ -8,6 +8,7 @@ import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.service.CredentialService; import at.tuwien.service.DatabaseService; +import at.tuwien.service.SubsetService; import at.tuwien.service.TableService; import at.tuwien.test.AbstractUnitTest; import jakarta.servlet.http.HttpServletRequest; @@ -59,6 +60,9 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @MockBean private TableService tableService; + @MockBean + private SubsetService subsetService; + @MockBean private DatabaseService databaseService; @@ -283,7 +287,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) .thenReturn(TABLE_8_DTO); - when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -306,7 +310,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(TABLE_8_DTO); when(tableService.getCount(eq(TABLE_8_DTO), any(Instant.class))) .thenReturn(3L); - when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); @@ -363,7 +367,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) .thenReturn(TABLE_8_DTO); doThrow(QueryMalformedException.class) - .when(tableService) + .when(subsetService) .getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null)); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -405,7 +409,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(TABLE_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(access); - when(tableService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -1146,7 +1150,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) .thenReturn(TABLE_8_DTO); - when(tableService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); /* test */ @@ -1167,7 +1171,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(TABLE_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(access); - when(tableService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); /* test */ diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java index 8586f8f92d..c69c8d338e 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java @@ -5,7 +5,7 @@ import at.tuwien.endpoints.ViewEndpoint; import at.tuwien.exception.*; import at.tuwien.service.CredentialService; import at.tuwien.service.DatabaseService; -import at.tuwien.service.TableService; +import at.tuwien.service.SubsetService; import at.tuwien.service.ViewService; import at.tuwien.test.AbstractUnitTest; import jakarta.servlet.http.HttpServletRequest; @@ -51,7 +51,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { private HttpServletRequest httpServletRequest; @MockBean - private TableService tableService; + private SubsetService subsetService; @Autowired private ViewEndpoint viewEndpoint; @@ -282,7 +282,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { .thenReturn(VIEW_1_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_1_ID)) .thenReturn(DATABASE_1_USER_1_READ_ACCESS_DTO); - when(tableService.getData(eq(DATABASE_1_DTO), eq(VIEW_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.getData(eq(DATABASE_1_DTO), eq(VIEW_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); 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 e48f0c048d..120fe49d8f 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 @@ -140,7 +140,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* ignore */ } try { - subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, 0L, 10L); + subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, 0L, 10L); } catch (Exception e) { /* ignore */ } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java index 2b7476e50f..550589df79 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java @@ -4,12 +4,12 @@ import at.tuwien.api.database.ViewColumnDto; import at.tuwien.api.database.ViewDto; 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.ColumnTypeDto; -import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; import at.tuwien.api.database.table.constraints.ConstraintsDto; -import at.tuwien.api.database.table.constraints.foreign.ForeignKeyCreateDto; +import at.tuwien.api.database.table.constraints.CreateTableConstraintsDto; +import at.tuwien.api.database.table.constraints.foreign.CreateForeignKeyDto; 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; @@ -619,8 +619,8 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { final at.tuwien.api.database.table.internal.TableCreateDto request = TableCreateDto.builder() .name("missing_foreign_key") .columns(List.of()) - .constraints(ConstraintsCreateDto.builder() - .foreignKeys(List.of(ForeignKeyCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() + .foreignKeys(List.of(CreateForeignKeyDto.builder() .columns(List.of("i_do_not_exist")) .referencedTable("neither_do_i") .referencedColumns(List.of("behold")) @@ -639,27 +639,27 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { TableExistsException { final at.tuwien.api.database.table.internal.TableCreateDto request = TableCreateDto.builder() .name("composite_primary_key") - .columns(List.of(ColumnCreateDto.builder() + .columns(List.of(CreateTableColumnDto.builder() .name("name") .type(ColumnTypeDto.VARCHAR) .size(255L) .nullAllowed(false) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("lat") .type(ColumnTypeDto.DECIMAL) .size(10L) .d(10L) .nullAllowed(false) .build(), - ColumnCreateDto.builder() + CreateTableColumnDto.builder() .name("lng") .type(ColumnTypeDto.DECIMAL) .size(10L) .d(10L) .nullAllowed(false) .build())) - .constraints(ConstraintsCreateDto.builder() + .constraints(CreateTableConstraintsDto.builder() .primaryKey(Set.of("lat", "lng")) .foreignKeys(List.of()) .checks(Set.of()) 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 d3af548164..0e89f79cb1 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 @@ -4,9 +4,9 @@ import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.TupleDeleteDto; import at.tuwien.api.database.table.TupleDto; import at.tuwien.api.database.table.TupleUpdateDto; -import at.tuwien.api.database.table.columns.ColumnCreateDto; import at.tuwien.api.database.table.columns.ColumnDto; import at.tuwien.api.database.table.columns.ColumnTypeDto; +import at.tuwien.api.database.table.columns.CreateTableColumnDto; import at.tuwien.exception.QueryMalformedException; import at.tuwien.exception.TableMalformedException; import at.tuwien.utils.MariaDbUtil; @@ -237,7 +237,7 @@ public interface MariaDbMapper { * @param data The column definition. * @return The MySQL string. */ - default String columnTypeDtoToDataType(ColumnCreateDto data) { + default String columnTypeDtoToDataType(CreateTableColumnDto data) { return switch (data.getType()) { case CHAR -> "CHAR(" + Objects.requireNonNullElse(data.getSize(), "1") + ")"; case VARCHAR -> "VARCHAR(" + Objects.requireNonNullElse(data.getSize(), "255") + ")"; @@ -260,7 +260,7 @@ public interface MariaDbMapper { }; } - default String columnCreateDtoToPrimaryKeyLengthSpecification(ColumnCreateDto data) { + default String columnCreateDtoToPrimaryKeyLengthSpecification(CreateTableColumnDto data) { if (EnumSet.of(ColumnTypeDto.BLOB, ColumnTypeDto.TEXT).contains(data.getType())) { return "(" + Objects.requireNonNullElse(data.getIndexLength(), 255) + ")"; } @@ -309,7 +309,7 @@ public interface MariaDbMapper { .append("` ("); log.trace("primary key column(s) exist: {}", data.getConstraints().getPrimaryKey()); final int[] idx = {0}; - for (ColumnCreateDto column : data.getColumns()) { + for (CreateTableColumnDto column : data.getColumns()) { stringBuilder.append(idx[0]++ > 0 ? ", " : "") .append("`") .append(nameToInternalName(column.getName())) @@ -336,11 +336,11 @@ public interface MariaDbMapper { .getPrimaryKey() .stream() .map(c -> { - final Optional<ColumnCreateDto> optional = data.getColumns() + final Optional<CreateTableColumnDto> optional = data.getColumns() .stream() .filter(cc -> cc.getName().equals(c)) .findFirst(); - log.trace("lookup {} in columns: {}", c, data.getColumns().stream().map(ColumnCreateDto::getName).toList()); + log.trace("lookup {} in columns: {}", c, data.getColumns().stream().map(CreateTableColumnDto::getName).toList()); return "`" + nameToInternalName(c) + "`" + columnCreateDtoToPrimaryKeyLengthSpecification(optional.get()); }) .toArray(String[]::new))) @@ -725,6 +725,30 @@ public interface MariaDbMapper { } } + default String rawSelectQuery(String query, Instant timestamp, Long page, Long size) { + /* query check (this is enforced by the db also) */ + final StringBuilder statement = new StringBuilder("SELECT * FROM (") + .append(query); + statement.append(")"); + if (timestamp != null) { + statement.append(" FOR SYSTEM_TIME AS OF TIMESTAMP '") + .append(mariaDbFormatter.format(timestamp)) + .append("'"); + } + statement.append(" as tbl"); + /* pagination */ + if (size != null && page != null) { + log.trace("pagination size/limit of {}", size); + statement.append(" LIMIT ") + .append(size); + log.trace("pagination page/offset of {}", page); + statement.append(" OFFSET ") + .append(page * size); + } + log.trace("mapped select query: {}", statement); + return statement.toString(); + } + default String defaultRawSelectQuery(String databaseName, String tableOrViewName, Instant timestamp, Long page, Long size) { /* query check (this is enforced by the db also) */ diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java index 5a120f44d2..314148663b 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java @@ -1,7 +1,7 @@ package at.tuwien.service; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.database.ViewCreateDto; import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.internal.TableCreateDto; @@ -48,7 +48,7 @@ public interface DatabaseService { * @throws SQLException * @throws ViewMalformedException */ - ViewDto createView(DatabaseDto database, ViewCreateDto data) throws SQLException, + ViewDto createView(DatabaseDto database, CreateViewDto data) throws SQLException, ViewMalformedException; /** diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java index b2de5cecca..e2fc560027 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/SubsetService.java @@ -1,5 +1,6 @@ package at.tuwien.service; +import at.tuwien.api.SortTypeDto; import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.exception.*; @@ -13,22 +14,6 @@ import java.util.UUID; public interface SubsetService { - /** - * Retrieve data from a subset in a database and optionally paginate with number of page and size of results. - * - * @param database The database. - * @param subset The subset. - * @param page The page number. - * @param size Te result size. - * @return The data. - * @throws ViewMalformedException The view is malformed. - * @throws SQLException The connection to the database could not be established. - * @throws QueryMalformedException The mapped query produced a database error. - * @throws TableNotFoundException The database table is malformed. - */ - Dataset<Row> getData(DatabaseDto database, QueryDto subset, Long page, Long size) - throws ViewMalformedException, SQLException, QueryMalformedException, TableNotFoundException; - /** * Creates a subset from the given statement at given time in the given database. * @@ -55,6 +40,21 @@ public interface SubsetService { Long reExecuteCount(DatabaseDto database, QueryDto query) throws TableMalformedException, SQLException, QueryMalformedException; + /** + * Retrieve data from a subset in a database and optionally paginate with number of page and size of results. + * + * @param database The database. + * @param query The query statements. + * @param page The page number. + * @param size Te result size. + * @return The data. + * @throws QueryMalformedException The mapped query produced a database error. + * @throws TableNotFoundException The database table is malformed. + */ + Dataset<Row> getData(DatabaseDto database, String query, Instant timestamp, Long page, Long size, + SortTypeDto sortDirection, String sortColumn) throws QueryMalformedException, + TableNotFoundException; + /** * Finds all queries in the query store of the given database id and query id. * diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java index 636e12ceb6..f664a82cf3 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java @@ -1,12 +1,8 @@ package at.tuwien.service; -import at.tuwien.api.SortTypeDto; -import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.table.*; import at.tuwien.exception.*; -import org.apache.spark.sql.Dataset; -import org.apache.spark.sql.Row; import java.sql.SQLException; import java.time.Instant; @@ -122,8 +118,4 @@ public interface TableService { */ void updateTuple(TableDto table, TupleUpdateDto data) throws SQLException, QueryMalformedException, TableMalformedException; - - Dataset<Row> getData(DatabaseDto database, String tableOrView, Instant timestamp, - Long page, Long size, SortTypeDto sortDirection, String sortColumn) - throws QueryMalformedException, TableNotFoundException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java index c21d37721b..1786ca5cb4 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java @@ -49,15 +49,11 @@ public abstract class DataConnector { } public String getSparkUrl(TableDto table) { - return getSparkUrl(table.getDatabase().getContainer(), null); + return getSparkUrl(table.getDatabase().getContainer(), table.getDatabase().getInternalName()); } public String getSparkUrl(DatabaseDto databaseDto) { - return getSparkUrl(databaseDto.getContainer(), null); - } - - public String getSparkUrl(ContainerDto container) { - return getSparkUrl(container, null); + return getSparkUrl(databaseDto.getContainer(), databaseDto.getInternalName()); } public String getJdbcUrl(ContainerDto container, String databaseName) { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java index c4afe44287..d733800a36 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java @@ -1,7 +1,7 @@ package at.tuwien.service.impl; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.database.ViewCreateDto; import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.constraints.unique.UniqueDto; @@ -146,7 +146,7 @@ public class DatabaseServiceMariaDbImpl extends DataConnector implements Databas } @Override - public ViewDto createView(DatabaseDto database, ViewCreateDto data) throws SQLException, + public ViewDto createView(DatabaseDto database, CreateViewDto data) throws SQLException, ViewMalformedException { final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java index 0a208b2890..bdfdb14838 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/SubsetServiceMariaDbImpl.java @@ -1,7 +1,7 @@ package at.tuwien.service.impl; +import at.tuwien.api.SortTypeDto; import at.tuwien.api.database.DatabaseDto; -import at.tuwien.api.database.ViewCreateDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.identifier.IdentifierBriefDto; import at.tuwien.api.identifier.IdentifierTypeDto; @@ -9,14 +9,13 @@ import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.mapper.DataMapper; import at.tuwien.mapper.MariaDbMapper; -import at.tuwien.mapper.MetadataMapper; -import at.tuwien.service.DatabaseService; import at.tuwien.service.SubsetService; -import at.tuwien.service.TableService; import com.mchange.v2.c3p0.ComboPooledDataSource; import lombok.extern.log4j.Log4j2; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; +import org.apache.spark.sql.SparkSession; +import org.apache.spark.sql.catalyst.ExtendedAnalysisException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -31,40 +30,41 @@ import java.util.UUID; public class SubsetServiceMariaDbImpl extends DataConnector implements SubsetService { private final DataMapper dataMapper; - private final TableService tableService; + private final SparkSession sparkSession; private final MariaDbMapper mariaDbMapper; - private final MetadataMapper metadataMapper; - private final DatabaseService databaseService; private final MetadataServiceGateway metadataServiceGateway; @Autowired - public SubsetServiceMariaDbImpl(DataMapper dataMapper, TableService tableService, MariaDbMapper mariaDbMapper, - MetadataMapper metadataMapper, DatabaseService databaseService, + public SubsetServiceMariaDbImpl(DataMapper dataMapper, MariaDbMapper mariaDbMapper, SparkSession sparkSession, MetadataServiceGateway metadataServiceGateway) { this.dataMapper = dataMapper; - this.tableService = tableService; + this.sparkSession = sparkSession; this.mariaDbMapper = mariaDbMapper; - this.metadataMapper = metadataMapper; - this.databaseService = databaseService; this.metadataServiceGateway = metadataServiceGateway; } @Override - public Dataset<Row> getData(DatabaseDto database, QueryDto subset, Long page, Long size) - throws ViewMalformedException, SQLException, QueryMalformedException, TableNotFoundException { - final String viewName = metadataMapper.queryDtoToViewName(subset); - if (!databaseService.existsView(database, viewName)) { - log.warn("Missing internal view {} for subset with id {}: create it from subset query", viewName, subset.getId()); - databaseService.createView(database, ViewCreateDto.builder() - .isPublic(false) - .isSchemaPublic(false) - .name(viewName) - .query(subset.getQuery()) - .build()); - } else { - log.debug("internal view {}.{} for subset with id {} exists", database.getInternalName(), viewName, subset.getId()); + public Dataset<Row> getData(DatabaseDto database, String query, Instant timestamp, Long page, Long size, + SortTypeDto sortDirection, String sortColumn) + throws QueryMalformedException, TableNotFoundException { + try { + return sparkSession.read() + .format("jdbc") + .option("user", database.getContainer().getUsername()) + .option("password", database.getContainer().getPassword()) + .option("url", getSparkUrl(database)) + .option("query", query) + .load(); + } catch (Exception e) { + if (e instanceof ExtendedAnalysisException exception) { + if (exception.getSimpleMessage().contains("TABLE_OR_VIEW_NOT_FOUND")) { + log.error("Failed to find named reference: {}", exception.getSimpleMessage()); + throw new TableNotFoundException("Failed to find named reference: " + exception.getSimpleMessage()) /* remove throwable on purpose, clutters the output */; + } + } + log.error("Malformed query: {}", e.getMessage()); + throw new QueryMalformedException("Malformed query: " + e.getMessage(), e); } - return tableService.getData(database, viewName, subset.getExecution(), page, size, null, null); } @Override diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java index 4d4121f060..466f7539fd 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java @@ -1,7 +1,5 @@ package at.tuwien.service.impl; -import at.tuwien.api.SortTypeDto; -import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.table.*; import at.tuwien.api.database.table.columns.ColumnDto; @@ -16,8 +14,10 @@ import at.tuwien.service.TableService; import at.tuwien.utils.MariaDbUtil; import com.mchange.v2.c3p0.ComboPooledDataSource; import lombok.extern.log4j.Log4j2; -import org.apache.spark.sql.*; -import org.apache.spark.sql.catalyst.ExtendedAnalysisException; +import org.apache.spark.sql.AnalysisException; +import org.apache.spark.sql.Dataset; +import org.apache.spark.sql.Row; +import org.apache.spark.sql.SaveMode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -36,16 +36,14 @@ import java.util.Properties; public class TableServiceMariaDbImpl extends DataConnector implements TableService { private final DataMapper dataMapper; - private final SparkSession sparkSession; private final MariaDbMapper mariaDbMapper; private final StorageService storageService; private final DatabaseService databaseService; @Autowired - public TableServiceMariaDbImpl(DataMapper dataMapper, SparkSession sparkSession, MariaDbMapper mariaDbMapper, - StorageService storageService, DatabaseService databaseService) { + public TableServiceMariaDbImpl(DataMapper dataMapper, MariaDbMapper mariaDbMapper, StorageService storageService, + DatabaseService databaseService) { this.dataMapper = dataMapper; - this.sparkSession = sparkSession; this.mariaDbMapper = mariaDbMapper; this.storageService = storageService; this.databaseService = databaseService; @@ -368,30 +366,4 @@ public class TableServiceMariaDbImpl extends DataConnector implements TableServi .getColumnType(); } - @Override - public Dataset<Row> getData(DatabaseDto database, String tableOrView, Instant timestamp, - Long page, Long size, SortTypeDto sortDirection, String sortColumn) - throws QueryMalformedException, TableNotFoundException { - try { - return sparkSession.read() - .format("jdbc") - .option("user", database.getContainer().getUsername()) - .option("password", database.getContainer().getPassword()) - .option("url", getSparkUrl(database)) - .option("query", mariaDbMapper.defaultRawSelectQuery(database.getInternalName(), tableOrView, - timestamp, page, size)) - .load(); - - } catch (Exception e) { - if (e instanceof ExtendedAnalysisException exception) { - if (exception.getSimpleMessage().contains("TABLE_OR_VIEW_NOT_FOUND")) { - log.error("Failed to find named reference: {}", exception.getSimpleMessage()); - throw new TableNotFoundException("Failed to find named reference: " + exception.getSimpleMessage()) /* remove throwable on purpose, clutters the output */; - } - } - log.error("Malformed query: {}", e.getMessage()); - throw new QueryMalformedException("Malformed query: " + e.getMessage(), e); - } - } - } -- GitLab From f2e64511686357c28f8418b9681a370167d3aeaa Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Fri, 24 Jan 2025 19:36:56 +0100 Subject: [PATCH 19/19] Fixed the tests with *magic* Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at> --- .docs/.openapi/api-data.yaml | 372 +++--- .docs/.openapi/api-metadata.yaml | 814 ++++++------- .docs/.openapi/api.yaml | 1029 +++++++++-------- .../at/tuwien/endpoints/SubsetEndpoint.java | 12 +- .../tuwien/validation/EndpointValidator.java | 10 +- .../endpoint/AccessEndpointUnitTest.java | 30 +- .../endpoint/DatabaseEndpointUnitTest.java | 22 +- .../endpoint/SubsetEndpointUnitTest.java | 260 ++--- .../endpoint/TableEndpointUnitTest.java | 189 +-- .../tuwien/endpoint/ViewEndpointUnitTest.java | 57 +- .../MetadataServiceGatewayUnitTest.java | 12 +- .../DefaultListenerIntegrationTest.java | 14 +- .../listener/DefaultListenerUnitTest.java | 14 +- .../at/tuwien/mvc/SubsetEndpointMvcTest.java | 12 +- .../ContainerServiceIntegrationTest.java | 38 +- .../service/CredentialServiceUnitTest.java | 30 +- .../DatabaseServiceIntegrationTest.java | 63 +- .../service/QueueServiceIntegrationTest.java | 12 +- .../service/SubsetServiceIntegrationTest.java | 22 +- .../service/TableServiceIntegrationTest.java | 167 +-- .../service/ViewServiceIntegrationTest.java | 4 +- .../at/tuwien/listener/DefaultListener.java | 11 +- .../java/at/tuwien/mapper/DataMapper.java | 1 + .../java/at/tuwien/mapper/MariaDbMapper.java | 4 +- .../java/at/tuwien/mapper/MetadataMapper.java | 2 - .../at/tuwien/service/DatabaseService.java | 3 - .../at/tuwien/service/impl/DataConnector.java | 4 +- .../impl/DatabaseServiceMariaDbImpl.java | 29 +- .../foreign/ForeignKeyBriefDto.java | 1 - .../java/at/tuwien/test/AbstractUnitTest.java | 28 +- .../main/java/at/tuwien/test/BaseTest.java | 91 +- 31 files changed, 1650 insertions(+), 1707 deletions(-) diff --git a/.docs/.openapi/api-data.yaml b/.docs/.openapi/api-data.yaml index fad4c0fd74..bd14ba5505 100644 --- a/.docs/.openapi/api-data.yaml +++ b/.docs/.openapi/api-data.yaml @@ -59,35 +59,26 @@ paths: type: string format: date-time responses: - "200": - description: Retrieved view data - headers: - Access-Control-Expose-Headers: - description: Expose `X-Count` custom header - required: true - style: simple - X-Count: - description: Number of rows - required: true - style: simple + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: - type: string - "400": - description: Request pagination is malformed + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find view in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + "409": + description: View schema could not be mapped content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find view in metadata database + "400": + description: Request pagination is malformed content: application/json: schema: @@ -98,12 +89,21 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "409": - description: View schema could not be mapped + "200": + description: Retrieved view data + headers: + Access-Control-Expose-Headers: + description: Expose `X-Count` custom header + required: true + style: simple + X-Count: + description: Number of rows + required: true + style: simple content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" + type: string security: - basicAuth: [] - bearerAuth: [] @@ -147,35 +147,26 @@ paths: type: string format: date-time responses: - "200": - description: Retrieved view data - headers: - Access-Control-Expose-Headers: - description: Expose `X-Count` custom header - required: true - style: simple - X-Count: - description: Number of rows - required: true - style: simple + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: - type: string - "400": - description: Request pagination is malformed + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find view in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + "409": + description: View schema could not be mapped content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find view in metadata database + "400": + description: Request pagination is malformed content: application/json: schema: @@ -186,12 +177,21 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "409": - description: View schema could not be mapped + "200": + description: Retrieved view data + headers: + Access-Control-Expose-Headers: + description: Expose `X-Count` custom header + required: true + style: simple + X-Count: + description: Number of rows + required: true + style: simple content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" + type: string security: - basicAuth: [] - bearerAuth: [] @@ -237,8 +237,8 @@ paths: type: integer format: int64 responses: - "403": - description: Not allowed to get table data + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: @@ -270,8 +270,8 @@ paths: application/json: schema: type: string - "400": - description: Request pagination or table data select query is malformed + "403": + description: Not allowed to get table data content: application/json: schema: @@ -312,8 +312,8 @@ paths: $ref: "#/components/schemas/TupleUpdateDto" required: true responses: - "403": - description: Update table data not allowed + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: @@ -324,16 +324,16 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Updated table data "404": description: Failed to find table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Request pagination or table data select query is malformed + "202": + description: Updated table data + "403": + description: Update table data not allowed content: application/json: schema: @@ -374,30 +374,30 @@ paths: $ref: "#/components/schemas/TupleDto" required: true responses: - "503": - description: Failed to establish connection with the metadata service or - storage service + "404": + description: Failed to find table in metadata database or blob in storage + service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Create table data not allowed + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find table in metadata database or blob in storage - service + "201": + description: Created table data + "503": + description: Failed to establish connection with the metadata service or + storage service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Created table data - "400": - description: Request pagination or table data select query is malformed + "403": + description: Create table data not allowed content: application/json: schema: @@ -438,8 +438,8 @@ paths: $ref: "#/components/schemas/TupleDeleteDto" required: true responses: - "403": - description: Delete table data not allowed + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: @@ -450,16 +450,16 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Deleted table data "404": description: Failed to find table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Request pagination or table data select query is malformed + "202": + description: Deleted table data + "403": + description: Delete table data not allowed content: application/json: schema: @@ -508,8 +508,8 @@ paths: type: integer format: int64 responses: - "403": - description: Not allowed to get table data + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: @@ -541,8 +541,8 @@ paths: application/json: schema: type: string - "400": - description: Request pagination or table data select query is malformed + "403": + description: Not allowed to get table data content: application/json: schema: @@ -573,6 +573,12 @@ paths: schema: type: integer format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time - name: page in: query required: false @@ -586,31 +592,12 @@ paths: type: integer format: int64 responses: - "403": - description: Not allowed to retrieve subset data - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database in metadata database or query in query - store of the data database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "503": description: Failed to communicate with database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Invalid pagination - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Retrieved subset data headers: @@ -632,6 +619,25 @@ paths: application/json: schema: type: string + "400": + description: Invalid pagination + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to retrieve subset data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -657,6 +663,12 @@ paths: schema: type: integer format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time - name: page in: query required: false @@ -670,31 +682,12 @@ paths: type: integer format: int64 responses: - "403": - description: Not allowed to retrieve subset data - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database in metadata database or query in query - store of the data database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "503": description: Failed to communicate with database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Invalid pagination - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Retrieved subset data headers: @@ -716,6 +709,25 @@ paths: application/json: schema: type: string + "400": + description: Invalid pagination + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to retrieve subset data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -746,21 +758,20 @@ paths: $ref: "#/components/schemas/QueryPersistDto" required: true responses: - "202": - description: Persisted subset + "503": + description: Failed to communicate with database content: application/json: schema: - $ref: "#/components/schemas/QueryDto" - "404": - description: Failed to find database in metadata database or query in query - store of the data database + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Persisted subset content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to communicate with database + $ref: "#/components/schemas/QueryDto" + "403": + description: Not allowed to persist subset content: application/json: schema: @@ -771,8 +782,9 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to persist subset + "404": + description: Failed to find database in metadata database or query in query + store of the data database content: application/json: schema: @@ -823,26 +835,26 @@ paths: responses: "202": description: Imported dataset successfully - "403": - description: Import table dataset not allowed + "503": + description: Failed to establish connection with the metadata service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Dataset and/or query are malformed + "404": + description: Failed to find table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to establish connection with the metadata service + "403": + description: Import table dataset not allowed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find table in metadata database + "400": + description: Dataset and/or query are malformed content: application/json: schema: @@ -874,15 +886,15 @@ paths: schema: type: boolean responses: - "404": - description: Failed to find database in metadata database or query in query - store of the data database + "503": + description: Failed to communicate with database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to find subsets + "404": + description: Failed to find database in metadata database or query in query + store of the data database content: application/json: schema: @@ -895,8 +907,8 @@ paths: type: array items: $ref: "#/components/schemas/QueryDto" - "503": - description: Failed to communicate with database + "403": + description: Not allowed to find subsets content: application/json: schema: @@ -945,15 +957,14 @@ paths: $ref: "#/components/schemas/ExecuteStatementDto" required: true responses: - "417": - description: Failed to insert query into query store of data database + "201": + description: Created subset content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database in metadata database or query in query - store of the data database + type: string + "503": + description: Failed to communicate with database content: application/json: schema: @@ -970,20 +981,21 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Created subset + "404": + description: Failed to find database in metadata database or query in query + store of the data database content: application/json: schema: - type: string - "503": - description: Failed to communicate with database + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Malformed select query content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Malformed select query + "417": + description: Failed to insert query into query store of data database content: application/json: schema: @@ -1020,12 +1032,6 @@ paths: type: string format: date-time responses: - "404": - description: Failed to find view in metadata database or export dataset - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "503": description: Failed to establish connection with the metadata service content: @@ -1038,6 +1044,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find view in metadata database or export dataset + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Exported view data content: @@ -1083,8 +1095,8 @@ paths: type: integer format: int64 responses: - "404": - description: Failed to find table history in data database + "403": + description: Find table history not allowed content: application/json: schema: @@ -1095,8 +1107,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Find table history not allowed + "404": + description: Failed to find table history in data database content: application/json: schema: @@ -1147,8 +1159,8 @@ paths: type: string format: date-time responses: - "403": - description: Export table data not allowed + "400": + description: Request pagination or table data select query is malformed content: application/json: schema: @@ -1159,12 +1171,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find table in metadata database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Exported table data content: @@ -1172,8 +1178,14 @@ paths: schema: type: string format: binary - "400": - description: Request pagination or table data select query is malformed + "404": + description: Failed to find table in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Export table data not allowed content: application/json: schema: @@ -1217,9 +1229,8 @@ paths: type: string format: date-time responses: - "404": - description: Failed to find database in metadata database or query in query - store of the data database + "503": + description: Failed to communicate with database content: application/json: schema: @@ -1236,12 +1247,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to communicate with database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Found subset content: @@ -1249,6 +1254,13 @@ paths: schema: $ref: "#/components/schemas/QueryDto" text/csv: {} + "404": + description: Failed to find database in metadata database or query in query + store of the data database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "400": description: Malformed select query content: diff --git a/.docs/.openapi/api-metadata.yaml b/.docs/.openapi/api-metadata.yaml index 6792f0f0e9..18fb71db29 100644 --- a/.docs/.openapi/api-metadata.yaml +++ b/.docs/.openapi/api-metadata.yaml @@ -64,18 +64,25 @@ paths: $ref: "#/components/schemas/CreateDatabaseDto" required: true responses: - "409": - description: Query store could not be created + "403": + description: Database create permission is missing or grant permissions + at broker service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Created a new database + "423": + description: Database quota exceeded content: application/json: schema: - $ref: "#/components/schemas/DatabaseBriefDto" + $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Database create query is malformed or image is not supported + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "503": description: Failed to save in search service content: @@ -94,21 +101,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Database create permission is missing or grant permissions - at broker service failed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "423": - description: Database quota exceeded + "201": + description: Created a new database content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Database create query is malformed or image is not supported + $ref: "#/components/schemas/DatabaseBriefDto" + "409": + description: Query store could not be created content: application/json: schema: @@ -173,6 +173,12 @@ paths: type: string format: uuid responses: + "200": + description: Found database access + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseAccessDto" "403": description: No access to this database content: @@ -185,12 +191,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Found database access - content: - application/json: - schema: - $ref: "#/components/schemas/DatabaseAccessDto" security: - bearerAuth: [] - basicAuth: [] @@ -228,29 +228,29 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Database or user not found + "502": + description: Access could not be updated due to connection error in the + data service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Modify access query or database connection is malformed + "503": + description: Access could not be updated in the data service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" "202": description: Modified access - "502": - description: Access could not be updated due to connection error in the - data service + "404": + description: Database or user not found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Access could not be updated in the data service + "400": + description: Modify access query or database connection is malformed content: application/json: schema: @@ -291,8 +291,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Database or user not found + "503": + description: Access could not be created in the data service content: application/json: schema: @@ -309,8 +309,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Access could not be created in the data service + "404": + description: Database or user not found content: application/json: schema: @@ -351,6 +351,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Revoke of access not permitted as no access was found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted access "400": description: Modify access query or database connection is malformed content: @@ -363,20 +371,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Deleted access "503": description: Access could not be revoked in the data service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Revoke of access not permitted as no access was found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -404,6 +404,12 @@ paths: type: string format: uuid responses: + "200": + description: Found database access + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseAccessDto" "403": description: No access to this database content: @@ -416,12 +422,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Found database access - content: - application/json: - schema: - $ref: "#/components/schemas/DatabaseAccessDto" security: - bearerAuth: [] - basicAuth: [] @@ -443,8 +443,8 @@ paths: type: string format: uuid responses: - "403": - description: Find user is not permitted + "404": + description: User was not found content: application/json: schema: @@ -455,8 +455,8 @@ paths: application/json: schema: $ref: "#/components/schemas/UserDto" - "404": - description: User was not found + "403": + description: Find user is not permitted content: application/json: schema: @@ -484,26 +484,26 @@ paths: $ref: "#/components/schemas/UserUpdateDto" required: true responses: - "400": - description: Modify user query is malformed + "202": + description: Modified user information content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database/user in metadata database + $ref: "#/components/schemas/UserDto" + "403": + description: Not allowed to modify user metadata content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Modified user information + "400": + description: Modify user query is malformed content: application/json: schema: - $ref: "#/components/schemas/UserDto" - "403": - description: Not allowed to modify user metadata + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database/user in metadata database content: application/json: schema: @@ -532,14 +532,20 @@ paths: $ref: "#/components/schemas/UserPasswordDto" required: true responses: - "403": - description: Not allowed to change foreign user password + "400": + description: Invalid password payload content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" "202": description: Modified user password + "403": + description: Not allowed to change foreign user password + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "404": description: Failed to find database/user in metadata database content: @@ -558,12 +564,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Invalid password payload - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -581,8 +581,8 @@ paths: $ref: "#/components/schemas/RefreshTokenRequestDto" required: true responses: - "502": - description: Connection to auth service failed + "403": + description: Not allowed content: application/json: schema: @@ -599,8 +599,8 @@ paths: application/json: schema: $ref: "#/components/schemas/TokenDto" - "403": - description: Not allowed + "502": + description: Connection to auth service failed content: application/json: schema: @@ -618,18 +618,31 @@ paths: $ref: "#/components/schemas/LoginRequestDto" required: true responses: - "202": - description: Obtained user token + "404": + description: Failed to find user in auth database content: application/json: schema: - $ref: "#/components/schemas/TokenDto" + $ref: "#/components/schemas/ApiErrorDto" + "428": + description: Account is not fully setup in auth service (requires password + change?) + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "400": description: Invalid login request content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Obtained user token + content: + application/json: + schema: + $ref: "#/components/schemas/TokenDto" "403": description: Not allowed to get token content: @@ -648,19 +661,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find user in auth database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "428": - description: Account is not fully setup in auth service (requires password - change?) - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" /api/ontology/{ontologyId}: get: tags: @@ -737,16 +737,16 @@ paths: type: integer format: int64 responses: - "202": - description: Deleted ontology successfully - content: - application/json: {} "404": description: Could not find ontology content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted ontology successfully + content: + application/json: {} security: - bearerAuth: [] - basicAuth: [] @@ -800,16 +800,16 @@ paths: type: integer format: int64 responses: - "202": - description: Deleted message - content: - application/json: {} "404": description: Could not find message content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted message + content: + application/json: {} security: - bearerAuth: [] - basicAuth: [] @@ -828,18 +828,18 @@ paths: type: integer format: int64 responses: - "404": - description: Image could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Found image content: application/json: schema: $ref: "#/components/schemas/ImageDto" + "404": + description: Image could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" put: tags: - image-endpoint @@ -929,20 +929,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to find in data service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Identifier could not be found + "502": + description: Connection to data service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "406": - description: Failed to find acceptable representation + "503": + description: Failed to find in data service content: application/json: schema: @@ -975,8 +969,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to data service failed + "404": + description: Identifier could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "406": + description: Failed to find acceptable representation content: application/json: schema: @@ -1004,12 +1004,18 @@ paths: $ref: "#/components/schemas/IdentifierSaveDto" required: true responses: - "202": - description: Saved identifier + "400": + description: Identifier form contains invalid request data content: application/json: schema: - $ref: "#/components/schemas/IdentifierDto" + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Failed to find database, table or view" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "403": description: Insufficient access rights or authorities content: @@ -1028,18 +1034,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Identifier form contains invalid request data + "202": + description: Saved identifier content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Failed to find database, table or view" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" + $ref: "#/components/schemas/IdentifierDto" security: - bearerAuth: [] - basicAuth: [] @@ -1063,14 +1063,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Deleted identifier "404": description: Identifier or database could not be found content: @@ -1083,6 +1075,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted identifier security: - bearerAuth: [] - basicAuth: [] @@ -1102,6 +1102,18 @@ paths: type: integer format: int64 responses: + "400": + description: Identifier form contains invalid request data + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Failed to find database, table or view" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "403": description: Insufficient access rights or authorities content: @@ -1126,18 +1138,6 @@ paths: application/json: schema: $ref: "#/components/schemas/IdentifierDto" - "400": - description: Identifier form contains invalid request data - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Failed to find database, table or view" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1169,32 +1169,32 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Visibility modification is not permitted + "202": + description: Visibility modified successfully content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database in metadata database + $ref: "#/components/schemas/DatabaseBriefDto" + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Visibility modified successfully + "404": + description: Failed to find database in metadata database content: application/json: schema: - $ref: "#/components/schemas/DatabaseBriefDto" - "502": - description: Connection to search service failed + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Visibility modification is not permitted content: application/json: schema: @@ -1223,6 +1223,18 @@ paths: type: integer format: int64 responses: + "403": + description: Find view is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, view or user could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Find view successfully headers: @@ -1254,18 +1266,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ViewDto" - "403": - description: Find view is not permitted - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Database, view or user could not be found" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1296,12 +1296,6 @@ paths: $ref: "#/components/schemas/ViewUpdateDto" required: true responses: - "400": - description: Update view query is malformed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "403": description: Update not allowed content: @@ -1332,6 +1326,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Update view query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1357,8 +1357,14 @@ paths: responses: "202": description: Delete view successfully - "403": - description: Deletion not allowed + "423": + description: Delete view resulted in an invalid query statement + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Database, view or user could not be found" content: application/json: schema: @@ -1381,14 +1387,8 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "423": - description: Delete view resulted in an invalid query statement - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Database, view or user could not be found" + "403": + description: Deletion not allowed content: application/json: schema: @@ -1420,6 +1420,18 @@ paths: type: integer format: int64 responses: + "502": + description: Failed to establish connection with broker service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: "Table, database or container could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "503": description: Failed to obtain queue information from broker service content: @@ -1448,18 +1460,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: "Table, database or container could not be found" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Failed to establish connection with broker service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1495,18 +1495,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Update table visibility not permitted - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Table could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "503": description: Failed to save in search service content: @@ -1525,6 +1513,18 @@ paths: application/json: schema: $ref: "#/components/schemas/TableBriefDto" + "403": + description: Update table visibility not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Table could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1558,12 +1558,6 @@ paths: $ref: "#/components/schemas/ApiErrorDto" "202": description: Delete table successfully - "403": - description: Access to the database is forbidden - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "404": description: "Table, database or container could not be found" content: @@ -1582,6 +1576,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Access to the database is forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1608,6 +1608,12 @@ paths: type: integer format: int64 responses: + "400": + description: Failed to map column statistic to known columns + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "503": description: Failed to save in search service content: @@ -1632,12 +1638,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Failed to map column statistic to known columns - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "202": description: Updated table statistics successfully security: @@ -1679,25 +1679,18 @@ paths: $ref: "#/components/schemas/ColumnSemanticsUpdateDto" required: true responses: - "400": - description: Update semantic concept query is malformed or update unit of - measurement query is malformed - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Access to the database is forbidden + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "202": + description: Updated column semantics successfully content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" + $ref: "#/components/schemas/ColumnDto" "404": description: Failed to find user/table/database/ontology in metadata database content: @@ -1710,12 +1703,19 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Updated column semantics successfully + "400": + description: Update semantic concept query is malformed or update unit of + measurement query is malformed content: application/json: schema: - $ref: "#/components/schemas/ColumnDto" + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Access to the database is forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1747,18 +1747,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Transfer of ownership is not permitted - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Database or user could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "202": description: Transfer of ownership was successful content: @@ -1777,6 +1765,18 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Transfer of ownership is not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1797,12 +1797,6 @@ paths: type: integer format: int64 responses: - "404": - description: Failed to find database in metadata database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Refreshed database views metadata content: @@ -1827,6 +1821,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Failed to find database in metadata database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1853,14 +1853,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to refresh table metadata + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to fin user/database in metadata database + "502": + description: Connection to search service failed content: application/json: schema: @@ -1871,14 +1871,14 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseBriefDto" - "503": - description: Failed to save in search service + "403": + description: Not allowed to refresh table metadata content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "404": + description: Failed to fin user/database in metadata database content: application/json: schema: @@ -1901,12 +1901,6 @@ paths: type: integer format: int64 responses: - "404": - description: Database or user could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: View of image was successful content: @@ -1916,6 +1910,12 @@ paths: items: type: string format: byte + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -1945,19 +1945,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "410": - description: File was not found in the Storage Service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Modify of image was successful - content: - application/json: - schema: - $ref: "#/components/schemas/DatabaseBriefDto" + $ref: "#/components/schemas/ApiErrorDto" "404": description: Database could not be found content: @@ -1976,6 +1964,18 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Modify of image was successful + content: + application/json: + schema: + $ref: "#/components/schemas/DatabaseBriefDto" + "410": + description: File was not found in the Storage Service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2017,22 +2017,6 @@ paths: $ref: "#/components/schemas/CreateUserDto" required: true responses: - "417": - description: User with e-mail already exists - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Parameters are not well-formed (likely email) - content: - application/json: {} - "503": - description: Failed to create in auth service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "201": description: Created user content: @@ -2063,6 +2047,22 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "400": + description: Parameters are not well-formed (likely email) + content: + application/json: {} + "417": + description: User with e-mail already exists + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "503": + description: Failed to create in auth service + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" /api/ontology: get: tags: @@ -2274,38 +2274,38 @@ paths: $ref: "#/components/schemas/CreateIdentifierDto" required: true responses: - "403": - description: Insufficient access rights or authorities + "201": + description: Drafted identifier content: application/json: schema: - $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + $ref: "#/components/schemas/IdentifierDto" + "400": + description: Identifier form contains invalid request data content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "404": + description: "Failed to find database, table or view" content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Identifier form contains invalid request data + "403": + description: Insufficient access rights or authorities content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "201": - description: Drafted identifier + "503": + description: Failed to save in search service content: application/json: schema: - $ref: "#/components/schemas/IdentifierDto" - "404": - description: "Failed to find database, table or view" + $ref: "#/components/schemas/ApiErrorDto" + "502": + description: Connection to search service failed content: application/json: schema: @@ -2328,12 +2328,6 @@ paths: type: integer format: int64 responses: - "404": - description: Database or user could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Find views successfully content: @@ -2342,6 +2336,12 @@ paths: type: array items: $ref: "#/components/schemas/ViewBriefDto" + "404": + description: Database or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2372,38 +2372,38 @@ paths: application/json: schema: $ref: "#/components/schemas/ViewBriefDto" - "400": - description: Create view query is malformed + "423": + description: Create view resulted in an invalid query statement content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Credentials missing + "503": + description: Failed to save in search service content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Failed to find database/user in metadata database. + "502": + description: Connection to search service failed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "503": - description: Failed to save in search service + "400": + description: Create view query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "502": - description: Connection to search service failed + "403": + description: Credentials missing content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "423": - description: Create view resulted in an invalid query statement + "404": + description: Failed to find database/user in metadata database. content: application/json: schema: @@ -2428,6 +2428,12 @@ paths: type: integer format: int64 responses: + "403": + description: List tables not permitted + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "404": description: Database could not be found content: @@ -2442,12 +2448,6 @@ paths: type: array items: $ref: "#/components/schemas/TableBriefDto" - "403": - description: List tables not permitted - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2471,14 +2471,20 @@ paths: $ref: "#/components/schemas/CreateTableDto" required: true responses: + "409": + description: Create table conflicts with existing table name + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "400": description: Create table query is malformed content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "409": - description: Create table conflicts with existing table name + "404": + description: "Database, container or user could not be found" content: application/json: schema: @@ -2507,12 +2513,6 @@ paths: application/json: schema: $ref: "#/components/schemas/TableBriefDto" - "404": - description: "Database, container or user could not be found" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2564,12 +2564,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "404": - description: Container image or user could not be found - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "201": description: Created a new container content: @@ -2582,6 +2576,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "404": + description: Container image or user could not be found + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2639,14 +2639,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Found entities - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/EntityDto" "404": description: Could not find ontology content: @@ -2659,6 +2651,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Found entities + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/EntityDto" security: - bearerAuth: [] - basicAuth: [] @@ -2766,6 +2766,18 @@ paths: type: integer format: int64 responses: + "404": + description: "Database, user or exchange could not be found" + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" + "403": + description: Not allowed to view database + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "200": description: Database found successfully headers: @@ -2782,12 +2794,6 @@ paths: application/json: schema: $ref: "#/components/schemas/DatabaseBriefDto" - "404": - description: "Database, user or exchange could not be found" - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "503": description: Failed to find queue information in broker service content: @@ -2800,12 +2806,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "403": - description: Not allowed to view database - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" security: - bearerAuth: [] - basicAuth: [] @@ -2843,12 +2843,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "400": - description: Failed to parse statistic in search service - content: - application/json: - schema: - $ref: "#/components/schemas/ApiErrorDto" "200": description: Suggested table semantics successfully content: @@ -2857,14 +2851,20 @@ paths: type: array items: $ref: "#/components/schemas/EntityDto" + "417": + description: Generated query is malformed + content: + application/json: + schema: + $ref: "#/components/schemas/ApiErrorDto" "404": description: Failed to find database/table in metadata database content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "417": - description: Generated query is malformed + "400": + description: Failed to parse statistic in search service content: application/json: schema: @@ -2905,6 +2905,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "200": + description: Suggested table column semantics successfully + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TableColumnEntityDto" "400": description: Generated query is malformed content: @@ -2917,14 +2925,6 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "200": - description: Suggested table column semantics successfully - content: - application/json: - schema: - type: array - items: - $ref: "#/components/schemas/TableColumnEntityDto" security: - bearerAuth: [] - basicAuth: [] @@ -2975,14 +2975,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" - "202": - description: Deleted container "404": description: Container not found content: application/json: schema: $ref: "#/components/schemas/ApiErrorDto" + "202": + description: Deleted container security: - bearerAuth: [] - basicAuth: [] @@ -3395,6 +3395,21 @@ components: family_name: type: string example: Carberry + DatabaseAccessDto: + required: + - type + - user + type: object + properties: + user: + $ref: "#/components/schemas/UserBriefDto" + type: + type: string + example: read + enum: + - read + - write_own + - write_all ApiErrorDto: required: - code @@ -3481,21 +3496,6 @@ components: code: type: string example: error.service.code - DatabaseAccessDto: - required: - - type - - user - type: object - properties: - user: - $ref: "#/components/schemas/UserBriefDto" - type: - type: string - example: read - enum: - - read - - write_own - - write_all UserUpdateDto: required: - language diff --git a/.docs/.openapi/api.yaml b/.docs/.openapi/api.yaml index 3338c9d6a2..c5d75bca5a 100644 --- a/.docs/.openapi/api.yaml +++ b/.docs/.openapi/api.yaml @@ -16,7 +16,7 @@ info: name: Apache 2.0 url: 'https://www.apache.org/licenses/LICENSE-2.0' title: DBRepo REST API - version: 1.6.1 + version: 1.6.2 servers: - description: Test Instance url: 'https://test.dbrepo.tuwien.ac.at' @@ -710,6 +710,12 @@ paths: schema: type: integer format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time - name: page in: query required: false @@ -797,6 +803,12 @@ paths: schema: type: integer format: int64 + - name: timestamp + in: query + required: false + schema: + type: string + format: date-time - name: page in: query required: false @@ -1456,7 +1468,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DatabaseCreateDto' + $ref: '#/components/schemas/CreateDatabaseDto' required: true responses: '201': @@ -2713,7 +2725,7 @@ paths: content: '*/*': schema: - $ref: '#/components/schemas/ViewDto' + $ref: '#/components/schemas/ViewBriefDto' '400': description: Update view query is malformed content: @@ -2769,10 +2781,6 @@ paths: responses: '202': description: Delete view successfully - content: - '*/*': - schema: - type: object '400': description: Delete view query is malformed content: @@ -2843,27 +2851,12 @@ paths: X-Username: description: The authentication username style: simple - X-Table: - description: The table internal name - style: simple Access-Control-Expose-Headers: description: Expose custom headers style: simple - X-Type: - description: The JDBC connection type - style: simple - X-Database: - description: The database internal name - style: simple X-Password: description: The authentication password style: simple - X-Host: - description: The database hostname - style: simple - X-Port: - description: The database port number - style: simple content: application/json: schema: @@ -3457,7 +3450,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SignupRequestDto' + $ref: '#/components/schemas/CreateUserDto' required: true responses: '201': @@ -3720,7 +3713,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/IdentifierCreateDto' + $ref: '#/components/schemas/CreateIdentifierDto' required: true responses: '201': @@ -3813,7 +3806,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ViewCreateDto' + $ref: '#/components/schemas/CreateViewDto' required: true responses: '201': @@ -3919,7 +3912,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/TableCreateDto' + $ref: '#/components/schemas/CreateTableDto' required: true responses: '201': @@ -5410,24 +5403,10 @@ components: required: - attributes - id + - password + - username type: object properties: - jdbcMethod: - type: string - example: mariadb - host: - type: string - example: data-db - port: - type: integer - format: int32 - example: 3306 - username: - type: string - example: username - database: - type: string - example: air_quality id: type: string format: uuid @@ -5435,6 +5414,12 @@ components: name: type: string example: Josiah Carberry + username: + type: string + example: username + password: + type: string + example: p4ssw0rd attributes: $ref: '#/components/schemas/UserAttributesDto' last_retrieved: @@ -5754,105 +5739,53 @@ components: display_name: type: string example: XOR - CreatorSaveDto: - required: - - creator_name - - id - type: object - properties: - id: - type: integer - format: int64 - example: 1 - firstname: - type: string - example: Josiah - lastname: - type: string - example: Carberry - affiliation: - type: string - example: Wesleyan University - creator_name: - type: string - example: 'Carberry, Josiah' - name_type: - type: string - example: Personal - enum: - - Personal - - Organizational - name_identifier: - type: string - example: 0000-0002-1825-0097 - name_identifier_scheme: - type: string - example: ORCID - enum: - - ORCID - - ROR - - ISNI - - GRID - affiliation_identifier: - type: string - example: 'https://ror.org/04d836q62' - affiliation_identifier_scheme: - type: string - example: ROR - enum: - - ROR - - GRID - - ISNI - IdentifierFunderSaveDto: + IdentifierSaveDto: required: - - funder_name + - creators + - database_id - id + - publication_year + - publisher + - titles + - type type: object properties: id: type: integer format: int64 example: 1 - funder_name: - type: string - example: European Commission - funder_identifier: - type: string - example: 'http://doi.org/10.13039/501100000780' - funder_identifier_type: + type: type: string - example: Crossref Funder ID + example: database enum: - - Crossref Funder ID - - ROR - - GND - - ISNI - - Other - scheme_uri: - type: string - example: 'http://doi.org/' - award_number: - type: string - example: '824087' - award_title: + - database + - subset + - table + - view + doi: type: string - example: EOSC-Life - IdentifierSaveDescriptionDto: - required: - - description - - id - type: object - properties: - id: - type: integer - format: int64 - example: 1 - description: + example: 10.1111/11111111 + titles: + type: array + items: + $ref: '#/components/schemas/SaveIdentifierTitleDto' + descriptions: + type: array + items: + $ref: '#/components/schemas/SaveIdentifierDescriptionDto' + funders: + type: array + items: + $ref: '#/components/schemas/SaveIdentifierFunderDto' + licenses: + type: array + items: + $ref: '#/components/schemas/LicenseDto' + publisher: type: string - example: 'Air quality reports at Stephansplatz, Vienna' + example: TU Wien language: type: string - example: en enum: - ab - aa @@ -6038,63 +5971,123 @@ components: - yo - za - zu - type: + creators: + type: array + items: + $ref: '#/components/schemas/SaveIdentifierCreatorDto' + database_id: + type: integer + format: int64 + example: 1 + query_id: + type: integer + format: int64 + view_id: + type: integer + format: int64 + table_id: + type: integer + format: int64 + publication_day: + type: integer + format: int32 + example: 15 + publication_month: + type: integer + format: int32 + example: 12 + publication_year: + type: integer + format: int32 + example: 2022 + related_identifiers: + type: array + items: + $ref: '#/components/schemas/SaveRelatedIdentifierDto' + LicenseDto: + required: + - identifier + - uri + type: object + properties: + identifier: type: string - example: Abstract - enum: - - Abstract - - Methods - - SeriesInformation - - TableOfContents - - TechnicalInfo - - Other - IdentifierSaveDto: + example: MIT + uri: + type: string + example: 'https://opensource.org/licenses/MIT' + description: + type: string + example: >- + A short and simple permissive license with conditions only requiring + preservation of copyright and license notices. Licensed works, + modifications, and larger works may be distributed under different + terms and without source code. + SaveIdentifierCreatorDto: required: - - creators - - database_id + - creator_name - id - - publication_year - - publisher - - titles - - type type: object properties: id: type: integer format: int64 example: 1 - type: + firstname: type: string - example: database + example: Josiah + lastname: + type: string + example: Carberry + affiliation: + type: string + example: Wesleyan University + creator_name: + type: string + example: 'Carberry, Josiah' + name_type: + type: string + example: Personal enum: - - database - - subset - - table - - view - doi: + - Personal + - Organizational + name_identifier: type: string - example: 10.1111/11111111 - titles: - type: array - items: - $ref: '#/components/schemas/IdentifierSaveTitleDto' - descriptions: - type: array - items: - $ref: '#/components/schemas/IdentifierSaveDescriptionDto' - funders: - type: array - items: - $ref: '#/components/schemas/IdentifierFunderSaveDto' - licenses: - type: array - items: - $ref: '#/components/schemas/LicenseDto' - publisher: + example: 0000-0002-1825-0097 + name_identifier_scheme: type: string - example: TU Wien + example: ORCID + enum: + - ORCID + - ROR + - ISNI + - GRID + affiliation_identifier: + type: string + example: 'https://ror.org/04d836q62' + affiliation_identifier_scheme: + type: string + example: ROR + enum: + - ROR + - GRID + - ISNI + SaveIdentifierDescriptionDto: + required: + - description + - id + type: object + properties: + id: + type: integer + format: int64 + example: 1 + description: + type: string + example: 'Air quality reports at Stephansplatz, Vienna' language: type: string + example: en enum: - ab - aa @@ -6280,40 +6273,51 @@ components: - yo - za - zu - creators: - type: array - items: - $ref: '#/components/schemas/CreatorSaveDto' - database_id: + type: + type: string + example: Abstract + enum: + - Abstract + - Methods + - SeriesInformation + - TableOfContents + - TechnicalInfo + - Other + SaveIdentifierFunderDto: + required: + - funder_name + - id + type: object + properties: + id: type: integer format: int64 example: 1 - query_id: - type: integer - format: int64 - view_id: - type: integer - format: int64 - table_id: - type: integer - format: int64 - publication_day: - type: integer - format: int32 - example: 15 - publication_month: - type: integer - format: int32 - example: 12 - publication_year: - type: integer - format: int32 - example: 2022 - related_identifiers: - type: array - items: - $ref: '#/components/schemas/RelatedIdentifierSaveDto' - IdentifierSaveTitleDto: + funder_name: + type: string + example: European Commission + funder_identifier: + type: string + example: 'http://doi.org/10.13039/501100000780' + funder_identifier_type: + type: string + example: Crossref Funder ID + enum: + - Crossref Funder ID + - ROR + - GND + - ISNI + - Other + scheme_uri: + type: string + example: 'http://doi.org/' + award_number: + type: string + example: '824087' + award_title: + type: string + example: EOSC-Life + SaveIdentifierTitleDto: required: - id - title @@ -6522,26 +6526,7 @@ components: - Subtitle - TranslatedTitle - Other - LicenseDto: - required: - - identifier - - uri - type: object - properties: - identifier: - type: string - example: MIT - uri: - type: string - example: 'https://opensource.org/licenses/MIT' - description: - type: string - example: >- - A short and simple permissive license with conditions only requiring - preservation of copyright and license notices. Licensed works, - modifications, and larger works may be distributed under different - terms and without source code. - RelatedIdentifierSaveDto: + SaveRelatedIdentifierDto: required: - id - relation @@ -7323,196 +7308,77 @@ components: is_schema_public: type: boolean example: true - ViewColumnDto: + ViewBriefDto: required: - database_id - id - internal_name - - is_null_allowed - name - - ord - - type + - query + - query_hash type: object properties: id: type: integer format: int64 - example: 12 + example: 4 name: - maxLength: 64 - minLength: 0 type: string - example: Given Name - size: - type: integer - format: int64 - example: 255 - d: - type: integer - format: int64 - example: 0 - description: - maxLength: 2048 - minLength: 0 + example: Air Quality + query: type: string - example: Column comment + example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC database_id: type: integer format: int64 example: 1 - ord: - type: integer - format: int32 - example: 0 internal_name: - maxLength: 64 - minLength: 0 type: string - example: given_name - index_length: - type: integer - format: int64 - example: 255 - length: - type: integer - format: int64 - example: 255 - type: + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + initial_view: + type: boolean + description: True if it is the default view for the database + example: true + query_hash: type: string - example: varchar - enum: - - char - - varchar - - binary - - varbinary - - tinyblob - - tinytext - - text - - blob - - mediumtext - - mediumblob - - longtext - - longblob - - enum - - set - - serial - - bit - - tinyint - - bool - - smallint - - mediumint - - int - - bigint - - float - - double - - decimal - - date - - datetime - - timestamp - - time - - year - is_null_allowed: + example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 + owned_by: + type: string + format: uuid + example: ac750fcf-ea02-4fce-85ac-d73857e18b35 + TableUpdateDto: + required: + - is_public + - is_schema_public + type: object + properties: + description: + maxLength: 180 + minLength: 0 + type: string + example: Air Quality in Austria + is_public: type: boolean - example: false - ViewDto: + example: true + is_schema_public: + type: boolean + example: true + TableBriefDto: required: - - columns - database_id - id - - identifiers - internal_name + - is_public + - is_schema_public + - is_versioned - name - - owner - - query - - query_hash - type: object - properties: - jdbcMethod: - type: string - example: mariadb - host: - type: string - example: data-db - port: - type: integer - format: int32 - example: 3306 - username: - type: string - example: username - database: - type: string - example: air_quality - id: - type: integer - format: int64 - example: 4 - name: - type: string - example: Air Quality - identifiers: - type: array - items: - $ref: '#/components/schemas/IdentifierDto' - query: - type: string - example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC - owner: - $ref: '#/components/schemas/UserBriefDto' - columns: - type: array - items: - $ref: '#/components/schemas/ViewColumnDto' - last_retrieved: - type: string - format: date-time - database_id: - type: integer - format: int64 - example: 1 - internal_name: - type: string - example: air_quality - is_public: - type: boolean - example: true - is_schema_public: - type: boolean - example: true - initial_view: - type: boolean - description: True if it is the default view for the database - example: true - query_hash: - type: string - example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 - TableUpdateDto: - required: - - is_public - - is_schema_public - type: object - properties: - description: - maxLength: 180 - minLength: 0 - type: string - example: Air Quality in Austria - is_public: - type: boolean - example: true - is_schema_public: - type: boolean - example: true - TableBriefDto: - required: - - database_id - - id - - internal_name - - is_public - - is_schema_public - - is_versioned - - name - - owned_by + - owned_by type: object properties: id: @@ -7761,7 +7627,7 @@ components: - read - write_own - write_all - SignupRequestDto: + CreateUserDto: required: - email - password @@ -7866,7 +7732,7 @@ components: minimum: 1024 type: integer format: int32 - IdentifierCreateDto: + CreateIdentifierDto: required: - creators - database_id @@ -7890,15 +7756,15 @@ components: titles: type: array items: - $ref: '#/components/schemas/IdentifierSaveTitleDto' + $ref: '#/components/schemas/SaveIdentifierTitleDto' descriptions: type: array items: - $ref: '#/components/schemas/IdentifierSaveDescriptionDto' + $ref: '#/components/schemas/SaveIdentifierDescriptionDto' funders: type: array items: - $ref: '#/components/schemas/IdentifierFunderSaveDto' + $ref: '#/components/schemas/SaveIdentifierFunderDto' licenses: type: array items: @@ -8096,7 +7962,7 @@ components: creators: type: array items: - $ref: '#/components/schemas/CreatorSaveDto' + $ref: '#/components/schemas/SaveIdentifierCreatorDto' database_id: type: integer format: int64 @@ -8125,8 +7991,8 @@ components: related_identifiers: type: array items: - $ref: '#/components/schemas/RelatedIdentifierSaveDto' - DatabaseCreateDto: + $ref: '#/components/schemas/SaveRelatedIdentifierDto' + CreateDatabaseDto: required: - container_id - is_public @@ -8147,7 +8013,7 @@ components: is_schema_public: type: boolean example: true - ViewCreateDto: + CreateViewDto: required: - is_public - is_schema_public @@ -8169,51 +8035,40 @@ components: is_schema_public: type: boolean example: true - ViewBriefDto: + CreateForeignKeyDto: required: - - database_id - - id - - internal_name - - name - - query - - query_hash + - columns + - referenced_columns + - referenced_table type: object properties: - id: - type: integer - format: int64 - example: 4 - name: - type: string - example: Air Quality - query: - type: string - example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC - database_id: - type: integer - format: int64 - example: 1 - internal_name: + columns: + type: array + items: + type: string + referenced_table: type: string - example: air_quality - is_public: - type: boolean - example: true - is_schema_public: - type: boolean - example: true - initial_view: - type: boolean - description: True if it is the default view for the database - example: true - query_hash: + referenced_columns: + type: array + items: + type: string + on_update: type: string - example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 - owned_by: + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + on_delete: type: string - format: uuid - example: ac750fcf-ea02-4fce-85ac-d73857e18b35 - ColumnCreateDto: + enum: + - restrict + - cascade + - set_null + - no_action + - set_default + CreateTableColumnDto: required: - name - null_allowed @@ -8292,7 +8147,7 @@ components: type: string unit_uri: type: string - ConstraintsCreateDto: + CreateTableConstraintsDto: required: - checks - foreign_keys @@ -8314,46 +8169,13 @@ components: foreign_keys: type: array items: - $ref: '#/components/schemas/ForeignKeyCreateDto' + $ref: '#/components/schemas/CreateForeignKeyDto' primary_key: uniqueItems: true type: array items: type: string - ForeignKeyCreateDto: - required: - - columns - - referenced_columns - - referenced_table - type: object - properties: - columns: - type: array - items: - type: string - referenced_table: - type: string - referenced_columns: - type: array - items: - type: string - on_update: - type: string - enum: - - restrict - - cascade - - set_null - - no_action - - set_default - on_delete: - type: string - enum: - - restrict - - cascade - - set_null - - no_action - - set_default - TableCreateDto: + CreateTableDto: required: - columns - constraints @@ -8375,9 +8197,9 @@ components: columns: type: array items: - $ref: '#/components/schemas/ColumnCreateDto' + $ref: '#/components/schemas/CreateTableColumnDto' constraints: - $ref: '#/components/schemas/ConstraintsCreateDto' + $ref: '#/components/schemas/CreateTableConstraintsDto' is_public: type: boolean example: true @@ -8440,9 +8262,13 @@ components: - quota type: object properties: - jdbcMethod: + id: + type: integer + format: int64 + example: 4 + name: type: string - example: mariadb + example: Air Quality host: type: string example: data-db @@ -8450,19 +8276,6 @@ components: type: integer format: int32 example: 3306 - username: - type: string - example: username - database: - type: string - example: air_quality - id: - type: integer - format: int64 - example: 4 - name: - type: string - example: Air Quality image: $ref: '#/components/schemas/ImageDto' quota: @@ -8473,6 +8286,12 @@ components: type: integer format: int64 example: 10 + username: + type: string + example: username + password: + type: string + example: p4ssw0rd last_retrieved: type: string format: date-time @@ -8782,6 +8601,79 @@ components: type: array items: $ref: '#/components/schemas/PrimaryKeyDto' + DatabaseDto: + required: + - accesses + - contact + - exchange_name + - id + - identifiers + - internal_name + - is_public + - is_schema_public + - name + - owner + - subsets + - tables + - views + type: object + properties: + id: + type: integer + format: int64 + example: 3 + name: + type: string + example: Air Quality + description: + type: string + example: Air Quality + tables: + type: array + items: + $ref: '#/components/schemas/TableDto' + views: + type: array + items: + $ref: '#/components/schemas/ViewDto' + container: + $ref: '#/components/schemas/ContainerDto' + accesses: + type: array + items: + $ref: '#/components/schemas/DatabaseAccessDto' + identifiers: + type: array + items: + $ref: '#/components/schemas/IdentifierDto' + subsets: + type: array + items: + $ref: '#/components/schemas/IdentifierDto' + contact: + $ref: '#/components/schemas/UserBriefDto' + owner: + $ref: '#/components/schemas/UserBriefDto' + last_retrieved: + type: string + format: date-time + exchange_name: + type: string + example: dbrepo + exchange_type: + type: string + example: topic + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + preview_image: + type: string ForeignKeyBriefDto: type: object properties: @@ -8877,22 +8769,6 @@ components: - routing_key type: object properties: - jdbcMethod: - type: string - example: mariadb - host: - type: string - example: data-db - port: - type: integer - format: int32 - example: 3306 - username: - type: string - example: username - database: - type: string - example: air_quality id: type: integer format: int64 @@ -8918,6 +8794,8 @@ components: type: array items: $ref: '#/components/schemas/ColumnDto' + database: + $ref: '#/components/schemas/DatabaseDto' constraints: $ref: '#/components/schemas/ConstraintsDto' last_retrieved: @@ -8988,6 +8866,155 @@ components: type: array items: $ref: '#/components/schemas/ColumnBriefDto' + ViewColumnDto: + required: + - database_id + - id + - internal_name + - is_null_allowed + - name + - ord + - type + type: object + properties: + id: + type: integer + format: int64 + example: 12 + name: + maxLength: 64 + minLength: 0 + type: string + example: Given Name + size: + type: integer + format: int64 + example: 255 + d: + type: integer + format: int64 + example: 0 + description: + maxLength: 2048 + minLength: 0 + type: string + example: Column comment + database_id: + type: integer + format: int64 + example: 1 + ord: + type: integer + format: int32 + example: 0 + internal_name: + maxLength: 64 + minLength: 0 + type: string + example: given_name + index_length: + type: integer + format: int64 + example: 255 + length: + type: integer + format: int64 + example: 255 + type: + type: string + example: varchar + enum: + - char + - varchar + - binary + - varbinary + - tinyblob + - tinytext + - text + - blob + - mediumtext + - mediumblob + - longtext + - longblob + - enum + - set + - serial + - bit + - tinyint + - bool + - smallint + - mediumint + - int + - bigint + - float + - double + - decimal + - date + - datetime + - timestamp + - time + - year + is_null_allowed: + type: boolean + example: false + ViewDto: + required: + - columns + - database_id + - id + - identifiers + - internal_name + - name + - owner + - query + - query_hash + type: object + properties: + id: + type: integer + format: int64 + example: 4 + name: + type: string + example: Air Quality + identifiers: + type: array + items: + $ref: '#/components/schemas/IdentifierDto' + query: + type: string + example: SELECT `id` FROM `air_quality` ORDER BY `value` DESC + database: + $ref: '#/components/schemas/DatabaseDto' + owner: + $ref: '#/components/schemas/UserBriefDto' + columns: + type: array + items: + $ref: '#/components/schemas/ViewColumnDto' + last_retrieved: + type: string + format: date-time + database_id: + type: integer + format: int64 + example: 1 + internal_name: + type: string + example: air_quality + is_public: + type: boolean + example: true + is_schema_public: + type: boolean + example: true + initial_view: + type: boolean + description: True if it is the default view for the database + example: true + query_hash: + type: string + example: 7de03e818900b6ea6d58ad0306d4a741d658c6df3d1964e89ed2395d8c7e7916 TableColumnEntityDto: required: - column_id diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java index 878502f1bc..90cb9846b2 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java @@ -1,6 +1,7 @@ package at.tuwien.endpoints; import at.tuwien.ExportResourceDto; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.ViewColumnDto; import at.tuwien.api.database.ViewDto; @@ -163,8 +164,7 @@ public class SubsetEndpoint extends RestEndpoint { Principal principal) throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, QueryNotFoundException, FormatNotAvailableException, StorageUnavailableException, UserNotFoundException, - MetadataServiceException, TableNotFoundException, ViewMalformedException, QueryMalformedException, - NotAllowedException { + MetadataServiceException, TableNotFoundException, QueryMalformedException, NotAllowedException { log.debug("endpoint find subset in database, databaseId={}, subsetId={}, accept={}, timestamp={}", databaseId, subsetId, accept, timestamp); final DatabaseDto database = credentialService.getDatabase(databaseId); @@ -341,7 +341,7 @@ public class SubsetEndpoint extends RestEndpoint { @RequestParam(required = false) Long size) throws PaginationException, DatabaseNotFoundException, RemoteUnavailableException, NotAllowedException, QueryNotFoundException, DatabaseUnavailableException, TableMalformedException, QueryMalformedException, - UserNotFoundException, MetadataServiceException, TableNotFoundException, ViewNotFoundException { + UserNotFoundException, MetadataServiceException, TableNotFoundException, ViewNotFoundException, ViewMalformedException { log.debug("endpoint get subset data, databaseId={}, subsetId={}, principal.name={} page={}, size={}", databaseId, subsetId, principal != null ? principal.getName() : null, page, size); endpointValidator.validateDataParams(page, size); @@ -383,6 +383,12 @@ public class SubsetEndpoint extends RestEndpoint { final Dataset<Row> dataset = subsetService.getData(database, query, timestamp, page, size, null, null); metricsService.countSubsetGetData(databaseId, subsetId); final String viewName = metadataMapper.queryDtoToViewName(subset); + databaseService.createView(database, CreateViewDto.builder() + .name(viewName) + .isPublic(false) + .isSchemaPublic(false) + .query(query) + .build()); final ViewDto view = databaseService.inspectView(database, viewName); headers.set("Access-Control-Expose-Headers", "X-Id X-Headers"); headers.set("X-Headers", String.join(",", view.getColumns().stream().map(ViewColumnDto::getInternalName).toList())); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java index 18307b5ac2..9ad13f5be6 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java @@ -6,7 +6,7 @@ import at.tuwien.api.database.DatabaseDto; import at.tuwien.config.QueryConfig; import at.tuwien.endpoints.RestEndpoint; import at.tuwien.exception.*; -import at.tuwien.gateway.MetadataServiceGateway; +import at.tuwien.service.CredentialService; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -24,12 +24,12 @@ import java.util.regex.Pattern; public class EndpointValidator extends RestEndpoint { private final QueryConfig queryConfig; - private final MetadataServiceGateway metadataServiceGateway; + private final CredentialService credentialService; @Autowired - public EndpointValidator(QueryConfig queryConfig, MetadataServiceGateway metadataServiceGateway) { + public EndpointValidator(QueryConfig queryConfig, CredentialService credentialService) { this.queryConfig = queryConfig; - this.metadataServiceGateway = metadataServiceGateway; + this.credentialService = credentialService; } public void validateDataParams(Long page, Long size) throws PaginationException { @@ -90,7 +90,7 @@ public class EndpointValidator extends RestEndpoint { if (isSystem(principal)) { return; } - final DatabaseAccessDto access = metadataServiceGateway.getAccess(database.getId(), getId(principal)); + final DatabaseAccessDto access = credentialService.getAccess(database.getId(), getId(principal)); log.trace("found access: {}", access); if (writeAccessOnly && !(access.getType().equals(AccessTypeDto.WRITE_OWN) || access.getType().equals(AccessTypeDto.WRITE_ALL))) { log.error("Access not allowed: no write access"); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java index 3cecdb6457..3cbdf09af1 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/AccessEndpointUnitTest.java @@ -51,7 +51,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getUser(USER_4_ID)) .thenReturn(USER_4_DTO); @@ -68,7 +68,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getUser(USER_1_ID)) .thenReturn(USER_1_DTO); @@ -85,12 +85,12 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getUser(USER_4_ID)) .thenReturn(USER_4_DTO); doThrow(SQLException.class) .when(accessService) - .create(DATABASE_1_DTO, USER_4_DTO, AccessTypeDto.READ); + .create(DATABASE_1_PRIVILEGED_DTO, USER_4_DTO, AccessTypeDto.READ); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -121,7 +121,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doThrow(UserNotFoundException.class) .when(credentialService) .getUser(USER_4_ID); @@ -149,7 +149,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getUser(USER_1_ID)) .thenReturn(USER_1_DTO); @@ -166,12 +166,12 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getUser(USER_1_ID)) .thenReturn(USER_1_DTO); doThrow(SQLException.class) .when(accessService) - .update(DATABASE_1_DTO, USER_1_DTO, AccessTypeDto.READ); + .update(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO, AccessTypeDto.READ); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -186,7 +186,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getUser(USER_4_ID)) .thenReturn(USER_4_DTO); @@ -229,7 +229,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doThrow(UserNotFoundException.class) .when(credentialService) .getUser(USER_1_ID); @@ -248,7 +248,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getUser(USER_1_ID)) .thenReturn(USER_1_DTO); doNothing() @@ -268,7 +268,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getUser(USER_4_ID)) .thenReturn(USER_4_DTO); @@ -311,7 +311,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doThrow(UserNotFoundException.class) .when(credentialService) .getUser(USER_1_ID); @@ -329,12 +329,12 @@ public class AccessEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getUser(USER_1_ID)) .thenReturn(USER_1_DTO); doThrow(SQLException.class) .when(accessService) - .delete(DATABASE_1_DTO, USER_1_DTO); + .delete(DATABASE_1_PRIVILEGED_DTO, USER_1_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java index 0c6f9d8521..43f7b9353e 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java @@ -65,13 +65,13 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { when(credentialService.getContainer(CONTAINER_1_ID)) .thenReturn(CONTAINER_1_DTO); when(containerService.createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doNothing() .when(containerService) .createQueryStore(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); doNothing() .when(accessService) - .create(eq(DATABASE_1_DTO), any(UserDto.class), any(AccessTypeDto.class)); + .create(eq(DATABASE_1_PRIVILEGED_DTO), any(UserDto.class), any(AccessTypeDto.class)); /* test */ final ResponseEntity<DatabaseDto> response = databaseEndpoint.create(DATABASE_1_CREATE_INTERNAL); @@ -87,13 +87,13 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { when(credentialService.getContainer(CONTAINER_1_ID)) .thenReturn(CONTAINER_1_DTO); when(containerService.createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doNothing() .when(containerService) .createQueryStore(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); doNothing() .when(accessService) - .create(eq(DATABASE_1_DTO), any(UserDto.class), any(AccessTypeDto.class)); + .create(eq(DATABASE_1_PRIVILEGED_DTO), any(UserDto.class), any(AccessTypeDto.class)); /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { @@ -145,7 +145,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { .when(credentialService) .getContainer(CONTAINER_1_ID); when(containerService.createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doThrow(QueryStoreCreateException.class) .when(containerService) .createQueryStore(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); @@ -163,7 +163,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); /* test */ databaseEndpoint.update(DATABASE_1_ID, USER_1_UPDATE_PASSWORD_DTO); @@ -176,10 +176,10 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doThrow(SQLException.class) .when(databaseService) - .update(DATABASE_1_DTO, USER_1_UPDATE_PASSWORD_DTO); + .update(DATABASE_1_PRIVILEGED_DTO, USER_1_UPDATE_PASSWORD_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -193,7 +193,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { @@ -224,10 +224,10 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doThrow(DatabaseMalformedException.class) .when(databaseService) - .update(DATABASE_1_DTO, USER_1_UPDATE_PASSWORD_DTO); + .update(DATABASE_1_PRIVILEGED_DTO, USER_1_UPDATE_PASSWORD_DTO); /* test */ assertThrows(DatabaseMalformedException.class, () -> { diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java index 53393be407..8837ccb0d1 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java @@ -1,14 +1,14 @@ package at.tuwien.endpoint; -import at.tuwien.api.SortTypeDto; +import at.tuwien.api.database.CreateViewDto; import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.ExecuteStatementDto; import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.query.QueryPersistDto; import at.tuwien.endpoints.SubsetEndpoint; import at.tuwien.exception.*; -import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.service.CredentialService; +import at.tuwien.service.DatabaseService; import at.tuwien.service.StorageService; import at.tuwien.service.SubsetService; import at.tuwien.test.AbstractUnitTest; @@ -52,15 +52,15 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @MockBean private SubsetService subsetService; - @MockBean - private MetadataServiceGateway metadataServiceGateway; - @MockBean private HttpServletRequest httpServletRequest; @MockBean private StorageService storageService; + @MockBean + private DatabaseService databaseService; + @MockBean private CredentialService credentialService; @@ -75,12 +75,12 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { RemoteUnavailableException, SQLException, MetadataServiceException { /* mock */ - when(subsetService.findAll(DATABASE_3_DTO, null)) + when(subsetService.findAll(DATABASE_3_PRIVILEGED_DTO, null)) .thenReturn(List.of(QUERY_1_DTO, QUERY_2_DTO, QUERY_3_DTO, QUERY_4_DTO, QUERY_5_DTO, QUERY_6_DTO)); /* test */ assertThrows(NotAllowedException.class, () -> { - generic_list(DATABASE_3_ID, DATABASE_3_DTO, null); + generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, null); }); } @@ -91,11 +91,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { MetadataServiceException { /* mock */ - when(subsetService.findAll(DATABASE_3_DTO, null)) + when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) + .thenReturn(DATABASE_3_USER_1_READ_ACCESS_DTO); + when(subsetService.findAll(DATABASE_3_PRIVILEGED_DTO, null)) .thenReturn(List.of(QUERY_1_DTO, QUERY_2_DTO, QUERY_3_DTO, QUERY_4_DTO, QUERY_5_DTO, QUERY_6_DTO)); /* test */ - final List<QueryDto> response = generic_list(DATABASE_3_ID, DATABASE_3_DTO, USER_3_PRINCIPAL); + final List<QueryDto> response = generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, USER_3_PRINCIPAL); assertEquals(6, response.size()); } @@ -116,14 +118,14 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); + .thenReturn(DATABASE_3_PRIVILEGED_DTO); doThrow(SQLException.class) .when(subsetService) - .findAll(DATABASE_3_DTO, null); + .findAll(DATABASE_3_PRIVILEGED_DTO, null); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { - generic_list(DATABASE_3_ID, DATABASE_3_DTO, USER_3_PRINCIPAL); + generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, USER_3_PRINCIPAL); }); } @@ -134,7 +136,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -149,8 +151,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); - when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) .thenReturn(QUERY_1_DTO); /* test */ @@ -167,8 +169,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); /* test */ @@ -182,8 +184,8 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_4_ID)) - .thenReturn(DATABASE_4_DTO); - when(subsetService.findById(DATABASE_4_DTO, QUERY_7_ID)) + .thenReturn(DATABASE_4_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_4_PRIVILEGED_DTO, QUERY_7_ID)) .thenReturn(QUERY_7_DTO); /* test */ @@ -202,13 +204,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); - when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) .thenReturn(QUERY_5_DTO); + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) + .thenReturn(mock); when(storageService.transformDataset(any(Dataset.class))) .thenReturn(EXPORT_RESOURCE_DTO); - when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) - .thenReturn(mock); /* test */ generic_findById(DATABASE_1_ID, QUERY_1_ID, "text/csv", null, USER_1_PRINCIPAL); @@ -221,7 +223,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); + .thenReturn(DATABASE_3_PRIVILEGED_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -238,10 +240,10 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(subsetService.findById(DATABASE_4_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_4_PRIVILEGED_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(storageService.transformDataset(any(Dataset.class))) .thenReturn(EXPORT_RESOURCE_DTO); @@ -275,10 +277,10 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); + .thenReturn(DATABASE_3_PRIVILEGED_DTO); doThrow(SQLException.class) .when(subsetService) - .findById(DATABASE_3_DTO, QUERY_5_ID); + .findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -286,29 +288,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { }); } - @Test - @WithMockUser(username = USER_1_USERNAME) - public void findById_publicDataPrivateSchemaUnavailableExport_fails() throws DatabaseNotFoundException, - RemoteUnavailableException, MetadataServiceException, SQLException, QueryMalformedException, - UserNotFoundException, QueryNotFoundException, TableNotFoundException, StorageUnavailableException { - - /* mock */ - when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) - .thenReturn(QUERY_5_DTO); - when(storageService.transformDataset(any(Dataset.class))) - .thenReturn(EXPORT_RESOURCE_DTO); - doThrow(SQLException.class) - .when(subsetService) - .getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString()); - - /* test */ - assertThrows(DatabaseUnavailableException.class, () -> { - generic_findById(DATABASE_3_ID, QUERY_5_ID, "text/csv", null, USER_1_PRINCIPAL); - }); - } - @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-query"}) public void create_noAccess_succeeds() throws UserNotFoundException, QueryStoreInsertException, @@ -323,11 +302,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); - when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) + when(subsetService.findById(eq(DATABASE_3_PRIVILEGED_DTO), anyLong())) .thenReturn(QUERY_5_DTO); + when(databaseService.inspectView(any(DatabaseDto.class), anyString())) + .thenReturn(QUERY_5_VIEW_DTO); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -367,11 +348,17 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.create(any(DatabaseDto.class), eq(QUERY_5_STATEMENT), any(Instant.class), eq(USER_1_ID))) + .thenReturn(QUERY_5_ID); + when(subsetService.findById(any(DatabaseDto.class), eq(QUERY_5_ID))) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); + when(databaseService.createView(any(DatabaseDto.class), any(CreateViewDto.class))) + .thenReturn(QUERY_5_VIEW_DTO); + when(databaseService.inspectView(any(DatabaseDto.class), anyString())) + .thenReturn(QUERY_5_VIEW_DTO); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -379,38 +366,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { subsetEndpoint.create(DATABASE_3_ID, request, USER_1_PRINCIPAL, httpServletRequest, null, null, null); } - @Test - @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-query"}) - public void create_unavailable_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, - SQLException, MetadataServiceException, QueryMalformedException, TableNotFoundException, - ViewMalformedException, UserNotFoundException, QueryNotFoundException, QueryStoreInsertException, - NotAllowedException { - final ExecuteStatementDto request = ExecuteStatementDto.builder() - .statement(QUERY_5_STATEMENT) - .build(); - - /* mock */ - when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - doThrow(SQLException.class) - .when(subsetService) - .getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString()); - when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) - .thenReturn(QUERY_5_DTO); - when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_1_ID)) - .thenReturn(DATABASE_3_USER_1_READ_ACCESS_DTO); - doThrow(SQLException.class) - .when(subsetService) - .create(eq(DATABASE_3_DTO), eq(QUERY_5_STATEMENT), any(Instant.class), eq(USER_1_ID)); - when(httpServletRequest.getMethod()) - .thenReturn("POST"); - - /* test */ - assertThrows(DatabaseUnavailableException.class, () -> { - subsetEndpoint.create(DATABASE_3_ID, request, USER_1_PRINCIPAL, httpServletRequest, null, null, null); - }); - } - @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-query"}) public void create_databaseNotFound_fails() throws RemoteUnavailableException, @@ -432,32 +387,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { }); } - @Test - @WithMockUser(username = USER_4_USERNAME) - public void create_noRole_fails() throws DatabaseNotFoundException, RemoteUnavailableException, - MetadataServiceException, UserNotFoundException, QueryStoreInsertException, TableMalformedException, - NotAllowedException, SQLException, QueryNotFoundException, DatabaseUnavailableException, - StorageUnavailableException, QueryMalformedException, QueryNotSupportedException, PaginationException, - StorageNotFoundException, TableNotFoundException, ViewMalformedException, ViewNotFoundException { - final Dataset<Row> mock = sparkSession.emptyDataFrame(); - final ExecuteStatementDto request = ExecuteStatementDto.builder() - .statement(QUERY_5_STATEMENT) - .build(); - - /* mock */ - when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) - .thenReturn(mock); - when(subsetService.findById(eq(DATABASE_3_DTO), anyLong())) - .thenReturn(QUERY_5_DTO); - when(httpServletRequest.getMethod()) - .thenReturn("POST"); - - /* test */ - subsetEndpoint.create(DATABASE_3_ID, request, USER_4_PRINCIPAL, httpServletRequest, null, null, null); - } - @Test @WithAnonymousUser public void create_publicDataPublicSchemaAnonymous_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, @@ -472,11 +401,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_4_ID)) - .thenReturn(DATABASE_4_DTO); - when(subsetService.findById(eq(DATABASE_4_DTO), anyLong())) + .thenReturn(DATABASE_4_PRIVILEGED_DTO); + when(subsetService.findById(eq(DATABASE_4_PRIVILEGED_DTO), anyLong())) .thenReturn(QUERY_5_DTO); - when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); + when(databaseService.inspectView(any(DatabaseDto.class), anyString())) + .thenReturn(QUERY_5_VIEW_DTO); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -498,11 +429,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); - when(subsetService.findById(eq(DATABASE_1_DTO), anyLong())) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(subsetService.findById(eq(DATABASE_1_PRIVILEGED_DTO), anyLong())) .thenReturn(QUERY_1_DTO); - when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); + when(databaseService.inspectView(any(DatabaseDto.class), anyString())) + .thenReturn(QUERY_1_VIEW_DTO); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -522,10 +455,10 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_2_ID)) - .thenReturn(DATABASE_2_DTO); - when(subsetService.findById(eq(DATABASE_2_DTO), anyLong())) + .thenReturn(DATABASE_2_PRIVILEGED_DTO); + when(subsetService.findById(eq(DATABASE_2_PRIVILEGED_DTO), anyLong())) .thenReturn(QUERY_2_DTO); - when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("POST"); @@ -540,18 +473,20 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void getData_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, NotAllowedException, SQLException, QueryNotFoundException, TableMalformedException, QueryMalformedException, DatabaseUnavailableException, PaginationException, MetadataServiceException, TableNotFoundException, - ViewNotFoundException { + ViewNotFoundException, ViewMalformedException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(subsetService.reExecuteCount(DATABASE_3_DTO, QUERY_5_DTO)) + when(subsetService.reExecuteCount(DATABASE_3_PRIVILEGED_DTO, QUERY_5_DTO)) .thenReturn(QUERY_5_RESULT_NUMBER); - when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), any(SortTypeDto.class), anyString())) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); + when(databaseService.inspectView(any(DatabaseDto.class), anyString())) + .thenReturn(QUERY_5_VIEW_DTO); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -565,14 +500,14 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void getData_head_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, NotAllowedException, SQLException, QueryNotFoundException, TableMalformedException, QueryMalformedException, DatabaseUnavailableException, PaginationException, MetadataServiceException, - TableNotFoundException, ViewNotFoundException { + TableNotFoundException, ViewNotFoundException, ViewMalformedException { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); - when(subsetService.reExecuteCount(DATABASE_3_DTO, QUERY_5_DTO)) + when(subsetService.reExecuteCount(DATABASE_3_PRIVILEGED_DTO, QUERY_5_DTO)) .thenReturn(QUERY_5_RESULT_NUMBER); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); @@ -590,18 +525,20 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void getData_private_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, NotAllowedException, TableMalformedException, QueryMalformedException, QueryNotFoundException, PaginationException, SQLException, - MetadataServiceException, TableNotFoundException, ViewNotFoundException { + MetadataServiceException, TableNotFoundException, ViewNotFoundException, ViewMalformedException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); - when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) .thenReturn(QUERY_1_DTO); - when(subsetService.reExecuteCount(DATABASE_1_DTO, QUERY_1_DTO)) + when(subsetService.reExecuteCount(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO)) .thenReturn(QUERY_1_RESULT_NUMBER); - when(subsetService.getData(DATABASE_1_DTO, QUERY_1_STATEMENT, Instant.now(), 0L, 10L, null, null)) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); + when(databaseService.inspectView(any(DatabaseDto.class), anyString())) + .thenReturn(QUERY_1_VIEW_DTO); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -618,7 +555,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -633,7 +570,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_1_ID); @@ -649,14 +586,14 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void getData_privateHead_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, NotAllowedException, TableMalformedException, QueryMalformedException, QueryNotFoundException, PaginationException, SQLException, - MetadataServiceException, TableNotFoundException, ViewNotFoundException { + MetadataServiceException, TableNotFoundException, ViewNotFoundException, ViewMalformedException { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); - when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID)) .thenReturn(QUERY_1_DTO); - when(subsetService.reExecuteCount(DATABASE_1_DTO, QUERY_1_DTO)) + when(subsetService.reExecuteCount(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO)) .thenReturn(QUERY_1_RESULT_NUMBER); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); @@ -669,29 +606,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { assertEquals(QUERY_1_RESULT_NUMBER, Long.parseLong(response.getHeaders().get("X-Count").get(0))); } - @Test - @WithMockUser(username = USER_1_USERNAME) - public void getData_unavailable_fails() throws DatabaseNotFoundException, RemoteUnavailableException, SQLException, - UserNotFoundException, QueryNotFoundException, MetadataServiceException, QueryMalformedException, - TableNotFoundException { - - /* mock */ - when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); - when(subsetService.findById(DATABASE_1_DTO, QUERY_1_ID)) - .thenReturn(QUERY_1_DTO); - when(httpServletRequest.getMethod()) - .thenReturn("GET"); - doThrow(SQLException.class) - .when(subsetService) - .getData(DATABASE_1_DTO, QUERY_1_STATEMENT, Instant.now(), 0L, 10L, null, null); - - /* test */ - assertThrows(DatabaseUnavailableException.class, () -> { - subsetEndpoint.getData(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, httpServletRequest, null, null, null); - }); - } - @Test @WithMockUser(username = USER_3_USERNAME, authorities = {"persist-query"}) public void persist_succeeds() throws NotAllowedException, RemoteUnavailableException, DatabaseNotFoundException, @@ -705,11 +619,11 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); + .thenReturn(DATABASE_3_PRIVILEGED_DTO); doNothing() .when(subsetService) - .persist(DATABASE_3_DTO, QUERY_5_ID, true); - when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) + .persist(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID, true); + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); /* test */ @@ -778,12 +692,12 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); + .thenReturn(DATABASE_3_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); doThrow(SQLException.class) .when(subsetService) - .persist(DATABASE_3_DTO, QUERY_5_ID, true); + .persist(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID, true); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { 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 ecd745e3ee..b37007a010 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 @@ -1,6 +1,7 @@ package at.tuwien.endpoint; import at.tuwien.api.database.DatabaseAccessDto; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.table.*; import at.tuwien.endpoints.TableEndpoint; @@ -101,11 +102,11 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); - when(databaseService.createTable(DATABASE_1_DTO, TABLE_4_CREATE_INTERNAL_DTO)) - .thenReturn(TABLE_4_DTO); - when(databaseService.inspectTable(DATABASE_1_DTO, TABLE_4_INTERNALNAME)) - .thenReturn(TABLE_4_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(databaseService.createTable(DATABASE_1_PRIVILEGED_DTO, TABLE_4_CREATE_INTERNAL_DTO)) + .thenReturn(TABLE_4_PRIVILEGED_DTO); + when(databaseService.inspectTable(DATABASE_1_PRIVILEGED_DTO, TABLE_4_INTERNALNAME)) + .thenReturn(TABLE_4_PRIVILEGED_DTO); /* test */ final ResponseEntity<TableDto> response = tableEndpoint.create(DATABASE_1_ID, TABLE_4_CREATE_INTERNAL_DTO); @@ -145,10 +146,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doThrow(SQLException.class) .when(databaseService) - .createTable(DATABASE_1_DTO, TABLE_4_CREATE_INTERNAL_DTO); + .createTable(DATABASE_1_PRIVILEGED_DTO, TABLE_4_CREATE_INTERNAL_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -173,7 +174,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(tableService.getStatistics(any(TableDto.class))) .thenReturn(TABLE_8_STATISTIC_DTO); @@ -189,7 +190,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); doThrow(SQLException.class) .when(tableService) .getStatistics(any(TableDto.class)); @@ -223,10 +224,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); doNothing() .when(tableService) - .delete(TABLE_1_DTO); + .delete(TABLE_1_PRIVILEGED_DTO); /* test */ final ResponseEntity<Void> response = tableEndpoint.delete(DATABASE_1_ID, TABLE_1_ID); @@ -266,10 +267,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); doThrow(SQLException.class) .when(tableService) - .delete(TABLE_1_DTO); + .delete(TABLE_1_PRIVILEGED_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -279,15 +280,17 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void getData_succeeds() throws DatabaseUnavailableException, TableNotFoundException, QueryMalformedException, + public void getData_publicDataPrivateSchema_succeeds() throws DatabaseUnavailableException, TableNotFoundException, QueryMalformedException, RemoteUnavailableException, PaginationException, MetadataServiceException, NotAllowedException, DatabaseNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); - when(subsetService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(credentialService.getDatabase(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -307,10 +310,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); - when(tableService.getCount(eq(TABLE_8_DTO), any(Instant.class))) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(tableService.getCount(eq(TABLE_8_PRIVILEGED_DTO), any(Instant.class))) .thenReturn(3L); - when(subsetService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.getData(eq(DATABASE_3_PRIVILEGED_DTO), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); @@ -332,7 +335,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -347,7 +350,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_2_ID); @@ -361,14 +364,16 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser public void getData_unavailable_fails() throws TableNotFoundException, RemoteUnavailableException, - MetadataServiceException, QueryMalformedException { + MetadataServiceException, QueryMalformedException, DatabaseNotFoundException { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(credentialService.getDatabase(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); doThrow(QueryMalformedException.class) .when(subsetService) - .getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null)); + .getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null)); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -385,7 +390,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); doThrow(RemoteUnavailableException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_2_ID); @@ -406,10 +411,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); + when(credentialService.getDatabase(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(access); - when(subsetService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -449,12 +456,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .createTuple(TABLE_8_DTO, request); + .createTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -515,7 +522,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); @@ -539,12 +546,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doThrow(SQLException.class) .when(tableService) - .createTuple(TABLE_8_DTO, request); + .createTuple(TABLE_8_PRIVILEGED_DTO, request); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -566,7 +573,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); @@ -587,7 +594,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); @@ -611,7 +618,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); @@ -636,12 +643,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .updateTuple(TABLE_8_DTO, request); + .updateTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -711,7 +718,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); @@ -737,12 +744,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); doThrow(SQLException.class) .when(tableService) - .updateTuple(TABLE_8_DTO, request); + .updateTuple(TABLE_8_PRIVILEGED_DTO, request); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -767,12 +774,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .updateTuple(TABLE_8_DTO, request); + .updateTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -798,7 +805,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); @@ -825,12 +832,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); doNothing() .when(tableService) - .updateTuple(TABLE_8_DTO, request); + .updateTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -853,12 +860,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .deleteTuple(TABLE_8_DTO, request); + .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -916,7 +923,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); @@ -938,12 +945,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); doThrow(SQLException.class) .when(tableService) - .deleteTuple(TABLE_8_DTO, request); + .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -964,12 +971,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .deleteTuple(TABLE_8_DTO, request); + .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -991,7 +998,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); @@ -1014,12 +1021,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); doNothing() .when(tableService) - .deleteTuple(TABLE_8_DTO, request); + .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -1036,8 +1043,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); - when(tableService.history(TABLE_8_DTO, null)) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(tableService.history(TABLE_8_PRIVILEGED_DTO, null)) .thenReturn(List.of()); /* test */ @@ -1052,7 +1059,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); /* test */ assertThrows(NotAllowedException.class, () -> { @@ -1078,7 +1085,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_4_ID); @@ -1096,10 +1103,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(DATABASE_1_USER_2_READ_ACCESS_DTO); - when(tableService.history(TABLE_1_DTO, 10L)) + when(tableService.history(TABLE_1_PRIVILEGED_DTO, 10L)) .thenReturn(List.of()); /* test */ @@ -1114,10 +1121,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); doThrow(SQLException.class) .when(tableService) - .history(TABLE_8_DTO, 100L); + .history(TABLE_8_PRIVILEGED_DTO, 100L); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -1143,14 +1150,16 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void exportData_succeeds() throws TableNotFoundException, NotAllowedException, StorageUnavailableException, + public void exportData_publicDataPrivateSchema_succeeds() throws TableNotFoundException, NotAllowedException, StorageUnavailableException, QueryMalformedException, RemoteUnavailableException, MetadataServiceException, DatabaseNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); - when(subsetService.getData(eq(DATABASE_3_DTO), eq(TABLE_8_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + .thenReturn(TABLE_8_PRIVILEGED_DTO); + when(credentialService.getDatabase(DATABASE_3_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) .thenReturn(mock); /* test */ @@ -1161,17 +1170,19 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @ParameterizedTest @WithMockUser(username = USER_2_USERNAME) @MethodSource("anyAccess_parameters") - public void exportData_private_succeeds(String name, DatabaseAccessDto access) throws TableNotFoundException, - NotAllowedException, StorageUnavailableException, QueryMalformedException, RemoteUnavailableException, - MetadataServiceException, DatabaseNotFoundException { + public void exportData_privateDataPrivateSchema_succeeds(String name, DatabaseAccessDto access) + throws TableNotFoundException, NotAllowedException, StorageUnavailableException, QueryMalformedException, + RemoteUnavailableException, MetadataServiceException, DatabaseNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(access); - when(subsetService.getData(eq(DATABASE_1_DTO), eq(TABLE_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(credentialService.getDatabase(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(null), eq(null), eq(null), eq(null))) .thenReturn(mock); /* test */ @@ -1186,7 +1197,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_4_ID); @@ -1205,9 +1216,9 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(databaseService.exploreTables(DATABASE_3_DTO)) - .thenReturn(List.of(TABLE_8_DTO)); + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(databaseService.exploreTables(DATABASE_3_PRIVILEGED_DTO)) + .thenReturn(List.of(TABLE_8_PRIVILEGED_DTO)); /* test */ final ResponseEntity<List<TableDto>> response = tableEndpoint.getSchema(DATABASE_3_ID); @@ -1241,10 +1252,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); + .thenReturn(DATABASE_3_PRIVILEGED_DTO); doThrow(SQLException.class) .when(databaseService) - .exploreTables(DATABASE_3_DTO); + .exploreTables(DATABASE_3_PRIVILEGED_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -1264,12 +1275,12 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); doNothing() .when(tableService) - .importDataset(TABLE_8_DTO, request); + .importDataset(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); @@ -1328,7 +1339,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); doThrow(SQLException.class) @@ -1354,7 +1365,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); doThrow(SQLException.class) @@ -1379,7 +1390,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_READ_ACCESS_DTO); @@ -1402,7 +1413,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); @@ -1422,7 +1433,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_3_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_OWN_ACCESS_DTO); @@ -1445,7 +1456,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); + .thenReturn(TABLE_8_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_3_ID, USER_1_ID)) .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); @@ -1466,7 +1477,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(DATABASE_1_USER_2_WRITE_ALL_ACCESS_DTO); @@ -1487,7 +1498,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_2_ID)) - .thenReturn(TABLE_2_DTO); + .thenReturn(TABLE_2_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO); @@ -1507,7 +1518,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO); @@ -1529,7 +1540,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getTable(DATABASE_1_ID, TABLE_2_ID)) - .thenReturn(TABLE_2_DTO); + .thenReturn(TABLE_2_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_2_ID)) .thenReturn(DATABASE_1_USER_2_READ_ACCESS_DTO); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java index c69c8d338e..5dce498856 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java @@ -1,5 +1,6 @@ package at.tuwien.endpoint; +import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.ViewDto; import at.tuwien.endpoints.ViewEndpoint; import at.tuwien.exception.*; @@ -71,9 +72,9 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); - when(databaseService.createView(DATABASE_1_DTO, VIEW_1_CREATE_DTO)) - .thenReturn(VIEW_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(databaseService.createView(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO)) + .thenReturn(VIEW_1_PRIVILEGED_DTO); /* test */ final ResponseEntity<ViewDto> response = viewEndpoint.create(DATABASE_1_ID, VIEW_1_CREATE_DTO); @@ -87,10 +88,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doThrow(SQLException.class) .when(databaseService) - .createView(DATABASE_1_DTO, VIEW_1_CREATE_DTO); + .createView(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -105,9 +106,9 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); - when(databaseService.createView(DATABASE_1_DTO, VIEW_1_CREATE_DTO)) - .thenReturn(VIEW_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(databaseService.createView(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO)) + .thenReturn(VIEW_1_PRIVILEGED_DTO); /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { @@ -138,8 +139,8 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); - when(databaseService.exploreViews(DATABASE_1_DTO)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); + when(databaseService.exploreViews(DATABASE_1_PRIVILEGED_DTO)) .thenReturn(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO)); /* test */ @@ -180,10 +181,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doThrow(SQLException.class) .when(databaseService) - .exploreViews(DATABASE_1_DTO); + .exploreViews(DATABASE_1_PRIVILEGED_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -208,10 +209,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_DTO); + .thenReturn(VIEW_1_PRIVILEGED_DTO); doNothing() .when(viewService) - .delete(VIEW_1_DTO); + .delete(VIEW_1_PRIVILEGED_DTO); /* test */ final ResponseEntity<Void> response = viewEndpoint.delete(DATABASE_1_ID, VIEW_1_ID); @@ -225,10 +226,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_DTO); + .thenReturn(VIEW_1_PRIVILEGED_DTO); doThrow(SQLException.class) .when(viewService) - .delete(VIEW_1_DTO); + .delete(VIEW_1_PRIVILEGED_DTO); /* test */ assertThrows(DatabaseUnavailableException.class, () -> { @@ -243,10 +244,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getDatabase(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); doNothing() .when(viewService) - .delete(VIEW_1_DTO); + .delete(VIEW_1_PRIVILEGED_DTO); /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { @@ -272,17 +273,19 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"view-database-view-data"}) - public void getData_private_succeeds() throws RemoteUnavailableException, ViewNotFoundException, + public void getData_privateDataPrivateSchema_succeeds() throws RemoteUnavailableException, ViewNotFoundException, SQLException, DatabaseUnavailableException, QueryMalformedException, PaginationException, NotAllowedException, MetadataServiceException, TableNotFoundException, DatabaseNotFoundException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_DTO); + .thenReturn(VIEW_1_PRIVILEGED_DTO); + when(credentialService.getDatabase(DATABASE_1_ID)) + .thenReturn(DATABASE_1_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_1_ID)) .thenReturn(DATABASE_1_USER_1_READ_ACCESS_DTO); - when(subsetService.getData(eq(DATABASE_1_DTO), eq(VIEW_1_INTERNAL_NAME), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) + when(subsetService.getData(any(DatabaseDto.class), anyString(), any(Instant.class), eq(0L), eq(10L), eq(null), eq(null))) .thenReturn(mock); when(httpServletRequest.getMethod()) .thenReturn("GET"); @@ -301,12 +304,12 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID)) - .thenReturn(VIEW_3_DTO); + .thenReturn(VIEW_3_PRIVILEGED_DTO); when(credentialService.getAccess(DATABASE_1_ID, USER_1_ID)) .thenReturn(DATABASE_1_USER_1_READ_ACCESS_DTO); when(httpServletRequest.getMethod()) .thenReturn("HEAD"); - when(viewService.count(eq(VIEW_3_DTO), any(Instant.class))) + when(viewService.count(eq(VIEW_3_PRIVILEGED_DTO), any(Instant.class))) .thenReturn(VIEW_3_DATA_COUNT); /* test */ @@ -328,7 +331,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID)) - .thenReturn(VIEW_3_DTO); + .thenReturn(VIEW_3_PRIVILEGED_DTO); when(httpServletRequest.getMethod()) .thenReturn("GET"); doThrow(NotAllowedException.class) @@ -364,7 +367,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID)) - .thenReturn(VIEW_3_DTO); + .thenReturn(VIEW_3_PRIVILEGED_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_3_ID); @@ -382,7 +385,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID)) - .thenReturn(VIEW_3_DTO); + .thenReturn(VIEW_3_PRIVILEGED_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_3_ID); @@ -416,7 +419,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest { /* mock */ when(credentialService.getView(DATABASE_1_ID, VIEW_3_ID)) - .thenReturn(VIEW_3_DTO); + .thenReturn(VIEW_3_PRIVILEGED_DTO); doThrow(NotAllowedException.class) .when(credentialService) .getAccess(DATABASE_1_ID, USER_1_ID); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java index a2ed7e3817..affb562080 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java @@ -59,7 +59,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class))) .thenReturn(ResponseEntity.status(HttpStatus.OK) .headers(headers) - .body(TABLE_1_DTO)); + .body(TABLE_1_PRIVILEGED_DTO)); /* test */ final TableDto response = metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID); @@ -105,7 +105,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* mock */ when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class))) .thenReturn(ResponseEntity.status(HttpStatus.NO_CONTENT) - .body(TABLE_1_DTO)); + .body(TABLE_1_PRIVILEGED_DTO)); /* test */ assertThrows(MetadataServiceException.class, () -> { @@ -115,7 +115,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { @Test public void getTableById_headerMissing_fails() { - final List<String> customHeaders = List.of("X-Type", "X-Host", "X-Port", "X-Username", "X-Password", "X-Database", "X-Sidecar-Host", "X-Sidecar-Port"); + final List<String> customHeaders = List.of("X-Username", "X-Password"); for (int i = 0; i < customHeaders.size(); i++) { final HttpHeaders headers = new HttpHeaders(); @@ -126,7 +126,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(TableDto.class))) .thenReturn(ResponseEntity.status(HttpStatus.OK) .headers(headers) - .body(TABLE_1_DTO)); + .body(TABLE_1_PRIVILEGED_DTO)); /* test */ assertThrows(MetadataServiceException.class, () -> { metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID); @@ -163,7 +163,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(HttpEntity.EMPTY), eq(DatabaseDto.class))) .thenReturn(ResponseEntity.ok() .headers(headers) - .body(DATABASE_1_DTO)); + .body(DATABASE_1_PRIVILEGED_DTO)); /* test */ final DatabaseDto response = metadataServiceGateway.getDatabaseById(DATABASE_1_ID); @@ -359,7 +359,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { when(internalRestTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(ViewDto.class))) .thenReturn(ResponseEntity.ok() .headers(headers) - .body(VIEW_1_DTO)); + .body(VIEW_1_PRIVILEGED_DTO)); /* test */ final ViewDto response = metadataServiceGateway.getViewById(CONTAINER_1_ID, VIEW_1_ID); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java index 752a9b0e05..bca5e11410 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java @@ -5,7 +5,7 @@ import at.tuwien.config.MariaDbContainerConfig; import at.tuwien.exception.MetadataServiceException; import at.tuwien.exception.RemoteUnavailableException; import at.tuwien.exception.TableNotFoundException; -import at.tuwien.gateway.MetadataServiceGateway; +import at.tuwien.service.CredentialService; import at.tuwien.test.AbstractUnitTest; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; @@ -42,13 +42,13 @@ import static org.mockito.Mockito.when; public class DefaultListenerIntegrationTest extends AbstractUnitTest { @MockBean - private MetadataServiceGateway metadataServiceGateway; + private CredentialService credentialService; @Autowired private DefaultListener defaultListener; @Container - private static RabbitMQContainer rabbitContainer = new RabbitMQContainer("rabbitmq:3.10"); + private static RabbitMQContainer rabbitContainer = new RabbitMQContainer(RABBITMQ_IMAGE); @Container private static MariaDBContainer<?> mariaDBContainer = MariaDbContainerConfig.getContainer(); @@ -67,8 +67,8 @@ public class DefaultListenerIntegrationTest extends AbstractUnitTest { final Message request = buildMessage("dbrepo." + DATABASE_1_ID + "." + TABLE_1_ID, "{\"id\":4,\"date\":\"2023-10-03\",\"mintemp\":15.0,\"rainfall\":0.2}", new HashMap<>()); /* mock */ - when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); /* test */ defaultListener.onMessage(request); @@ -83,8 +83,8 @@ public class DefaultListenerIntegrationTest extends AbstractUnitTest { /* mock */ doThrow(TableNotFoundException.class) - .when(metadataServiceGateway) - .getTableById(DATABASE_1_ID, TABLE_1_ID); + .when(credentialService) + .getTable(DATABASE_1_ID, TABLE_1_ID); /* test */ defaultListener.onMessage(request); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java index 7a439ed55c..74042aa578 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java @@ -5,7 +5,7 @@ import at.tuwien.config.MariaDbContainerConfig; import at.tuwien.exception.MetadataServiceException; import at.tuwien.exception.RemoteUnavailableException; import at.tuwien.exception.TableNotFoundException; -import at.tuwien.gateway.MetadataServiceGateway; +import at.tuwien.service.CredentialService; import at.tuwien.test.AbstractUnitTest; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; @@ -38,7 +38,7 @@ import static org.mockito.Mockito.when; public class DefaultListenerUnitTest extends AbstractUnitTest { @MockBean - private MetadataServiceGateway metadataServiceGateway; + private CredentialService credentialService; @Autowired private DefaultListener defaultListener; @@ -54,7 +54,7 @@ public class DefaultListenerUnitTest extends AbstractUnitTest { genesis(); /* metadata database */ MariaDbConfig.dropAllDatabases(CONTAINER_1_PRIVILEGED_DTO); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_PRIVILEGED_DTO); } @Test @@ -81,8 +81,8 @@ public class DefaultListenerUnitTest extends AbstractUnitTest { final Message request = buildMessage("dbrepo.1.1", "{,}", new HashMap<>()); /* mock */ - when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + when(credentialService.getTable(DATABASE_1_ID, TABLE_1_ID)) + .thenReturn(TABLE_1_PRIVILEGED_DTO); /* test */ defaultListener.onMessage(request); @@ -96,8 +96,8 @@ public class DefaultListenerUnitTest extends AbstractUnitTest { /* mock */ doThrow(TableNotFoundException.class) - .when(metadataServiceGateway) - .getTableById(DATABASE_1_ID, TABLE_1_ID); + .when(credentialService) + .getTable(DATABASE_1_ID, TABLE_1_ID); /* test */ defaultListener.onMessage(request); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java index abbe28d55a..158c6743a4 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SubsetEndpointMvcTest.java @@ -41,8 +41,8 @@ public class SubsetEndpointMvcTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); /* test */ @@ -56,8 +56,8 @@ public class SubsetEndpointMvcTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) - .thenReturn(DATABASE_3_DTO); - when(subsetService.findById(DATABASE_3_DTO, QUERY_5_ID)) + .thenReturn(DATABASE_3_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_3_PRIVILEGED_DTO, QUERY_5_ID)) .thenReturn(QUERY_5_DTO); /* test */ @@ -72,8 +72,8 @@ public class SubsetEndpointMvcTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_4_ID)) - .thenReturn(DATABASE_4_DTO); - when(subsetService.findById(DATABASE_4_DTO, QUERY_7_ID)) + .thenReturn(DATABASE_4_PRIVILEGED_DTO); + when(subsetService.findById(DATABASE_4_PRIVILEGED_DTO, QUERY_7_ID)) .thenReturn(QUERY_5_DTO); /* test */ diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java index 251dfe53c5..e1579fbe95 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java @@ -42,17 +42,22 @@ public class ContainerServiceIntegrationTest extends AbstractUnitTest { } @BeforeEach - public void beforeEach() throws SQLException { + public void beforeEach() throws SQLException, InterruptedException { genesis(); /* metadata database */ MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.createDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + Thread.sleep(1000) /* wait for test container some more */; } @Test public void create_succeeds() throws SQLException, DatabaseMalformedException { + /* mock */ + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + /* test */ - final DatabaseDto response = containerService.createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL); + final DatabaseDto response = containerService.createDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_CREATE_INTERNAL); assertNull(response.getName()); assertEquals(DATABASE_1_INTERNALNAME, response.getInternalName()); assertEquals(EXCHANGE_DBREPO_NAME, response.getExchangeName()); @@ -65,45 +70,26 @@ public class ContainerServiceIntegrationTest extends AbstractUnitTest { } @Test - public void create_exists_fails() throws SQLException { - - /* mock */ - MariaDbConfig.createDatabase(CONTAINER_1_DTO, DATABASE_1_INTERNALNAME); + public void create_exists_fails() { /* test */ assertThrows(DatabaseMalformedException.class, () -> { - containerService.createDatabase(CONTAINER_1_DTO, DATABASE_1_CREATE_INTERNAL); + containerService.createDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_CREATE_INTERNAL); }); } @Test public void createQueryStore_succeeds() throws SQLException, QueryStoreCreateException, InterruptedException { - /* mock */ - MariaDbConfig.dropQueryStore(DATABASE_1_DTO); - /* test */ createQueryStore_generic(DATABASE_1_INTERNALNAME); } - @Test - public void createQueryStore_fails() { - - /* test */ - assertThrows(QueryStoreCreateException.class, () -> { - createQueryStore_generic(DATABASE_1_INTERNALNAME); - }); - } - - protected void createQueryStore_generic(String databaseName) throws SQLException, QueryStoreCreateException, - InterruptedException { - - /* pre-condition */ - Thread.sleep(1000) /* wait for test container some more */; + protected void createQueryStore_generic(String databaseName) throws SQLException, QueryStoreCreateException { /* test */ - containerService.createQueryStore(CONTAINER_1_DTO, databaseName); - final List<Map<String, Object>> response = MariaDbConfig.listQueryStore(DATABASE_1_DTO); + containerService.createQueryStore(CONTAINER_1_PRIVILEGED_DTO, databaseName); + final List<Map<String, Object>> response = MariaDbConfig.listQueryStore(DATABASE_1_PRIVILEGED_DTO); assertEquals(0, response.size()); } } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/CredentialServiceUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/CredentialServiceUnitTest.java index 8b5e2cc7c5..07e283640a 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/CredentialServiceUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/CredentialServiceUnitTest.java @@ -48,7 +48,7 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_1_PRIVILEGED_DTO); /* test */ final DatabaseDto response = credentialService.getDatabase(DATABASE_1_ID); @@ -62,7 +62,7 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) - .thenReturn(DATABASE_1_DTO) + .thenReturn(DATABASE_1_PRIVILEGED_DTO) .thenThrow(RuntimeException.class) /* should never be thrown */; credentialService.getDatabase(DATABASE_1_ID); @@ -78,8 +78,8 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getDatabaseById(DATABASE_1_ID)) - .thenReturn(DATABASE_2_DTO) /* needs to be different id for test case */ - .thenReturn(DATABASE_1_DTO); + .thenReturn(DATABASE_2_PRIVILEGED_DTO) /* needs to be different id for test case */ + .thenReturn(DATABASE_1_PRIVILEGED_DTO); /* pre-condition */ final DatabaseDto tmp = credentialService.getDatabase(DATABASE_1_ID); @@ -98,7 +98,7 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); + .thenReturn(CONTAINER_1_PRIVILEGED_DTO); /* test */ final ContainerDto response = credentialService.getContainer(CONTAINER_1_ID); @@ -128,8 +128,8 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(DATABASE_1_ID)) - .thenReturn(CONTAINER_2_DTO) /* needs to be different id for test case */ - .thenReturn(CONTAINER_1_DTO); + .thenReturn(CONTAINER_2_PRIVILEGED_DTO) /* needs to be different id for test case */ + .thenReturn(CONTAINER_1_PRIVILEGED_DTO); /* pre-condition */ final ContainerDto tmp = credentialService.getContainer(CONTAINER_1_ID); @@ -251,7 +251,7 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); /* test */ final TableDto response = credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); @@ -265,7 +265,7 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO) + .thenReturn(TABLE_1_PRIVILEGED_DTO) .thenThrow(RuntimeException.class) /* should never be thrown */; credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); @@ -281,8 +281,8 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_2_DTO) /* needs to be different id for test case */ - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_2_PRIVILEGED_DTO) /* needs to be different id for test case */ + .thenReturn(TABLE_1_PRIVILEGED_DTO); /* pre-condition */ final TableDto tmp = credentialService.getTable(DATABASE_1_ID, TABLE_1_ID); @@ -301,7 +301,7 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_DTO); + .thenReturn(VIEW_1_PRIVILEGED_DTO); /* test */ final ViewDto response = credentialService.getView(DATABASE_1_ID, VIEW_1_ID); @@ -314,7 +314,7 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_1_DTO) + .thenReturn(VIEW_1_PRIVILEGED_DTO) .thenThrow(RuntimeException.class) /* should never be thrown */; credentialService.getView(DATABASE_1_ID, VIEW_1_ID); @@ -330,8 +330,8 @@ public class CredentialServiceUnitTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getViewById(DATABASE_1_ID, VIEW_1_ID)) - .thenReturn(VIEW_2_DTO) /* needs to be different id for test case */ - .thenReturn(VIEW_1_DTO); + .thenReturn(VIEW_2_PRIVILEGED_DTO) /* needs to be different id for test case */ + .thenReturn(VIEW_1_PRIVILEGED_DTO); /* pre-condition */ final ViewDto tmp = credentialService.getView(DATABASE_1_ID, VIEW_1_ID); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java index 550589df79..6663a71d13 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java @@ -60,31 +60,36 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { } @BeforeEach - public void beforeEach() throws SQLException { + public void beforeEach() throws SQLException, InterruptedException { genesis(); /* metadata database */ MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_DTO); + Thread.sleep(1000) /* wait for test container some more */; } @Test public void createView_succeeds() throws SQLException, ViewMalformedException { /* test */ - databaseService.createView(DATABASE_1_DTO, VIEW_1_CREATE_DTO); + databaseService.createView(DATABASE_1_PRIVILEGED_DTO, VIEW_1_CREATE_DTO); } @Test public void exploreViews_succeeds() throws SQLException, ViewNotFoundException, DatabaseMalformedException { /* test */ - final List<ViewDto> response = databaseService.exploreViews(DATABASE_1_DTO); + final List<ViewDto> response = databaseService.exploreViews(DATABASE_1_PRIVILEGED_DTO); final ViewDto view0 = response.get(0); assertEquals("not_in_metadata_db2", view0.getName()); assertEquals("not_in_metadata_db2", view0.getInternalName()); assertEquals(DATABASE_1_ID, view0.getVdbid()); - assertEquals(DATABASE_1_OWNER, view0.getOwner().getId()); + assertEquals(USER_1_BRIEF_DTO, view0.getOwner()); assertFalse(view0.getIsInitialView()); assertEquals(DATABASE_1_PUBLIC, view0.getIsPublic()); + assertEquals(DATABASE_1_SCHEMA_PUBLIC, view0.getIsSchemaPublic()); assertTrue(view0.getQuery().length() >= 69); assertNotNull(view0.getQueryHash()); assertEquals(4, view0.getColumns().size()); @@ -106,8 +111,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { .build(); /* mock */ - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); - MariaDbConfig.grantWriteAccess(DATABASE_1_DTO, USER_1_USERNAME); + MariaDbConfig.grantWriteAccess(DATABASE_1_PRIVILEGED_DTO, USER_1_USERNAME); /* pre-condition */ MariaDbConfig.mockQuery(CONTAINER_1_HOST, CONTAINER_1_PORT, DATABASE_1_INTERNALNAME, "CREATE SEQUENCE debug NOCACHE", USER_1_USERNAME, USER_1_PASSWORD); @@ -119,34 +123,31 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { } /* test */ - databaseService.update(DATABASE_1_DTO, request); + databaseService.update(DATABASE_1_PRIVILEGED_DTO, request); MariaDbConfig.mockQuery(CONTAINER_1_HOST, CONTAINER_1_PORT, DATABASE_1_INTERNALNAME, "CREATE SEQUENCE debug2 NOCACHE", USER_1_USERNAME, USER_2_PASSWORD); } @Test - public void update_notExists_fails() throws SQLException { + public void update_notExists_fails() { final UpdateUserPasswordDto request = UpdateUserPasswordDto.builder() .username("i_do_not_exist") .password(USER_1_PASSWORD) .build(); - /* mock */ - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); - /* test */ assertThrows(DatabaseMalformedException.class, () -> { - databaseService.update(DATABASE_1_DTO, request); + databaseService.update(DATABASE_1_PRIVILEGED_DTO, request); }); } - + @Test public void inspectTable_sameNameDifferentDb_succeeds() throws TableNotFoundException, SQLException { /* mock */ - MariaDbConfig.execute(DATABASE_2_DTO, "CREATE TABLE not_in_metadata_db (wrong_id BIGINT NOT NULL PRIMARY KEY, given_name VARCHAR(255) NOT NULL, middle_name VARCHAR(255), family_name VARCHAR(255) NOT NULL, age INT NOT NULL) WITH SYSTEM VERSIONING;"); + MariaDbConfig.execute(DATABASE_2_PRIVILEGED_DTO, "CREATE TABLE not_in_metadata_db (wrong_id BIGINT NOT NULL PRIMARY KEY, given_name VARCHAR(255) NOT NULL, middle_name VARCHAR(255), family_name VARCHAR(255) NOT NULL, age INT NOT NULL) WITH SYSTEM VERSIONING;"); /* test */ - final TableDto response = databaseService.inspectTable(DATABASE_1_DTO, "not_in_metadata_db"); + final TableDto response = databaseService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "not_in_metadata_db"); assertEquals("not_in_metadata_db", response.getInternalName()); assertEquals("not_in_metadata_db", response.getName()); assertEquals(DATABASE_1_ID, response.getTdbid()); @@ -182,7 +183,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { public void inspectTableEnum_succeeds() throws TableNotFoundException, SQLException { /* test */ - final TableDto response = databaseService.inspectTable(DATABASE_2_DTO, "experiments"); + final TableDto response = databaseService.inspectTable(DATABASE_2_PRIVILEGED_DTO, "experiments"); assertEquals("experiments", response.getInternalName()); assertEquals("experiments", response.getName()); assertEquals(DATABASE_2_ID, response.getTdbid()); @@ -215,14 +216,14 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { public void inspectTableFullConstraints_succeeds() throws TableNotFoundException, SQLException { /* test */ - final TableDto response = databaseService.inspectTable(DATABASE_1_DTO, "weather_aus"); + final TableDto response = databaseService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "weather_aus"); assertEquals("weather_aus", response.getInternalName()); assertEquals("weather_aus", response.getName()); assertEquals(DATABASE_1_ID, response.getTdbid()); assertTrue(response.getIsVersioned()); assertEquals(DATABASE_1_PUBLIC, response.getIsPublic()); assertNotNull(response.getOwner()); - assertEquals(DATABASE_1_OWNER, response.getOwner().getId()); + assertEquals(USER_1_BRIEF_DTO, response.getOwner()); assertEquals(USER_1_NAME, response.getOwner().getName()); assertEquals(USER_1_USERNAME, response.getOwner().getUsername()); assertEquals(USER_1_FIRSTNAME, response.getOwner().getFirstname()); @@ -315,7 +316,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { public void inspectTable_multipleForeignKeyReferences_succeeds() throws TableNotFoundException, SQLException { /* test */ - final TableDto response = databaseService.inspectTable(DATABASE_1_DTO, "complex_foreign_keys"); + final TableDto response = databaseService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "complex_foreign_keys"); final ConstraintsDto constraints = response.getConstraints(); final List<ForeignKeyDto> foreignKeys = constraints.getForeignKeys(); assertEquals(1, foreignKeys.size()); @@ -368,7 +369,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { public void inspectTable_multiplePrimaryKey_succeeds() throws TableNotFoundException, SQLException { /* test */ - final TableDto response = databaseService.inspectTable(DATABASE_1_DTO, "complex_primary_key"); + final TableDto response = databaseService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "complex_primary_key"); final ConstraintsDto constraints = response.getConstraints(); final List<PrimaryKeyDto> primaryKey = new LinkedList<>(constraints.getPrimaryKey()); assertEquals(2, primaryKey.size()); @@ -406,7 +407,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { public void inspectTable_exoticBoolean_succeeds() throws TableNotFoundException, SQLException { /* test */ - final TableDto response = databaseService.inspectTable(DATABASE_1_DTO, "exotic_boolean"); + final TableDto response = databaseService.inspectTable(DATABASE_1_PRIVILEGED_DTO, "exotic_boolean"); final ConstraintsDto constraints = response.getConstraints(); final List<PrimaryKeyDto> primaryKey = new LinkedList<>(constraints.getPrimaryKey()); assertEquals(1, primaryKey.size()); @@ -435,11 +436,11 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { public void inspectView_succeeds() throws SQLException, ViewNotFoundException { /* test */ - final ViewDto response = databaseService.inspectView(DATABASE_1_DTO, "not_in_metadata_db2"); + final ViewDto response = databaseService.inspectView(DATABASE_1_PRIVILEGED_DTO, "not_in_metadata_db2"); assertEquals("not_in_metadata_db2", response.getInternalName()); assertEquals("not_in_metadata_db2", response.getName()); assertEquals(DATABASE_1_ID, response.getVdbid()); - assertEquals(DATABASE_1_OWNER, response.getOwner().getId()); + assertEquals(USER_1_BRIEF_DTO, response.getOwner()); assertFalse(response.getIsInitialView()); assertEquals(DATABASE_1_PUBLIC, response.getIsPublic()); assertTrue(response.getQuery().length() >= 69); @@ -467,7 +468,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { public void getSchemas_succeeds() throws TableNotFoundException, SQLException, DatabaseMalformedException { /* test */ - final List<TableDto> response = databaseService.exploreTables(DATABASE_1_DTO); + final List<TableDto> response = databaseService.exploreTables(DATABASE_1_PRIVILEGED_DTO); assertEquals(4, response.size()); final TableDto table0 = response.get(0); Assertions.assertEquals("complex_foreign_keys", table0.getInternalName()); @@ -599,8 +600,8 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { TableExistsException { /* test */ - final TableDto response = databaseService.createTable(DATABASE_1_DTO, TABLE_4_CREATE_INTERNAL_DTO); - assertEquals(TABLE_4_NAME, response.getName()); + final TableDto response = databaseService.createTable(DATABASE_1_PRIVILEGED_DTO, TABLE_4_CREATE_INTERNAL_DTO); + assertEquals(TABLE_4_INTERNALNAME, response.getName()); assertEquals(TABLE_4_INTERNALNAME, response.getInternalName()); final List<ColumnDto> columns = response.getColumns(); assertEquals(TABLE_4_COLUMNS.size(), columns.size()); @@ -630,7 +631,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { /* test */ assertThrows(TableMalformedException.class, () -> { - databaseService.createTable(DATABASE_1_DTO, request); + databaseService.createTable(DATABASE_1_PRIVILEGED_DTO, request); }); } @@ -668,7 +669,7 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { .build(); /* test */ - final TableDto response = databaseService.createTable(DATABASE_1_DTO, request); + final TableDto response = databaseService.createTable(DATABASE_1_PRIVILEGED_DTO, request); assertEquals("composite_primary_key", response.getName()); assertEquals("composite_primary_key", response.getInternalName()); final List<ColumnDto> columns = response.getColumns(); @@ -699,11 +700,11 @@ public class DatabaseServiceIntegrationTest extends AbstractUnitTest { TableExistsException { /* mock */ - MariaDbConfig.dropTable(DATABASE_1_DTO, TABLE_1_INTERNAL_NAME); + MariaDbConfig.dropTable(DATABASE_1_PRIVILEGED_DTO, TABLE_1_INTERNAL_NAME); /* test */ - final TableDto response = databaseService.createTable(DATABASE_1_DTO, TABLE_1_CREATE_INTERNAL_DTO); - assertEquals(TABLE_1_NAME, response.getName()); + final TableDto response = databaseService.createTable(DATABASE_1_PRIVILEGED_DTO, TABLE_1_CREATE_INTERNAL_DTO); + assertEquals(TABLE_1_INTERNAL_NAME, response.getName()); assertEquals(TABLE_1_INTERNAL_NAME, response.getInternalName()); assertEquals(TABLE_1_COLUMNS.size(), response.getColumns().size()); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java index fc9860909f..18b2dd8e01 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java @@ -53,7 +53,7 @@ public class QueueServiceIntegrationTest extends AbstractUnitTest { genesis(); /* metadata database */ MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_PRIVILEGED_DTO); } @Test @@ -69,12 +69,12 @@ public class QueueServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); + .thenReturn(CONTAINER_1_PRIVILEGED_DTO); when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); /* test */ - queueService.insert(TABLE_1_DTO, request); + queueService.insert(TABLE_1_PRIVILEGED_DTO, request); } @Test @@ -87,10 +87,10 @@ public class QueueServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); + .thenReturn(TABLE_1_PRIVILEGED_DTO); /* test */ - queueService.insert(TABLE_1_DTO, request); + queueService.insert(TABLE_1_PRIVILEGED_DTO, request); } } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java index ea58aa1da4..56115690b5 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/SubsetServiceIntegrationTest.java @@ -45,7 +45,7 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { genesis(); /* metadata database */ MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_PRIVILEGED_DTO); } @Test @@ -107,7 +107,7 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { /* test */ persist_generic(QUERY_2_ID, List.of(IDENTIFIER_5_BRIEF_DTO), true); - final QueryDto response = queryService.findById(DATABASE_1_DTO, QUERY_2_ID); + final QueryDto response = queryService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_2_ID); assertEquals(2L, response.getId()); assertTrue(response.getIsPersisted()); } @@ -123,7 +123,7 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { /* test */ persist_generic(QUERY_1_ID, List.of(IDENTIFIER_2_BRIEF_DTO), false); - final QueryDto response = queryService.findById(DATABASE_1_DTO, QUERY_1_ID); + final QueryDto response = queryService.findById(DATABASE_1_PRIVILEGED_DTO, QUERY_1_ID); assertEquals(1L, response.getId()); assertFalse(response.getIsPersisted()); } @@ -140,10 +140,10 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { .thenReturn(List.of(IDENTIFIER_2_BRIEF_DTO)); when(metadataServiceGateway.getUserById(USER_1_ID)) .thenReturn(USER_1_DTO); - MariaDbConfig.insertQueryStore(DATABASE_1_DTO, QUERY_1_DTO, USER_1_ID); + MariaDbConfig.insertQueryStore(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, USER_1_ID); /* test */ - final QueryDto response = queryService.findById(DATABASE_1_DTO, queryId); + final QueryDto response = queryService.findById(DATABASE_1_PRIVILEGED_DTO, queryId); assertEquals(QUERY_1_ID, response.getId()); } @@ -155,13 +155,13 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { Thread.sleep(1000) /* wait for test container some more */; /* mock */ - MariaDbConfig.insertQueryStore(DATABASE_1_DTO, QUERY_1_DTO, USER_1_ID); - MariaDbConfig.insertQueryStore(DATABASE_1_DTO, QUERY_2_DTO, USER_1_ID); + MariaDbConfig.insertQueryStore(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, USER_1_ID); + MariaDbConfig.insertQueryStore(DATABASE_1_PRIVILEGED_DTO, QUERY_2_DTO, USER_1_ID); when(metadataServiceGateway.getIdentifiers(DATABASE_1_ID, null)) .thenReturn(List.of(IDENTIFIER_2_BRIEF_DTO, IDENTIFIER_5_BRIEF_DTO)); /* test */ - return queryService.findAll(DATABASE_1_DTO, filterPersisted); + return queryService.findAll(DATABASE_1_PRIVILEGED_DTO, filterPersisted); } protected void persist_generic(Long queryId, List<IdentifierBriefDto> identifiers, Boolean persist) @@ -174,11 +174,11 @@ public class SubsetServiceIntegrationTest extends AbstractUnitTest { /* mock */ when(metadataServiceGateway.getIdentifiers(DATABASE_1_ID, queryId)) .thenReturn(identifiers); - MariaDbConfig.insertQueryStore(DATABASE_1_DTO, QUERY_1_DTO, USER_1_ID); - MariaDbConfig.insertQueryStore(DATABASE_1_DTO, QUERY_2_DTO, USER_1_ID); + MariaDbConfig.insertQueryStore(DATABASE_1_PRIVILEGED_DTO, QUERY_1_DTO, USER_1_ID); + MariaDbConfig.insertQueryStore(DATABASE_1_PRIVILEGED_DTO, QUERY_2_DTO, USER_1_ID); /* test */ - queryService.persist(DATABASE_1_DTO, queryId, persist); + queryService.persist(DATABASE_1_PRIVILEGED_DTO, queryId, persist); } } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java index e29e418ca8..f235f39d63 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java @@ -7,7 +7,6 @@ import at.tuwien.config.MariaDbConfig; import at.tuwien.config.MariaDbContainerConfig; import at.tuwien.config.S3Config; import at.tuwien.exception.*; -import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.test.AbstractUnitTest; import com.google.common.io.Files; import lombok.extern.log4j.Log4j2; @@ -18,7 +17,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -42,7 +40,6 @@ import java.util.Map; import java.util.Set; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; @Log4j2 @SpringBootTest @@ -59,9 +56,6 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { @Autowired private S3Config s3Config; - @MockBean - private MetadataServiceGateway metadataServiceGateway; - @Container private static MariaDBContainer<?> mariaDBContainer = MariaDbContainerConfig.getContainer(); @@ -85,8 +79,8 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_3_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_3_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_PRIVILEGED_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_3_PRIVILEGED_DTO); /* s3 */ if (s3Client.listBuckets().buckets().stream().noneMatch(b -> b.name().equals(s3Config.getS3Bucket()))) { s3Client.createBucket(CreateBucketRequest.builder() @@ -101,8 +95,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { } @Test - public void updateTuple_succeeds() throws SQLException, RemoteUnavailableException, ContainerNotFoundException, - TableNotFoundException, TableMalformedException, QueryMalformedException, MetadataServiceException { + public void updateTuple_succeeds() throws SQLException, TableMalformedException, QueryMalformedException { /* modify row based on primary key */ final TupleUpdateDto request = TupleUpdateDto.builder() .data(new HashMap<>() {{ @@ -116,15 +109,9 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { }}) .build(); - /* mock */ - when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); - when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); - /* test */ - tableService.updateTuple(TABLE_1_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.updateTuple(TABLE_1_PRIVILEGED_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("1", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); // <<< assertEquals("Vienna", result.get(0).get("location")); // <<< @@ -133,9 +120,8 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { } @Test - public void updateTuple_modifyPrimaryKey_succeeds() throws SQLException, RemoteUnavailableException, - ContainerNotFoundException, TableNotFoundException, TableMalformedException, QueryMalformedException, - MetadataServiceException { + public void updateTuple_modifyPrimaryKey_succeeds() throws SQLException, TableMalformedException, + QueryMalformedException { /* modify row primary key based on primary key */ final TupleUpdateDto request = TupleUpdateDto.builder() .data(new HashMap<>() {{ @@ -150,15 +136,9 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { }}) .build(); - /* mock */ - when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); - when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); - /* test */ - tableService.updateTuple(TABLE_1_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.updateTuple(TABLE_1_PRIVILEGED_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("4", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); // <<< assertEquals("Vienna", result.get(0).get("location")); // <<< @@ -167,9 +147,8 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { } @Test - public void updateTuple_missingPrimaryKey_succeeds() throws SQLException, RemoteUnavailableException, - ContainerNotFoundException, TableNotFoundException, TableMalformedException, QueryMalformedException, - MetadataServiceException { + public void updateTuple_missingPrimaryKey_succeeds() throws SQLException, TableMalformedException, + QueryMalformedException { /* modify row based on non-primary key column */ final TupleUpdateDto request = TupleUpdateDto.builder() .data(new HashMap<>() {{ @@ -183,15 +162,9 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { }}) .build(); - /* mock */ - when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); - when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); - /* test */ - tableService.updateTuple(TABLE_1_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.updateTuple(TABLE_1_PRIVILEGED_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("1", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); // <<< assertEquals("Vienna", result.get(0).get("location")); // <<< @@ -200,9 +173,8 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { } @Test - public void updateTuple_notInOrder_succeeds() throws SQLException, RemoteUnavailableException, - ContainerNotFoundException, TableNotFoundException, TableMalformedException, QueryMalformedException, - MetadataServiceException { + public void updateTuple_notInOrder_succeeds() throws SQLException, TableMalformedException, + QueryMalformedException { /* modify row based on non-primary key column */ final TupleUpdateDto request = TupleUpdateDto.builder() .data(new HashMap<>() {{ @@ -216,15 +188,9 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { }}) .build(); - /* mock */ - when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); - when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); - /* test */ - tableService.updateTuple(TABLE_1_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.updateTuple(TABLE_1_PRIVILEGED_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 1", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("1", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); // <<< assertEquals("Vienna", result.get(0).get("location")); // <<< @@ -233,9 +199,8 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { } @Test - public void createTuple_succeeds() throws SQLException, RemoteUnavailableException, ContainerNotFoundException, - TableNotFoundException, TableMalformedException, QueryMalformedException, StorageUnavailableException, - StorageNotFoundException, MetadataServiceException { + public void createTuple_succeeds() throws SQLException, TableMalformedException, QueryMalformedException, + StorageUnavailableException, StorageNotFoundException { /* add row with primary key */ final TupleDto request = TupleDto.builder() .data(new HashMap<>() {{ @@ -247,15 +212,9 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { }}) .build(); - /* mock */ - when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); - when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); - /* test */ - tableService.createTuple(TABLE_1_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.createTuple(TABLE_1_PRIVILEGED_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("4", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); assertEquals("Vienna", result.get(0).get("location")); @@ -264,9 +223,8 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { } @Test - public void createTuple_autogeneratedBlob_succeeds() throws SQLException, RemoteUnavailableException, ContainerNotFoundException, - TableNotFoundException, TableMalformedException, QueryMalformedException, StorageUnavailableException, - StorageNotFoundException, MetadataServiceException, IOException { + public void createTuple_autogeneratedBlob_succeeds() throws SQLException, TableMalformedException, + QueryMalformedException, StorageUnavailableException, StorageNotFoundException, IOException { /* add row with primary key */ final TupleDto request = TupleDto.builder() .data(new HashMap<>() {{ @@ -276,26 +234,21 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { .build(); /* mock */ - when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); - when(metadataServiceGateway.getTableById(DATABASE_3_ID, TABLE_8_ID)) - .thenReturn(TABLE_8_DTO); s3Client.putObject(PutObjectRequest.builder() .key("s3key") .bucket(s3Config.getS3Bucket()) .build(), RequestBody.fromFile(new File("src/test/resources/csv/keyboard.csv"))); /* test */ - tableService.createTuple(TABLE_8_DTO, request); - final List<Map<String, byte[]>> result = MariaDbConfig.selectQueryByteArr(DATABASE_3_DTO, "SELECT raw FROM mfcc WHERE raw IS NOT NULL", Set.of("raw")); + tableService.createTuple(TABLE_8_PRIVILEGED_DTO, request); + final List<Map<String, byte[]>> result = MariaDbConfig.selectQueryByteArr(DATABASE_3_PRIVILEGED_DTO, "SELECT raw FROM mfcc WHERE raw IS NOT NULL", Set.of("raw")); assertNotNull(result.get(0).get("raw")); assertArrayEquals(Files.toByteArray(new File("src/test/resources/csv/keyboard.csv")), result.get(0).get("raw")); } @Test - public void createTuple_notInOrder_succeeds() throws SQLException, RemoteUnavailableException, - ContainerNotFoundException, TableNotFoundException, TableMalformedException, QueryMalformedException, - StorageUnavailableException, StorageNotFoundException, MetadataServiceException { + public void createTuple_notInOrder_succeeds() throws SQLException, TableMalformedException, QueryMalformedException, + StorageUnavailableException, StorageNotFoundException { /* add row with primary key */ final TupleDto request = TupleDto.builder() .data(new HashMap<>() {{ @@ -307,15 +260,9 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { }}) .build(); - /* mock */ - when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); - when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); - /* test */ - tableService.createTuple(TABLE_1_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); + tableService.createTuple(TABLE_1_PRIVILEGED_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id, `date`, location, mintemp, rainfall FROM weather_aus WHERE id = 4", Set.of("id", "date", "location", "mintemp", "rainfall")); assertEquals("4", result.get(0).get("id")); assertEquals("2023-10-03", result.get(0).get("date")); assertEquals("Vienna", result.get(0).get("location")); @@ -324,8 +271,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { } @Test - public void deleteTuple_succeeds() throws SQLException, RemoteUnavailableException, ContainerNotFoundException, - TableNotFoundException, TableMalformedException, QueryMalformedException, MetadataServiceException { + public void deleteTuple_succeeds() throws SQLException, TableMalformedException, QueryMalformedException { /* delete row based on primary key */ final TupleDeleteDto request = TupleDeleteDto.builder() .keys(new HashMap<>() {{ @@ -333,22 +279,15 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { }}) .build(); - /* mock */ - when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); - when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); - /* test */ - tableService.deleteTuple(TABLE_1_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id FROM weather_aus WHERE id = 1", Set.of("id")); + tableService.deleteTuple(TABLE_1_PRIVILEGED_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id FROM weather_aus WHERE id = 1", Set.of("id")); assertEquals(0, result.size()); } @Test - public void deleteTuple_withoutPrimaryKey_succeeds() throws SQLException, RemoteUnavailableException, - ContainerNotFoundException, TableNotFoundException, TableMalformedException, QueryMalformedException, - MetadataServiceException { + public void deleteTuple_withoutPrimaryKey_succeeds() throws SQLException, TableMalformedException, + QueryMalformedException { /* remove row based on non-primary key */ final TupleDeleteDto request = TupleDeleteDto.builder() .keys(new HashMap<>() {{ @@ -357,15 +296,9 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { }}) .build(); - /* mock */ - when(metadataServiceGateway.getContainerById(CONTAINER_1_ID)) - .thenReturn(CONTAINER_1_DTO); - when(metadataServiceGateway.getTableById(DATABASE_1_ID, TABLE_1_ID)) - .thenReturn(TABLE_1_DTO); - /* test */ - tableService.deleteTuple(TABLE_1_DTO, request); - final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_DTO, "SELECT id FROM weather_aus WHERE id = 1", Set.of("id")); + tableService.deleteTuple(TABLE_1_PRIVILEGED_DTO, request); + final List<Map<String, String>> result = MariaDbConfig.selectQuery(DATABASE_1_PRIVILEGED_DTO, "SELECT id FROM weather_aus WHERE id = 1", Set.of("id")); assertEquals(0, result.size()); } @@ -374,7 +307,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void getStatistics_succeeds() throws TableMalformedException, SQLException, TableNotFoundException { /* test */ - final TableStatisticDto response = tableService.getStatistics(TABLE_2_DTO); + final TableStatisticDto response = tableService.getStatistics(TABLE_2_PRIVILEGED_DTO); assertEquals(TABLE_2_COLUMNS.size(), response.getColumns().size()); log.trace("response rows: {}", response.getRows()); assertEquals(3L, response.getRows()); @@ -403,18 +336,18 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void delete_succeeds() throws SQLException, QueryMalformedException { /* test */ - tableService.delete(TABLE_1_DTO); + tableService.delete(TABLE_1_PRIVILEGED_DTO); } @Test public void delete_notFound_fails() throws SQLException { /* mock */ - MariaDbConfig.createDatabase(CONTAINER_1_DTO, DATABASE_2_INTERNALNAME); + MariaDbConfig.createDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); /* test */ assertThrows(QueryMalformedException.class, () -> { - tableService.delete(TABLE_5_DTO); + tableService.delete(TABLE_5_PRIVILEGED_DTO); }); } @@ -422,7 +355,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void getCount_succeeds() throws SQLException, QueryMalformedException { /* test */ - final Long response = tableService.getCount(TABLE_1_DTO, null); + final Long response = tableService.getCount(TABLE_1_PRIVILEGED_DTO, null); assertEquals(3, response); } @@ -430,7 +363,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void getCount_timestamp_succeeds() throws SQLException, QueryMalformedException { /* test */ - final Long response = tableService.getCount(TABLE_1_DTO, Instant.ofEpochSecond(0)); + final Long response = tableService.getCount(TABLE_1_PRIVILEGED_DTO, Instant.ofEpochSecond(0)); assertEquals(0, response); } @@ -438,11 +371,11 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void getCount_notFound_fails() throws SQLException { /* mock */ - MariaDbConfig.createDatabase(CONTAINER_1_DTO, DATABASE_2_INTERNALNAME); + MariaDbConfig.createDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); /* test */ assertThrows(QueryMalformedException.class, () -> { - tableService.getCount(TABLE_5_DTO, null); + tableService.getCount(TABLE_5_PRIVILEGED_DTO, null); }); } @@ -450,7 +383,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void history_succeeds() throws SQLException, TableNotFoundException { /* test */ - final List<TableHistoryDto> response = tableService.history(TABLE_1_DTO, 1000L); + final List<TableHistoryDto> response = tableService.history(TABLE_1_PRIVILEGED_DTO, 1000L); assertEquals(1, response.size()); final TableHistoryDto history0 = response.get(0); assertNotNull(history0.getTimestamp()); @@ -462,11 +395,11 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { public void history_notFound_fails() throws SQLException { /* mock */ - MariaDbConfig.createDatabase(CONTAINER_1_DTO, DATABASE_2_INTERNALNAME); + MariaDbConfig.createDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_2_INTERNALNAME); /* test */ assertThrows(TableNotFoundException.class, () -> { - tableService.history(TABLE_5_DTO, null); + tableService.history(TABLE_5_PRIVILEGED_DTO, null); }); } @@ -488,7 +421,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { .build(), RequestBody.fromFile(new File("src/test/resources/csv/weather_aus.csv"))); /* test */ - tableService.importDataset(TABLE_1_DTO, request); + tableService.importDataset(TABLE_1_PRIVILEGED_DTO, request); } @Test @@ -509,7 +442,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* test */ assertThrows(TableMalformedException.class, () -> { - tableService.importDataset(TABLE_1_DTO, request); + tableService.importDataset(TABLE_1_PRIVILEGED_DTO, request); }); } @@ -531,7 +464,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { /* test */ assertThrows(MalformedException.class, () -> { - tableService.importDataset(TABLE_1_DTO, request); + tableService.importDataset(TABLE_1_PRIVILEGED_DTO, request); }); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java index 74e2bd3d68..2edf7f5f28 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java @@ -34,14 +34,14 @@ public class ViewServiceIntegrationTest extends AbstractUnitTest { genesis(); /* metadata database */ MariaDbConfig.dropDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_INTERNALNAME); - MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_DTO); + MariaDbConfig.createInitDatabase(CONTAINER_1_PRIVILEGED_DTO, DATABASE_1_PRIVILEGED_DTO); } @Test public void delete_succeeds() throws SQLException, ViewMalformedException { /* test */ - viewService.delete(VIEW_1_DTO); + viewService.delete(VIEW_1_PRIVILEGED_DTO); } } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java b/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java index e31455a75e..d2a66ab548 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java @@ -4,7 +4,7 @@ import at.tuwien.api.database.table.TableDto; import at.tuwien.exception.MetadataServiceException; import at.tuwien.exception.RemoteUnavailableException; import at.tuwien.exception.TableNotFoundException; -import at.tuwien.gateway.MetadataServiceGateway; +import at.tuwien.service.CredentialService; import at.tuwien.service.QueueService; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -28,14 +28,13 @@ public class DefaultListener implements MessageListener { private final ObjectMapper objectMapper; private final QueueService queueService; - private final MetadataServiceGateway metadataServiceGateway; + private final CredentialService credentialService; @Autowired - public DefaultListener(ObjectMapper objectMapper, QueueService queueService, - MetadataServiceGateway metadataServiceGateway) { + public DefaultListener(ObjectMapper objectMapper, QueueService queueService, CredentialService credentialService) { this.objectMapper = objectMapper; this.queueService = queueService; - this.metadataServiceGateway = metadataServiceGateway; + this.credentialService = credentialService; } @Override @@ -59,7 +58,7 @@ public class DefaultListener implements MessageListener { log.trace("received message for table with id {} of database id {}: {} bytes", tableId, databaseId, message.getMessageProperties().getContentLength()); final Map<String, Object> body; try { - final TableDto table = metadataServiceGateway.getTableById(databaseId, tableId); + final TableDto table = credentialService.getTable(databaseId, tableId); body = objectMapper.readValue(message.getBody(), typeRef); queueService.insert(table, body); } catch (IOException e) { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java index e287cd9d9c..956d50b0dd 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java @@ -99,6 +99,7 @@ public interface DataMapper { .vdbid(database.getId()) .isInitialView(false) .isPublic(database.getIsPublic()) + .isSchemaPublic(database.getIsSchemaPublic()) .query(resultSet.getString(9)) .queryHash(Hashing.sha256() .hashString(resultSet.getString(9), StandardCharsets.UTF_8) 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 0e89f79cb1..8be9ef68e3 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 @@ -541,7 +541,7 @@ public interface MariaDbMapper { } /* parameterized query for prepared statement */ final StringBuilder statement = new StringBuilder("UPDATE `") - .append(table.getDatabase()) + .append(table.getDatabase().getInternalName()) .append("`.`") .append(table.getInternalName()) .append("` SET "); @@ -579,7 +579,7 @@ public interface MariaDbMapper { } /* parameterized query for prepared statement */ final StringBuilder statement = new StringBuilder("INSERT INTO `") - .append(table.getDatabase()) + .append(table.getDatabase().getInternalName()) .append("`.`") .append(table.getInternalName()) .append("` ("); diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java index 359e251ea2..116737e2ed 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MetadataMapper.java @@ -52,8 +52,6 @@ public interface MetadataMapper { IdentifierBriefDto identifierDtoToIdentifierBriefDto(IdentifierDto data); - TableDto databaseDtoToTableDto(DatabaseDto data); - default String metricToUri(String baseUrl, Long databaseId, Long tableId, Long subsetId, Long viewId) { final StringBuilder uri = new StringBuilder(baseUrl) .append("/database/") diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java index 314148663b..c325d7b33a 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/DatabaseService.java @@ -37,9 +37,6 @@ public interface DatabaseService { TableDto createTable(DatabaseDto database, TableCreateDto data) throws SQLException, TableMalformedException, TableExistsException, TableNotFoundException; - Boolean existsView(DatabaseDto database, String viewName) throws SQLException, - QueryMalformedException; - /** * Creates a view in given data database with view definition. * @param database The data database object. diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java index 1786ca5cb4..37a345426a 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DataConnector.java @@ -26,11 +26,11 @@ public abstract class DataConnector { } public ComboPooledDataSource getDataSource(ViewDto view) { - return getDataSource(view.getDatabase().getContainer(), null); + return getDataSource(view.getDatabase().getContainer(), view.getDatabase().getInternalName()); } public ComboPooledDataSource getDataSource(TableDto table) { - return getDataSource(table.getDatabase().getContainer(), null); + return getDataSource(table.getDatabase().getContainer(), table.getDatabase().getInternalName()); } public ComboPooledDataSource getDataSource(ContainerDto container) { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java index d733800a36..4d899c9978 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceMariaDbImpl.java @@ -93,7 +93,7 @@ public class DatabaseServiceMariaDbImpl extends DataConnector implements Databas @Override public TableDto createTable(DatabaseDto database, TableCreateDto data) throws SQLException, - TableMalformedException, TableExistsException { + TableMalformedException, TableExistsException, TableNotFoundException { final String tableName = mariaDbMapper.nameToInternalName(data.getName()); final ComboPooledDataSource dataSource = getDataSource(database); final Connection connection = dataSource.getConnection(); @@ -116,35 +116,10 @@ public class DatabaseServiceMariaDbImpl extends DataConnector implements Databas dataSource.close(); } log.info("Created table with name {}", tableName); - final TableDto table = metadataMapper.databaseDtoToTableDto(database); - table.setInternalName(tableName); + final TableDto table = inspectTable(database, tableName); return table; } - @Override - public Boolean existsView(DatabaseDto database, String viewName) throws SQLException, - QueryMalformedException { - final ComboPooledDataSource dataSource = getDataSource(database); - final Connection connection = dataSource.getConnection(); - final Boolean queryResult; - try { - /* find view data */ - final long start = System.currentTimeMillis(); - final PreparedStatement statement = connection.prepareStatement(mariaDbMapper.selectExistsTableOrViewRawQuery()); - statement.setString(1, database.getInternalName()); - statement.setString(2, viewName); - final ResultSet resultSet = statement.executeQuery(); - log.trace("executed statement in {} ms", System.currentTimeMillis() - start); - queryResult = mariaDbMapper.resultSetToBoolean(resultSet); - } catch (SQLException e) { - log.error("Failed to prepare statement {}", e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement: " + e.getMessage(), e); - } finally { - dataSource.close(); - } - return queryResult; - } - @Override public ViewDto createView(DatabaseDto database, CreateViewDto data) throws SQLException, ViewMalformedException { diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyBriefDto.java index a22b8749ae..56dd986324 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/constraints/foreign/ForeignKeyBriefDto.java @@ -13,7 +13,6 @@ import lombok.extern.jackson.Jacksonized; @ToString public class ForeignKeyBriefDto { - @NonNull @Schema(example = "8") private Long id; } 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 9d5ab6b3d7..f78366fe89 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 @@ -37,13 +37,11 @@ public abstract class AbstractUnitTest extends BaseTest { DATABASE_1_USER_1_READ_ACCESS.setType(AccessType.READ); 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_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); + 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))); UNIT_1.setId(UNIT_1_ID); + TABLE_1.setDatabase(DATABASE_1); TABLE_1.setColumns(new LinkedList<>(TABLE_1_COLUMNS)); TABLE_1.setConstraints(TABLE_1_CONSTRAINTS); - TABLE_1_DTO.setColumns(new LinkedList<>(TABLE_1_COLUMNS_DTO)); - TABLE_1_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); - TABLE_2_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); VIEW_1_DTO.setIdentifiers(VIEW_1_DTO_IDENTIFIERS); DATABASE_1.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_1, IDENTIFIER_2, IDENTIFIER_3, IDENTIFIER_4))); IDENTIFIER_1.setDatabase(DATABASE_1); @@ -58,13 +56,18 @@ public abstract class AbstractUnitTest extends BaseTest { DATABASE_1_DTO.setViews(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO))); TABLE_1_DTO.setColumns(new LinkedList<>(TABLE_1_COLUMNS_DTO)); TABLE_1_DTO.setConstraints(TABLE_1_CONSTRAINTS_DTO); + TABLE_1_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); + TABLE_1_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_1_COLUMNS_DTO)); + TABLE_1_PRIVILEGED_DTO.setConstraints(TABLE_1_CONSTRAINTS_DTO); TABLE_2.setDatabase(DATABASE_1); TABLE_2.setColumns(new LinkedList<>(TABLE_2_COLUMNS)); TABLE_2_CONSTRAINTS.getForeignKeys().get(0).getReferences().get(0).setForeignKey(TABLE_2_CONSTRAINTS.getForeignKeys().get(0)); TABLE_2.setConstraints(TABLE_2_CONSTRAINTS); TABLE_2_DTO.setColumns(new LinkedList<>(TABLE_2_COLUMNS_DTO)); - TABLE_2_DTO.setColumns(new LinkedList<>(TABLE_2_COLUMNS_DTO)); TABLE_2_DTO.setConstraints(TABLE_2_CONSTRAINTS_DTO); + TABLE_2_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); + TABLE_2_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_2_COLUMNS_DTO)); + TABLE_2_PRIVILEGED_DTO.setConstraints(TABLE_2_CONSTRAINTS_DTO); TABLE_3.setDatabase(DATABASE_1); TABLE_3.setColumns(new LinkedList<>(TABLE_3_COLUMNS)); TABLE_3.setConstraints(TABLE_3_CONSTRAINTS); @@ -75,6 +78,9 @@ public abstract class AbstractUnitTest extends BaseTest { TABLE_4.setConstraints(TABLE_4_CONSTRAINTS); TABLE_4_DTO.setColumns(TABLE_4_COLUMNS_DTO); TABLE_4_DTO.setConstraints(TABLE_4_CONSTRAINTS_DTO); + TABLE_4_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO); + TABLE_4_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_4_COLUMNS_DTO)); + TABLE_4_PRIVILEGED_DTO.setConstraints(TABLE_4_CONSTRAINTS_DTO); VIEW_1.setDatabase(DATABASE_1); VIEW_1.setColumns(new LinkedList<>(VIEW_1_COLUMNS)); VIEW_1.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_3))); @@ -93,6 +99,7 @@ public abstract class AbstractUnitTest extends BaseTest { 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_DTO.setAccesses(new LinkedList<>(List.of(DATABASE_2_USER_2_WRITE_ALL_ACCESS_DTO, DATABASE_2_USER_3_READ_ACCESS_DTO))); + DATABASE_2_PRIVILEGED_DTO.setAccesses(new LinkedList<>(List.of(DATABASE_2_USER_2_WRITE_ALL_ACCESS_DTO, DATABASE_2_USER_3_READ_ACCESS_DTO))); DATABASE_2.setTables(new LinkedList<>(List.of(TABLE_5, TABLE_6, TABLE_7))); VIEW_4.setColumns(new LinkedList<>(VIEW_4_COLUMNS)); DATABASE_2.setViews(new LinkedList<>(List.of(VIEW_4))); @@ -105,8 +112,7 @@ public abstract class AbstractUnitTest extends BaseTest { TABLE_5.setConstraints(TABLE_5_CONSTRAINTS); TABLE_5_DTO.setColumns(new LinkedList<>(TABLE_5_COLUMNS_DTO)); TABLE_5_DTO.setConstraints(TABLE_5_CONSTRAINTS_DTO); - TABLE_5_DTO.setColumns(TABLE_5_COLUMNS_DTO); - TABLE_5_DTO.setConstraints(TABLE_5_CONSTRAINTS_DTO); + TABLE_5_PRIVILEGED_DTO.setDatabase(DATABASE_2_PRIVILEGED_DTO); TABLE_6.setDatabase(DATABASE_2); TABLE_6.setColumns(new LinkedList<>(TABLE_6_COLUMNS)); TABLE_6.setConstraints(TABLE_6_CONSTRAINTS); @@ -123,20 +129,22 @@ public abstract class AbstractUnitTest extends BaseTest { 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))); DATABASE_3.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_6))); + DATABASE_3.setAccesses(new LinkedList<>(List.of(DATABASE_3_USER_1_WRITE_ALL_ACCESS))); DATABASE_3_DTO.setTables(new LinkedList<>(List.of(TABLE_8_DTO))); DATABASE_3_DTO.setViews(new LinkedList<>(List.of(VIEW_5_DTO))); DATABASE_3_DTO.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_6_DTO))); + DATABASE_3_DTO.setAccesses(new LinkedList<>(List.of(DATABASE_3_USER_1_WRITE_ALL_ACCESS_DTO))); + DATABASE_3_PRIVILEGED_DTO.setAccesses(new LinkedList<>(List.of(DATABASE_3_USER_1_WRITE_ALL_ACCESS_DTO))); TABLE_8.setDatabase(DATABASE_3); TABLE_8.setColumns(new LinkedList<>(TABLE_8_COLUMNS)); TABLE_8.setConstraints(TABLE_8_CONSTRAINTS); TABLE_8_DTO.setColumns(new LinkedList<>(TABLE_8_COLUMNS_DTO)); TABLE_8_DTO.setConstraints(TABLE_8_CONSTRAINTS_DTO); - TABLE_8_DTO.setColumns(new LinkedList<>(TABLE_8_COLUMNS_DTO)); - TABLE_8_DTO.setConstraints(TABLE_8_CONSTRAINTS_DTO); + TABLE_8_PRIVILEGED_DTO.setColumns(new LinkedList<>(TABLE_8_COLUMNS_DTO)); + TABLE_8_PRIVILEGED_DTO.setDatabase(DATABASE_3_PRIVILEGED_DTO); VIEW_5.setDatabase(DATABASE_3); VIEW_5.setColumns(VIEW_5_COLUMNS); VIEW_5_DTO.setColumns(VIEW_5_COLUMNS_DTO); 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 21e7e0519b..f4b89de585 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 @@ -5,23 +5,23 @@ import at.tuwien.api.amqp.CreateVirtualHostDto; import at.tuwien.api.amqp.ExchangeDto; import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto; import at.tuwien.api.amqp.QueueDto; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.auth.LoginRequestDto; import at.tuwien.api.auth.RefreshTokenRequestDto; -import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.container.ContainerBriefDto; import at.tuwien.api.container.ContainerDto; import at.tuwien.api.container.image.*; import at.tuwien.api.database.*; import at.tuwien.api.database.query.QueryBriefDto; import at.tuwien.api.database.query.QueryDto; -import at.tuwien.api.database.table.TableBriefDto; import at.tuwien.api.database.table.CreateTableDto; +import at.tuwien.api.database.table.TableBriefDto; import at.tuwien.api.database.table.TableDto; import at.tuwien.api.database.table.TableStatisticDto; import at.tuwien.api.database.table.columns.*; import at.tuwien.api.database.table.columns.concepts.*; -import at.tuwien.api.database.table.constraints.CreateTableConstraintsDto; import at.tuwien.api.database.table.constraints.ConstraintsDto; +import at.tuwien.api.database.table.constraints.CreateTableConstraintsDto; import at.tuwien.api.database.table.constraints.foreign.*; import at.tuwien.api.database.table.constraints.primary.PrimaryKeyDto; import at.tuwien.api.database.table.constraints.unique.UniqueDto; @@ -151,6 +151,8 @@ public abstract class BaseTest { public final static String MARIADB_IMAGE = "mariadb:11.3.2"; + public final static String RABBITMQ_IMAGE = "rabbitmq:3.13.7"; + public final static String KEYCLOAK_IMAGE = "quay.io/keycloak/keycloak:24.0"; public final static String[] DEFAULT_SEMANTICS_HANDLING = new String[]{"default-semantics-handling", @@ -1268,10 +1270,7 @@ public abstract class BaseTest { public final static String DATABASE_1_EXCHANGE = "dbrepo"; public final static Instant DATABASE_1_CREATED = Instant.ofEpochSecond(1677399741L) /* 2023-02-26 08:22:21 (UTC) */; public final static Instant DATABASE_1_LAST_MODIFIED = Instant.ofEpochSecond(1677399741L) /* 2023-02-26 08:22:21 (UTC) */; - public final static UUID DATABASE_1_OWNER = USER_1_ID; public final static UUID DATABASE_1_CREATED_BY = USER_1_ID; - public final static UserDto DATABASE_1_CREATOR_DTO = USER_1_DTO; - public final static UserDto DATABASE_1_OWNER_DTO = USER_1_DTO; public final static CreateDatabaseDto DATABASE_1_CREATE = CreateDatabaseDto.builder() .name(DATABASE_1_NAME) @@ -1664,7 +1663,7 @@ public abstract class BaseTest { .routingKey(TABLE_1_ROUTING_KEY) .identifiers(new LinkedList<>()) .columns(new LinkedList<>() /* TABLE_1_COLUMNS_DTO */) - .constraints(null) /* TABLE_1_CONSTRAINT_DTO */ + .constraints(null) /* TABLE_1_CONSTRAINTS_DTO */ .owner(USER_1_BRIEF_DTO) .avgRowLength(TABLE_1_AVG_ROW_LENGTH) .numRows(TABLE_1_NUM_ROWS) @@ -2243,6 +2242,28 @@ public abstract class BaseTest { .maxDataLength(TABLE_4_MAX_DATA_LENGTH) .build(); + public final static TableDto TABLE_4_PRIVILEGED_DTO = TableDto.builder() + .id(TABLE_4_ID) + .tdbid(DATABASE_1_ID) + .internalName(TABLE_4_INTERNALNAME) + .description(TABLE_4_DESCRIPTION) + .name(TABLE_4_NAME) + .queueName(TABLE_4_QUEUE_NAME) + .routingKey(TABLE_4_ROUTING_KEY) + .database(null) /* DATABASE_1_DTO */ + .columns(new LinkedList<>()) /* TABLE_4_COLUMNS_DTO */ + .constraints(null) /* TABLE_4_CONSTRAINTS_DTO */ + .isVersioned(TABLE_4_VERSIONED) + .isPublic(TABLE_4_IS_PUBLIC) + .isSchemaPublic(TABLE_4_SCHEMA_PUBLIC) + .owner(USER_1_BRIEF_DTO) + .avgRowLength(TABLE_4_AVG_ROW_LENGTH) + .numRows(TABLE_4_NUM_ROWS) + .dataLength(TABLE_4_DATA_LENGTH) + .maxDataLength(TABLE_4_MAX_DATA_LENGTH) + .lastRetrieved(Instant.now()) + .build(); + public final static TableBriefDto TABLE_4_BRIEF_DTO = TableBriefDto.builder() .id(TABLE_4_ID) .databaseId(DATABASE_1_ID) @@ -2909,6 +2930,33 @@ public abstract class BaseTest { .resultNumber(3L) .build(); + public final static ViewDto QUERY_1_VIEW_DTO = ViewDto.builder() + .vdbid(QUERY_1_DATABASE_ID) + .query(QUERY_1_STATEMENT) + .queryHash(QUERY_1_QUERY_HASH) + .owner(USER_1_BRIEF_DTO) + .columns(new LinkedList<>(List.of(ViewColumnDto.builder() + .name("id") + .internalName("id") + .build(), + ViewColumnDto.builder() + .name("date") + .internalName("date") + .build(), + ViewColumnDto.builder() + .name("location") + .internalName("location") + .build(), + ViewColumnDto.builder() + .name("mintemp") + .internalName("mintemp") + .build(), + ViewColumnDto.builder() + .name("rainfall") + .internalName("rainfall") + .build()))) + .build(); + public final static QueryBriefDto QUERY_1_BRIEF_DTO = QueryBriefDto.builder() .id(QUERY_1_ID) .databaseId(QUERY_1_DATABASE_ID) @@ -3080,6 +3128,21 @@ public abstract class BaseTest { .owner(USER_1_BRIEF_DTO) .build(); + public final static ViewDto QUERY_5_VIEW_DTO = ViewDto.builder() + .vdbid(DATABASE_3_ID) + .query(QUERY_5_STATEMENT) + .queryHash(QUERY_5_QUERY_HASH) + .owner(USER_1_BRIEF_DTO) + .columns(new LinkedList<>(List.of(ViewColumnDto.builder() + .name("id") + .internalName("id") + .build(), + ViewColumnDto.builder() + .name("value") + .internalName("value") + .build()))) + .build(); + public final static List<Map<String, Object>> QUERY_5_RESULT_DTO = new LinkedList<>(List.of( Map.of("id", BigInteger.valueOf(1L), "value", 11.2), Map.of("id", BigInteger.valueOf(2L), "value", 11.3), @@ -7518,7 +7581,7 @@ public abstract class BaseTest { .lastModified(DATABASE_1_LAST_MODIFIED) .ownedBy(DATABASE_1_CREATED_BY) .owner(USER_1) - .ownedBy(DATABASE_1_OWNER) + .ownedBy(USER_1_ID) .owner(USER_1) .image(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) .contactPerson(USER_1_ID) @@ -7541,6 +7604,7 @@ public abstract class BaseTest { .identifiers(new LinkedList<>(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO))) .tables(new LinkedList<>(List.of(TABLE_1_DTO, TABLE_2_DTO, TABLE_3_DTO, TABLE_4_DTO))) .views(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO))) + .owner(USER_1_BRIEF_DTO) .build(); public final static DatabaseDto DATABASE_1_PRIVILEGED_DTO = DatabaseDto.builder() @@ -7551,9 +7615,11 @@ public abstract class BaseTest { .container(CONTAINER_1_PRIVILEGED_DTO) .internalName(DATABASE_1_INTERNALNAME) .exchangeName(DATABASE_1_EXCHANGE) + .accesses(new LinkedList<>(List.of())) /* DATABASE_1_USER_1_READ_ACCESS_DTO */ .identifiers(new LinkedList<>(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO))) .tables(new LinkedList<>(List.of(TABLE_1_DTO, TABLE_2_DTO, TABLE_3_DTO, TABLE_4_DTO))) .views(new LinkedList<>(List.of(VIEW_1_DTO, VIEW_2_DTO, VIEW_3_DTO))) + .owner(USER_1_BRIEF_DTO) .lastRetrieved(Instant.now()) .build(); @@ -7891,6 +7957,13 @@ public abstract class BaseTest { .user(USER_1) .build(); + public final static DatabaseAccessDto DATABASE_3_USER_1_WRITE_ALL_ACCESS_DTO = DatabaseAccessDto.builder() + .type(AccessTypeDto.WRITE_ALL) + .hdbid(DATABASE_3_ID) + .huserid(USER_1_ID) + .user(USER_1_BRIEF_DTO) + .build(); + public final static DatabaseAccess DATABASE_3_USER_2_READ_ACCESS = DatabaseAccess.builder() .type(AccessType.READ) .hdbid(DATABASE_3_ID) @@ -8087,9 +8160,9 @@ public abstract class BaseTest { .foreignKeys(new LinkedList<>()) .uniques(new LinkedList<>()) .primaryKey(new LinkedHashSet<>(Set.of(PrimaryKeyDto.builder() + .id(1L) .table(TABLE_1_BRIEF_DTO) .column(TABLE_1_COLUMNS_BRIEF_0_DTO) - .id(1L) .build()))) .build(); -- GitLab