diff --git a/.dbrepo2/deploy b/.dbrepo2/deploy deleted file mode 100755 index 2cda49aec781a27fc0a36336a6e3990003bb863e..0000000000000000000000000000000000000000 --- a/.dbrepo2/deploy +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# set version -TAG=latest - -# tear everything down -echo "Removing all data ..." -/bin/bash teardown -docker system prune -f --volumes || true - -echo "Pulling new images ..." -docker pull "dbrepo/analyse-service:${TAG}" -docker pull "dbrepo/authentication-service:${TAG}" -docker pull "dbrepo/metadata-db:${TAG}" -docker pull "dbrepo/ui:${TAG}" -docker pull "dbrepo/ui-proxy:${TAG}" -docker pull "dbrepo/identifier-service:${TAG}" -docker pull "dbrepo/container-service:${TAG}" -docker pull "dbrepo/database-service:${TAG}" -docker pull "dbrepo/discovery-service:${TAG}" -docker pull "dbrepo/gateway-service:${TAG}" -docker pull "dbrepo/query-service:${TAG}" -docker pull "dbrepo/table-service:${TAG}" -docker pull "dbrepo/units-service:${TAG}" -docker pull "dbrepo/broker-service:${TAG}" -docker pull "dbrepo/metadata-service:${TAG}" -echo "Pulled new images" - -# deploy dbrepo -echo "Deploy DBRepo ..." -docker compose -f ./docker-compose.dbrepo2.yml up -d - -# clone tuw specific deployment -git -C "/home/demo/dbrepo-tuw" pull || git clone ssh://git@gitlab.tuwien.ac.at:822/martin.weise/dbrepo-tuw.git "/home/demo/dbrepo-tuw" -cd /home/demo/dbrepo-tuw && git checkout dev - -# build tuw specific deployment -docker compose -f /home/demo/dbrepo-tuw/docker-compose.dbrepo2.yml build || exit 3 -docker compose -f /home/demo/dbrepo-tuw/docker-compose.dbrepo2.yml up -d || exit 4 -echo "Deployed TU Wien specific deployment" \ No newline at end of file diff --git a/.dbrepo2/known_hosts b/.dbrepo2/known_hosts deleted file mode 100644 index d17a72e2de908c97eb551254d51770e6725332b8..0000000000000000000000000000000000000000 --- a/.dbrepo2/known_hosts +++ /dev/null @@ -1,6 +0,0 @@ -gitlab.tuwien.ac.at ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKslN24njaMeH0xyVBO/lzeQ5/X/6UFkiz2qdcWihjT28slO/6pFBAxSzPUTBeVJWxKJgX9N+Hm65S9/Z2521E8Y9F56dcrOgeQBZ4GbLPSbEZIFN/71VL4cOh6tLahNZsAwd6y2Zi5XTAqho31VWfdCZkegZB3dNzLfOuC0t3KpxEAmWus5j8InNYDTBIy6U351/3h3oy32EdLvLpaHP+oy+QFAqXOlcXlKwHTfv9SSchQRLfWzrL4hVvohbCwMDBBvIP8J0WQ9mV95xfcwuipMWP3TksDGbst2MQ6HRXZ+yfie9Vgzg+++AjpHXQCMdalEmedNxCmxbHWYJxFHUV -gitlab.tuwien.ac.at ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBC+2QClFaM3ZNQ6nl3nHkZdFSyNP26uHPzJEUBu9J4yC1ON9GPHb2P3rC//wS819e3LWP4PXb8ug/EyEjWN30MA= -gitlab.tuwien.ac.at ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILTzOKU3zEYNo1xy8rxZsa8D0/y4EN4oB0E7wgWCkOpZ -dbrepo2.ec.tuwien.ac.at ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5GFfX4xmYeQdQQL81heBjKb4d1M8xjOjF4lpfNmc3/wyCPtP/dt4r3nkcOW3X14yPximy8BlArsPk4ul14BWNI9yrU5VA1FLlWq9Og6SfMJ1EAUNsePDd3qCpr/ljlydS3kWCVrXD1bNO0kQ3/36e9l64BDvZXyhI3n9DvkPO93n9xJ151IC9IwjnSzJQPghboUDRiYwT2B9wt+uC+k7tTV5tQH5kJk8fEa8nMyHOc8aD2miHubrcQJrYO/fv+vVxVWJKtSdz22wuvFnj8SfGClxbT8cfMo8X+LpIQOH8VYwIZVznuSlnNYOOYO6EGYyRnqud6oz0B5RUbwyeBhtAgZW1C1OXO9DVDLnazzFxJIzwhwyZCPurpLpP7bd6P+oFWy1A7bxIhfvdduEDE80vEOPrBl44TDe6RJR6QILtdUn9rrvcV/kgfj04zkQJjqMvX2pdCpdMIU1Pm+NQ53k3oOap7j9UHnpWX7C3mk76ueQddQxWZUhGsrFUAYSJfus= -dbrepo2.ec.tuwien.ac.at ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOGTeKw2aKnykIUmvLiBNaGbq3xlSEsnD1M1HiZsPJ9ZtfSV12y9F0yutV4j68Rb+eHbyOxoyVekfl19ODDvXLM= -dbrepo2.ec.tuwien.ac.at ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPpSoshRRtQBj4ebWeaWoeGr1XFIlx3L+BXV69fafBGr \ No newline at end of file diff --git a/.fda-deployment/clean b/.fda-deployment/clean deleted file mode 100755 index 03506fc6ef3a480eea7269f90c2f8a99209a1bc0..0000000000000000000000000000000000000000 --- a/.fda-deployment/clean +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -docker container stop $(docker container ls -aq) || true -docker container rm $(docker container ls -aq) || true -docker volume rm $(docker volume ls -q) || true diff --git a/.fda-deployment/clean-tmp b/.fda-deployment/clean-tmp deleted file mode 100755 index ac34df7e9c815917e5908b129f3502c18e97e2f4..0000000000000000000000000000000000000000 --- a/.fda-deployment/clean-tmp +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -FILES=$(cd /tmp && find -exec basename '{}' ';' 2>/dev/null | egrep '^.{32}$' | egrep "^([a-z0-9]+)$") -for file in $FILES; do - rm -f /tmp/$file - echo "Removed /tmp/${file}" -done \ No newline at end of file diff --git a/.fda-deployment/crontab b/.fda-deployment/crontab deleted file mode 100644 index bbc97b1a429e174b4a42e99503e70098f1acb441..0000000000000000000000000000000000000000 --- a/.fda-deployment/crontab +++ /dev/null @@ -1,2 +0,0 @@ -0 2 * * * /usr/bin/make -C /home/rocky/fda-services teardown -1 2 * * * /usr/bin/make -C /home/rocky/fda-services run \ No newline at end of file diff --git a/.fda-deployment/fda-authentication-service/install_cert b/.fda-deployment/fda-authentication-service/install_cert deleted file mode 100755 index f52db602f44a1c08cb4cb4e8e8cc6e17e45d190b..0000000000000000000000000000000000000000 --- a/.fda-deployment/fda-authentication-service/install_cert +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -TMP_CERT_LOCATION="/root/keys" -TMP_SAML_LOCATION="/root/keys" -KEY_STORE_LOCATION="/tmp/dbrepo.jks" -KEY_STORE_PASS="dbrepo" -CERT_LOCATION="/etc/letsencrypt/live/dbrepo.ossdip.at" -SAML_KEY="/root/keys/saml_sign.key" -SAML_PUB="/root/keys/saml_sign.cer" -TU_SAML_CERT="./fda-authentication-service/rest-service/src/main/resources/saml/tu.crt" - -# PLACE -sudo mkdir -p "${TMP_CERT_LOCATION}" -sudo mkdir -p "${TMP_SAML_LOCATION}" - -# REQUEST -sudo certbot certonly --standalone --preferred-challenges http -d dbrepo.ossdip.at \ - -m martin.weise@tuwien.ac.at --agree-tos --keep-until-expiring - -# CONVERT PKCS12 -sudo openssl pkcs12 -export -out "${TMP_SAML_LOCATION}/saml.p12" -in "${SAML_PUB}" \ - -inkey "${SAML_KEY}" -passout "pass:${KEY_STORE_PASS}" - -# CONVERT PKCS12 -sudo openssl pkcs12 -export -out "${TMP_CERT_LOCATION}/cert.p12" -in "${CERT_LOCATION}/cert.pem" \ - -inkey "${CERT_LOCATION}/privkey.pem" -passout "pass:${KEY_STORE_PASS}" - -# FIX PERMISSIONS -sudo chmod -R 644 "${TMP_CERT_LOCATION}" -sudo chmod -R 644 "${TMP_SAML_LOCATION}" - -# IMPORT SSL SIGN PRIVKEY -sudo keytool -noprompt -importkeystore -deststorepass "${KEY_STORE_PASS}" -destkeypass "${KEY_STORE_PASS}" \ - -destkeystore "${KEY_STORE_LOCATION}" -srckeystore "${TMP_CERT_LOCATION}/cert.p12" -srcstoretype PKCS12 \ - -srcstorepass "${KEY_STORE_PASS}" -alias 1 -destalias ssl - -# IMPORT SAML MESSAGE SIGN PRIVKEY -sudo keytool -noprompt -importkeystore -deststorepass "${KEY_STORE_PASS}" -destkeypass "${KEY_STORE_PASS}" \ - -destkeystore "${KEY_STORE_LOCATION}" -srckeystore "${TMP_SAML_LOCATION}" -srcstoretype PKCS12 \ - -srcstorepass "${KEY_STORE_PASS}" -alias 1 -destalias saml - -# IMPORT METADATA VERIFICATION PUBKEY -sudo keytool -noprompt -importcert -file "${TU_SAML_CERT}" -storepass "${KEY_STORE_PASS}" \ - -keystore "${KEY_STORE_LOCATION}" -alias tu - -# OWNERSHIP -sudo chown centos:docker "${TMP_CERT_LOCATION}" -sudo chown centos:docker "${TMP_SAML_LOCATION}" -sudo chown centos:docker "${KEY_STORE_LOCATION}" - -# TRUST LET'S ENCRYPT -sudo keytool -noprompt -import -alias letsencrypt -keystore "${KEY_STORE_LOCATION}" -storepass "${KEY_STORE_PASS}" \ - -file "${CERT_LOCATION}/chain.pem" diff --git a/.fda-deployment/fda-authentication-service/install_smtp b/.fda-deployment/fda-authentication-service/install_smtp deleted file mode 100755 index 22b0afaa76093e4c311b2451e365733963b15b49..0000000000000000000000000000000000000000 --- a/.fda-deployment/fda-authentication-service/install_smtp +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -sudo cat /root/smtp.secret >> ./.env diff --git a/.fda-deployment/fda-ui/install_cert b/.fda-deployment/fda-ui/install_cert deleted file mode 100755 index 92553123718052ab77978aea4d8ad3a699c0c816..0000000000000000000000000000000000000000 --- a/.fda-deployment/fda-ui/install_cert +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -CA_PATH="/etc/letsencrypt/live/dbrepo.ossdip.at" - -sudo certbot certonly --standalone --preferred-challenges http -d dbrepo.ossdip.at --agree-tos --keep-until-expiring - -KEY=$(sudo sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g' "${CA_PATH}/privkey.pem") -CERT=$(sudo sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g' "${CA_PATH}/cert.pem") - -cat << EOF -UI_KEY="${KEY}" -UI_CERT="${CERT}" -EOF \ No newline at end of file diff --git a/.fda-deployment/hosts b/.fda-deployment/hosts deleted file mode 100644 index 40da214c6af6d90f416cca86829a26ae06edec66..0000000000000000000000000000000000000000 --- a/.fda-deployment/hosts +++ /dev/null @@ -1,7 +0,0 @@ -# FDA USERDB -172.28.0.2 fda-userdb-weather-aus -172.28.0.3 fda-userdb-infection -172.28.0.4 fda-userdb-air -172.28.0.5 fda-userdb-u01 -172.28.0.6 fda-userdb-u02 -172.28.0.7 fda-userdb-u03 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 36fb8d753994b88376b278622de69351a108abdf..ae9e683f84116b3457f7a61d93748f2e47c31c2a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,11 @@ target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ - +dbrepo-* # Notebooks .jupyter/ .pytest_cache/ +__pycache__/ # demo .demo diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c18ce65e3b9ed6b126c279b57784086a06dc3f0e..1967e4afeacc6e81ad68eca7d0ccb97337f32f3d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,6 @@ before_script: - "mvn --version" - "python3 --version" - "df / -h" - - "grep -rnw '@Test' | wc -l" variables: HOSTALIASES: ./hosts @@ -23,76 +22,77 @@ stages: - build-backend - build-frontend - test-backend + - test-frontend - build-docker - release -build-backend-metadata-db: +build-metadata-db: stage: build-backend script: - - "make build-backend-metadata-db" + - "make build-metadata-db" -build-backend-authentication: +build-authentication-service: stage: build-backend needs: - - build-backend-metadata-db + - build-metadata-db script: - - "make build-backend-authentication" + - "make build-authentication-service" -build-backend-identifier: +build-identifier-service: stage: build-backend needs: - - build-backend-metadata-db + - build-metadata-db script: - - "make build-backend-identifier" + - "make build-identifier-service" -build-backend-container: +build-container-service: stage: build-backend needs: - - build-backend-metadata-db + - build-metadata-db script: - - "make build-backend-container" + - "make build-container-service" -build-backend-database: +build-database-service: stage: build-backend needs: - - build-backend-metadata-db + - build-metadata-db script: - - "make build-backend-database" + - "make build-database-service" -build-backend-discovery: +build-discovery-service: stage: build-backend needs: - - build-backend-metadata-db + - build-metadata-db script: - - "make build-backend-discovery" + - "make build-discovery-service" -build-backend-gateway: +build-gateway-service: stage: build-backend needs: - - build-backend-metadata-db + - build-metadata-db script: - - "make build-backend-gateway" + - "make build-gateway-service" -build-backend-query: +build-query-service: stage: build-backend needs: - - build-backend-metadata-db + - build-metadata-db script: - - "make build-backend-query" + - "make build-query-service" -build-backend-table: +build-table-service: stage: build-backend needs: - - build-backend-metadata-db + - build-metadata-db script: - - "make build-backend-table" + - "make build-table-service" -build-backend-metadata: +build-metadata-service: stage: build-backend needs: - - build-backend-metadata-db + - build-metadata-db script: - - "make build-backend-metadata" + - "make build-metadata-service" build-semantics-service: stage: build-backend @@ -107,7 +107,7 @@ build-analyse-service: test-backend-authentication: stage: test-backend needs: - - build-backend-authentication + - build-authentication-service script: - "make test-authentication-service" - "cat ./fda-authentication-service/report/target/site/jacoco-aggregate/index.html | grep -o 'Total[^%]*%' | sed 's/<.*>/ /; s/Total/Jacoco Coverage Total:/'" @@ -120,10 +120,11 @@ test-backend-authentication: reports: junit: ./fda-authentication-service/rest-service/target/surefire-reports/TEST-*.xml coverage: '/Total.*?([0-9]{1,3})%/' -test-backend-identifier: + +test-identifier-service: stage: test-backend needs: - - build-backend-authentication + - build-authentication-service script: - "make test-identifier-service" - "cat ./fda-identifier-service/report/target/site/jacoco-aggregate/index.html | grep -o 'Total[^%]*%' | sed 's/<.*>/ /; s/Total/Jacoco Coverage Total:/'" @@ -137,10 +138,10 @@ test-backend-identifier: junit: ./fda-identifier-service/rest-service/target/surefire-reports/TEST-*.xml coverage: '/Total.*?([0-9]{1,3})%/' -test-backend-container: +test-container-service: stage: test-backend needs: - - build-backend-container + - build-container-service script: - "make test-container-service" - "cat ./fda-container-service/report/target/site/jacoco-aggregate/index.html | grep -o 'Total[^%]*%' | sed 's/<.*>/ /; s/Total/Jacoco Coverage Total:/'" @@ -154,10 +155,10 @@ test-backend-container: junit: ./fda-container-service/rest-service/target/surefire-reports/TEST-*.xml coverage: '/Total.*?([0-9]{1,3})%/' -test-backend-database: +test-database-service: stage: test-backend needs: - - build-backend-database + - build-database-service script: - "docker pull elasticsearch:7.13.4" - "make test-database-service" @@ -171,11 +172,12 @@ test-backend-database: reports: junit: ./fda-database-service/rest-service/target/surefire-reports/TEST-*.xml coverage: '/Total.*?([0-9]{1,3})%/' + timeout: 2 hour -test-backend-discovery: +test-discovery-service: stage: test-backend needs: - - build-backend-discovery + - build-discovery-service script: - "make test-discovery-service" - "cat ./fda-discovery-service/report/target/site/jacoco-aggregate/index.html | grep -o 'Total[^%]*%' | sed 's/<.*>/ /; s/Total/Jacoco Coverage Total:/'" @@ -189,10 +191,10 @@ test-backend-discovery: junit: ./fda-discovery-service/discovery/target/surefire-reports/TEST-*.xml coverage: '/Total.*?([0-9]{1,3})%/' -test-backend-query: +test-query-service: stage: test-backend needs: - - build-backend-query + - build-query-service script: - "make test-query-service" - "cat ./fda-query-service/report/target/site/jacoco-aggregate/index.html | grep -o 'Total[^%]*%' | sed 's/<.*>/ /; s/Total/Jacoco Coverage Total:/'" @@ -205,11 +207,12 @@ test-backend-query: reports: junit: ./fda-query-service/rest-service/target/surefire-reports/TEST-*.xml coverage: '/Total.*?([0-9]{1,3})%/' + timeout: 2 hour -test-backend-table: +test-table-service: stage: test-backend needs: - - build-backend-table + - build-table-service script: - "make test-table-service" - "cat ./fda-table-service/report/target/site/jacoco-aggregate/index.html | grep -o 'Total[^%]*%' | sed 's/<.*>/ /; s/Total/Jacoco Coverage Total:/'" @@ -223,10 +226,10 @@ test-backend-table: junit: ./fda-table-service/rest-service/target/surefire-reports/TEST-*.xml coverage: '/Total.*?([0-9]{1,3})%/' -test-backend-metadata: +test-metadata-service: stage: test-backend needs: - - build-backend-metadata + - build-metadata-service script: - "make test-metadata-service" - "cat ./fda-metadata-service/report/target/site/jacoco-aggregate/index.html | grep -o 'Total[^%]*%' | sed 's/<.*>/ /; s/Total/Jacoco Coverage Total:/'" @@ -240,10 +243,10 @@ test-backend-metadata: junit: ./fda-metadata-service/rest-service/target/surefire-reports/TEST-*.xml coverage: '/Total.*?([0-9]{1,3})%/' -test-backend-gateway: +test-gateway-service: stage: test-backend needs: - - build-backend-gateway + - build-gateway-service script: - "make test-gateway-service" - "cat ./fda-gateway-service/report/target/site/jacoco-aggregate/index.html | grep -o 'Total[^%]*%' | sed 's/<.*>/ /; s/Total/Jacoco Coverage Total:/'" @@ -289,19 +292,35 @@ test-analyse-service: junit: ./fda-analyse-service/report.xml coverage: '/TOTAL.*?([0-9]{1,3})%/' +test-frontend: + stage: test-frontend + needs: + - build-frontend + script: + - "make test-frontend" + - "cat ./fda-ui/coverage.txt | grep -o 'Lines[^%]*%'" + artifacts: + when: always + paths: + - ./fda-analyse-service/report.xml + expire_in: 1 days + reports: + junit: ./fda-analyse-service/report.xml + coverage: '/TOTAL.*?([0-9]{1,3})%/' + build-docker: stage: build-docker needs: - - build-backend-metadata-db - - build-backend-authentication - - build-backend-identifier - - build-backend-container - - build-backend-database - - build-backend-discovery - - build-backend-gateway - - build-backend-query - - build-backend-table - - build-backend-metadata + - build-metadata-db + - build-authentication-service + - build-identifier-service + - build-container-service + - build-database-service + - build-discovery-service + - build-gateway-service + - build-query-service + - build-table-service + - build-metadata-service - build-semantics-service - build-analyse-service script: diff --git a/.gitlab/jacoco2cobertura.py b/.gitlab/jacoco2cobertura.py deleted file mode 100755 index 76ad75a70583ae4689952ef037ecb241eec48897..0000000000000000000000000000000000000000 --- a/.gitlab/jacoco2cobertura.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env python3 -import sys -import xml.etree.ElementTree as ET -import re -import os.path - -# branch-rate="0.0" complexity="0.0" line-rate="1.0" -# branch="true" hits="1" number="86" - -def find_lines(j_package, filename): - """Return all <line> elements for a given source file in a package.""" - lines = list() - sourcefiles = j_package.findall("sourcefile") - for sourcefile in sourcefiles: - if sourcefile.attrib.get("name") == os.path.basename(filename): - lines = lines + sourcefile.findall("line") - return lines - -def line_is_after(jm, start_line): - return int(jm.attrib.get('line', 0)) > start_line - -def method_lines(jmethod, jmethods, jlines): - """Filter the lines from the given set of jlines that apply to the given jmethod.""" - start_line = int(jmethod.attrib.get('line', 0)) - larger = list(int(jm.attrib.get('line', 0)) for jm in jmethods if line_is_after(jm, start_line)) - end_line = min(larger) if len(larger) else 99999999 - - for jline in jlines: - if start_line <= int(jline.attrib['nr']) < end_line: - yield jline - -def convert_lines(j_lines, into): - """Convert the JaCoCo <line> elements into Cobertura <line> elements, add them under the given element.""" - c_lines = ET.SubElement(into, 'lines') - for jline in j_lines: - mb = int(jline.attrib['mb']) - cb = int(jline.attrib['cb']) - ci = int(jline.attrib['ci']) - - cline = ET.SubElement(c_lines, 'line') - cline.set('number', jline.attrib['nr']) - cline.set('hits', '1' if ci > 0 else '0') # Probably not true but no way to know from JaCoCo XML file - - if mb + cb > 0: - percentage = str(int(100 * (float(cb) / (float(cb) + float(mb))))) + '%' - cline.set('branch', 'true') - cline.set('condition-coverage', percentage + ' (' + str(cb) + '/' + str(cb + mb) + ')') - - cond = ET.SubElement(ET.SubElement(cline, 'conditions'), 'condition') - cond.set('number', '0') - cond.set('type', 'jump') - cond.set('coverage', percentage) - else: - cline.set('branch', 'false') - -def guess_filename(path_to_class): - m = re.match('([^$]*)', path_to_class) - return (m.group(1) if m else path_to_class) + '.java' - -def add_counters(source, target): - target.set('line-rate', counter(source, 'LINE')) - target.set('branch-rate', counter(source, 'BRANCH')) - target.set('complexity', counter(source, 'COMPLEXITY', sum)) - -def fraction(covered, missed): - return covered / (covered + missed) - -def sum(covered, missed): - return covered + missed - -def counter(source, type, operation=fraction): - cs = source.findall('counter') - c = next((ct for ct in cs if ct.attrib.get('type') == type), None) - - if c is not None: - covered = float(c.attrib['covered']) - missed = float(c.attrib['missed']) - - return str(operation(covered, missed)) - else: - return '0.0' - -def convert_method(j_method, j_lines): - c_method = ET.Element('method') - c_method.set('name', j_method.attrib['name']) - c_method.set('signature', j_method.attrib['desc']) - - add_counters(j_method, c_method) - convert_lines(j_lines, c_method) - - return c_method - -def convert_class(j_class, j_package): - c_class = ET.Element('class') - c_class.set('name', j_class.attrib['name'].replace('/', '.')) - c_class.set('filename', guess_filename(j_class.attrib['name'])) - - all_j_lines = list(find_lines(j_package, c_class.attrib['filename'])) - - c_methods = ET.SubElement(c_class, 'methods') - all_j_methods = list(j_class.findall('method')) - for j_method in all_j_methods: - j_method_lines = method_lines(j_method, all_j_methods, all_j_lines) - c_methods.append(convert_method(j_method, j_method_lines)) - - add_counters(j_class, c_class) - convert_lines(all_j_lines, c_class) - - return c_class - -def convert_package(j_package): - c_package = ET.Element('package') - c_package.attrib['name'] = j_package.attrib['name'].replace('/', '.') - - c_classes = ET.SubElement(c_package, 'classes') - for j_class in j_package.findall('class'): - c_classes.append(convert_class(j_class, j_package)) - - add_counters(j_package, c_package) - - return c_package - -def convert_root(source, target, source_roots): - target.set('timestamp', str(int(source.find('sessioninfo').attrib['start']) / 1000)) - - sources = ET.SubElement(target, 'sources') - for s in source_roots: - ET.SubElement(sources, 'source').text = s - - packages = ET.SubElement(target, 'packages') - for package in source.findall('package'): - packages.append(convert_package(package)) - - add_counters(source, target) - -def jacoco2cobertura(filename, source_roots): - if filename == '-': - root = ET.fromstring(sys.stdin.read()) - else: - tree = ET.parse(filename) - root = tree.getroot() - - into = ET.Element('coverage') - convert_root(root, into, source_roots) - print(ET.tostring(into, encoding='utf8', method='xml').decode("utf-8")) - -if __name__ == '__main__': - if len(sys.argv) < 2: - print("Usage: cover2cover.py FILENAME [SOURCE_ROOTS]") - sys.exit(1) - - filename = sys.argv[1] - source_roots = sys.argv[2:] if 2 < len(sys.argv) else '.' - - jacoco2cobertura(filename, source_roots) \ No newline at end of file diff --git a/.junit/hosts b/.junit/hosts new file mode 100644 index 0000000000000000000000000000000000000000..701ef56fa315ea16163432c842816c946150d97f --- /dev/null +++ b/.junit/hosts @@ -0,0 +1,10 @@ +########################################################### +# PLACE IN /etc/hosts # +########################################################### + +172.30.0.5 dbrepo-userdb-u01 +172.30.0.6 dbrepo-userdb-u02 +172.30.0.7 dbrepo-userdb-u03 +172.30.0.8 dbrepo-userdb-u04 +172.31.0.2 dbrepo-broker-service +172.31.0.3 dbrepo-search-service \ No newline at end of file diff --git a/.dbrepo2/teardown b/.junit/teardown similarity index 100% rename from .dbrepo2/teardown rename to .junit/teardown diff --git a/Makefile b/Makefile index ec1921ff29552a4a0bd432d26a0c4708a4f15c36..a5bce8de2e48b1547e456f13be75256590b415c9 100644 --- a/Makefile +++ b/Makefile @@ -4,39 +4,36 @@ TAG ?= latest all: -clean: - bash ./.dbrepo2/clean.sh - -build-backend: build-backend-metadata-db build-backend-database build-backend-query build-backend-table build-backend-identifier build-backend-authentication build-backend-container build-backend-discovery build-backend-gateway build-backend-metadata build-analyse-service +build-backend: build-metadata-db build-database-service build-query-service build-table-service build-identifier-service build-authentication-service build-container-service build-discovery-service build-gateway-service build-metadata-service build-analyse-service -build-backend-metadata-db: +build-metadata-db: mvn -f ./fda-metadata-db/pom.xml clean install -build-backend-authentication: build-backend-metadata-db +build-authentication-service: build-metadata-db mvn -f ./fda-authentication-service/pom.xml clean package -DskipTests -build-backend-identifier: build-backend-metadata-db +build-identifier-service: build-metadata-db mvn -f ./fda-identifier-service/pom.xml clean package -DskipTests -build-backend-table: build-backend-metadata-db +build-table-service: build-metadata-db mvn -f ./fda-table-service/pom.xml clean package -DskipTests -build-backend-container: build-backend-metadata-db +build-container-service: build-metadata-db mvn -f ./fda-container-service/pom.xml clean package -DskipTests -build-backend-database: build-backend-metadata-db +build-database-service: build-metadata-db mvn -f ./fda-database-service/pom.xml clean package -DskipTests -build-backend-discovery: build-backend-metadata-db +build-discovery-service: build-metadata-db mvn -f ./fda-discovery-service/pom.xml clean package -DskipTests -build-backend-gateway: build-backend-metadata-db +build-gateway-service: build-metadata-db mvn -f ./fda-gateway-service/pom.xml clean package -DskipTests -build-backend-query: build-backend-metadata-db +build-query-service: build-metadata-db mvn -f ./fda-query-service/pom.xml clean package -DskipTests -build-backend-metadata: build-backend-metadata-db +build-metadata-service: build-metadata-db mvn -f ./fda-metadata-service/pom.xml clean package -DskipTests build-semantics-service: @@ -150,93 +147,35 @@ release-search: release-metadata: docker push "dbrepo/metadata-service:${TAG}" -pull: pull-identifier pull-container pull-search pull-database pull-discovery pull-gateway pull-query pull-table pull-analyse pull-authentication pull-metadata-db pull-ui pull-units pull-broker pull-metadata - -pull-analyse: - docker pull "dbrepo/analyse-service:${TAG}" - -pull-authentication: - docker pull "dbrepo/authentication-service:${TAG}" - -pull-metadata-db: - docker pull "dbrepo/metadata-db:${TAG}" - -pull-ui: - docker pull "dbrepo/ui:${TAG}" - -pull-identifier: - docker pull "dbrepo/identifier-service:${TAG}" - -pull-container: - docker pull "dbrepo/container-service:${TAG}" - -pull-database: - docker pull "dbrepo/database-service:${TAG}" - -pull-discovery: - docker pull "dbrepo/discovery-service:${TAG}" - -pull-gateway: - docker pull "dbrepo/gateway-service:${TAG}" - -pull-query: - docker pull "dbrepo/query-service:${TAG}" - -pull-table: - docker pull "dbrepo/table-service:${TAG}" - -pull-units: - docker pull "dbrepo/semantics-service:${TAG}" - -pull-broker: - docker pull "dbrepo/broker-service:${TAG}" - -pull-metadata: - docker pull "dbrepo/metadata-service:${TAG}" - -pull-search: - docker pull "dbrepo/search-service:${TAG}" - test-backend: test-authentication-service test-container-service test-database-service test-discovery-service test-gateway-service test-query-service test-table-service test-identifier-service test-metadata-service test-semantics-service test-analyse-service -test-authentication-service: build-backend-metadata-db build-backend-authentication - docker system prune -f --volumes +test-authentication-service: clean build-metadata-db build-authentication-service docker pull rabbitmq:3-management-alpine mvn -f ./fda-authentication-service/pom.xml clean test verify -test-identifier-service: build-backend-metadata-db build-backend-identifier - docker system prune -f --volumes +test-identifier-service: clean build-metadata-db build-identifier-service mvn -f ./fda-identifier-service/pom.xml clean test verify -test-container-service: build-backend-metadata-db build-backend-container - docker system prune -f --volumes - docker pull mysql:8.0 +test-container-service: clean build-metadata-db build-container-service mvn -f ./fda-container-service/pom.xml clean test verify -test-database-service: build-backend-metadata-db build-backend-database - docker system prune -f --volumes +test-database-service: clean build-metadata-db build-database-service docker pull rabbitmq:3-management-alpine - docker pull nginx:alpine mvn -f ./fda-database-service/pom.xml clean test verify -test-discovery-service: build-backend-metadata-db build-backend-discovery - docker system prune -f --volumes +test-discovery-service: clean build-metadata-db build-discovery-service mvn -f ./fda-discovery-service/pom.xml clean test verify -test-gateway-service: build-backend-metadata-db build-backend-gateway - docker system prune -f --volumes +test-gateway-service: clean build-metadata-db build-gateway-service mvn -f ./fda-gateway-service/pom.xml clean test verify -test-query-service: build-backend-metadata-db build-backend-query - docker system prune -f --volumes +test-query-service: clean build-metadata-db build-query-service mvn -f ./fda-query-service/pom.xml clean test verify -test-table-service: build-backend-metadata-db build-backend-table - docker system prune -f --volumes +test-table-service: clean build-metadata-db build-table-service mvn -f ./fda-table-service/pom.xml clean test verify -test-metadata-service: build-backend-metadata-db build-backend-metadata - docker system prune -f --volumes +test-metadata-service: clean build-metadata-db build-metadata-service mvn -f ./fda-metadata-service/pom.xml clean test verify test-semantics-service: build-semantics-service @@ -245,13 +184,16 @@ test-semantics-service: build-semantics-service test-analyse-service: build-analyse-service bash ./fda-analyse-service/test.sh -coverage-frontend: clean build-frontend +coverage-frontend: build-frontend yarn --cwd ./fda-ui run coverage || true test-frontend: clean build-frontend yarn --cwd ./fda-ui install - docker compose up -d - yarn --cwd ./fda-ui run test + yarn --cwd ./fda-ui run test:unit || true + yarn --cwd ./fda-ui run coverage || true + +clean: + docker system prune -f --volumes test-clients: bash ./.gitlab/test.sh @@ -259,4 +201,4 @@ test-clients: test: test-backend test-frontend teardown: - ./.dbrepo2/teardown + ./.junit/teardown diff --git a/docker-compose.dbrepo1.yml b/docker-compose.dbrepo1.yml index dfd09f5097236500634958c7072211892968336c..01db36561d40b6d328fade93b271caa6666d750d 100644 --- a/docker-compose.dbrepo1.yml +++ b/docker-compose.dbrepo1.yml @@ -31,7 +31,7 @@ networks: services: - metadata-db: + dbrepo-metadata-db: restart: on-failure container_name: dbrepo-metadata-db hostname: metadata-db @@ -60,7 +60,7 @@ services: logging: driver: json-file - gateway-service: + dbrepo-gateway-service: restart: on-failure container_name: dbrepo-gateway-service hostname: gateway-service @@ -72,12 +72,12 @@ services: env_file: - .env depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy logging: driver: json-file - database-service: + dbrepo-database-service: restart: on-failure container_name: dbrepo-database-service hostname: database-service @@ -90,16 +90,16 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock depends_on: - container-service: + dbrepo-container-service: condition: service_healthy - broker-service: + dbrepo-broker-service: condition: service_started - authentication-service: + dbrepo-authentication-service: condition: service_healthy logging: driver: json-file - container-service: + dbrepo-container-service: restart: on-failure container_name: dbrepo-container-service hostname: container-service @@ -111,12 +111,12 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock depends_on: - authentication-service: + dbrepo-authentication-service: condition: service_healthy logging: driver: json-file - authentication-service: + dbrepo-authentication-service: restart: on-failure container_name: dbrepo-authentication-service hostname: authentication-service @@ -126,16 +126,16 @@ services: env_file: - .env depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy - broker-service: + dbrepo-broker-service: condition: service_started - metadata-db: + dbrepo-metadata-db: condition: service_healthy logging: driver: json-file - query-service: + dbrepo-query-service: restart: on-failure container_name: dbrepo-query-service hostname: query-service @@ -148,14 +148,14 @@ services: volumes: - ${SHARED_FILESYSTEM}:/tmp depends_on: - table-service: + dbrepo-table-service: condition: service_healthy - authentication-service: + dbrepo-authentication-service: condition: service_healthy logging: driver: json-file - table-service: + dbrepo-table-service: restart: on-failure container_name: dbrepo-table-service hostname: table-service @@ -169,16 +169,16 @@ services: - /var/run/docker.sock:/var/run/docker.sock - ${SHARED_FILESYSTEM}:/tmp depends_on: - authentication-service: + dbrepo-authentication-service: condition: service_healthy - search-service: + dbrepo-search-service: condition: service_started - broker-service: + dbrepo-broker-service: condition: service_started logging: driver: json-file - identifier-service: + dbrepo-identifier-service: restart: on-failure container_name: dbrepo-identifier-service hostname: identifier-service @@ -188,16 +188,16 @@ services: env_file: - .env depends_on: - query-service: + dbrepo-query-service: condition: service_healthy - authentication-service: + dbrepo-authentication-service: condition: service_healthy volumes: - ${SHARED_FILESYSTEM}:/tmp logging: driver: json-file - fda-metadata-service: + dbrepo-metadata-service: restart: on-failure container_name: dbrepo-metadata-service hostname: metadata-service @@ -207,12 +207,12 @@ services: env_file: - .env depends_on: - metadata-db: + dbrepo-metadata-db: condition: service_started logging: driver: json-file - analyse-service: + dbrepo-analyse-service: restart: on-failure container_name: dbrepo-analyse-service hostname: analyse-service @@ -226,12 +226,12 @@ services: - ${SHARED_FILESYSTEM}:/tmp - /var/run/docker.sock:/var/run/docker.sock depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy logging: driver: json-file - semantics-service: + dbrepo-semantics-service: restart: on-failure container_name: dbrepo-semantics-service hostname: semantics-service @@ -244,14 +244,14 @@ services: - ${SHARED_FILESYSTEM}:/tmp - /var/run/docker.sock:/var/run/docker.sock depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy - metadata-db: + dbrepo-metadata-db: condition: service_healthy logging: driver: json-file - broker-service: + dbrepo-broker-service: restart: on-failure container_name: dbrepo-broker-service hostname: broker-service @@ -264,14 +264,14 @@ services: env_file: - .env depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy volumes: - broker-service-data:/var/lib/rabbitmq/ logging: driver: json-file - search-service: + dbrepo-search-service: restart: always container_name: dbrepo-search-service hostname: search-service @@ -279,7 +279,7 @@ services: networks: core: depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy ports: - 9200:9200 @@ -290,7 +290,7 @@ services: logging: driver: json-file - ui: + dbrepo-ui: restart: on-failure container_name: dbrepo-ui hostname: ui @@ -303,9 +303,9 @@ services: volumes: - ${SHARED_FILESYSTEM}:/tmp depends_on: - identifier-service: + dbrepo-identifier-service: condition: service_healthy - database-service: + dbrepo-database-service: condition: service_healthy logging: driver: json-file diff --git a/docker-compose.dbrepo2.yml b/docker-compose.dbrepo2.yml index dbd34f265e1c072684a60209978b9304a1b04fd4..7c169dd1cecfd8f1c9674ea6fb5c1be5a738c07c 100644 --- a/docker-compose.dbrepo2.yml +++ b/docker-compose.dbrepo2.yml @@ -31,7 +31,7 @@ networks: services: - metadata-db: + dbrepo-metadata-db: restart: on-failure container_name: dbrepo-metadata-db hostname: metadata-db @@ -48,7 +48,7 @@ services: logging: driver: json-file - discovery-service: + dbrepo-discovery-service: restart: on-failure container_name: dbrepo-discovery-service hostname: discovery-service @@ -60,7 +60,7 @@ services: logging: driver: json-file - gateway-service: + dbrepo-gateway-service: restart: on-failure container_name: dbrepo-gateway-service hostname: gateway-service @@ -72,12 +72,12 @@ services: env_file: - .env depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy logging: driver: json-file - database-service: + dbrepo-database-service: restart: on-failure container_name: dbrepo-database-service hostname: database-service @@ -90,16 +90,16 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock depends_on: - container-service: + dbrepo-container-service: condition: service_healthy - broker-service: + dbrepo-broker-service: condition: service_started - authentication-service: + dbrepo-authentication-service: condition: service_healthy logging: driver: json-file - container-service: + dbrepo-container-service: restart: on-failure container_name: dbrepo-container-service hostname: container-service @@ -111,12 +111,12 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock depends_on: - authentication-service: + dbrepo-authentication-service: condition: service_healthy logging: driver: json-file - authentication-service: + dbrepo-authentication-service: restart: on-failure container_name: dbrepo-authentication-service hostname: authentication-service @@ -126,16 +126,16 @@ services: env_file: - .env depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy - broker-service: + dbrepo-broker-service: condition: service_started - metadata-db: + dbrepo-metadata-db: condition: service_healthy logging: driver: json-file - query-service: + dbrepo-query-service: restart: on-failure container_name: dbrepo-query-service hostname: query-service @@ -148,14 +148,14 @@ services: volumes: - ${SHARED_FILESYSTEM}:/tmp depends_on: - table-service: + dbrepo-table-service: condition: service_healthy - authentication-service: + dbrepo-authentication-service: condition: service_healthy logging: driver: json-file - table-service: + dbrepo-table-service: restart: on-failure container_name: dbrepo-table-service hostname: table-service @@ -169,16 +169,16 @@ services: - /var/run/docker.sock:/var/run/docker.sock - ${SHARED_FILESYSTEM}:/tmp depends_on: - authentication-service: + dbrepo-authentication-service: condition: service_healthy - search-service: + dbrepo-search-service: condition: service_started - broker-service: + dbrepo-broker-service: condition: service_started logging: driver: json-file - identifier-service: + dbrepo-identifier-service: restart: on-failure container_name: dbrepo-identifier-service hostname: identifier-service @@ -188,16 +188,16 @@ services: env_file: - .env depends_on: - query-service: + dbrepo-query-service: condition: service_healthy - authentication-service: + dbrepo-authentication-service: condition: service_healthy volumes: - ${SHARED_FILESYSTEM}:/tmp logging: driver: json-file - metadata-service: + dbrepo-metadata-service: restart: on-failure container_name: dbrepo-metadata-service hostname: metadata-service @@ -207,12 +207,12 @@ services: env_file: - .env depends_on: - metadata-db: + dbrepo-metadata-db: condition: service_started logging: driver: json-file - analyse-service: + dbrepo-analyse-service: restart: on-failure container_name: dbrepo-analyse-service hostname: analyse-service @@ -226,12 +226,12 @@ services: - ${SHARED_FILESYSTEM}:/tmp - /var/run/docker.sock:/var/run/docker.sock depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy logging: driver: json-file - semantics-service: + dbrepo-semantics-service: restart: on-failure container_name: dbrepo-semantics-service hostname: semantics-service @@ -244,14 +244,14 @@ services: - ${SHARED_FILESYSTEM}:/tmp - /var/run/docker.sock:/var/run/docker.sock depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy - metadata-db: + dbrepo-metadata-db: condition: service_healthy logging: driver: json-file - broker-service: + dbrepo-broker-service: restart: on-failure container_name: dbrepo-broker-service hostname: broker-service @@ -264,14 +264,14 @@ services: env_file: - .env depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy volumes: - broker-service-data:/var/lib/rabbitmq/ logging: driver: json-file - search-service: + dbrepo-search-service: restart: always container_name: dbrepo-search-service hostname: search-service @@ -279,7 +279,7 @@ services: networks: core: depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy env_file: - .env @@ -288,7 +288,7 @@ services: logging: driver: json-file - ui: + dbrepo-ui: restart: on-failure container_name: dbrepo-ui hostname: ui @@ -301,9 +301,9 @@ services: volumes: - ${SHARED_FILESYSTEM}:/tmp depends_on: - identifier-service: + dbrepo-identifier-service: condition: service_healthy - database-service: + dbrepo-database-service: condition: service_healthy logging: driver: json-file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 8dffa725c1febcd0ce08eb324b3d152a7ee37b5c..8984baecab55922994fb89672234edc276ec221e 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -31,7 +31,7 @@ networks: services: - metadata-db: + dbrepo-metadata-db: restart: on-failure container_name: dbrepo-metadata-db hostname: metadata-db @@ -48,7 +48,7 @@ services: logging: driver: json-file - discovery-service: + dbrepo-discovery-service: restart: on-failure container_name: dbrepo-discovery-service hostname: discovery-service @@ -60,7 +60,7 @@ services: logging: driver: json-file - gateway-service: + dbrepo-gateway-service: restart: on-failure container_name: dbrepo-gateway-service hostname: gateway-service @@ -72,12 +72,12 @@ services: env_file: - .env depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy logging: driver: json-file - database-service: + dbrepo-database-service: restart: on-failure container_name: dbrepo-database-service hostname: database-service @@ -90,16 +90,16 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock depends_on: - container-service: + dbrepo-container-service: condition: service_healthy - broker-service: + dbrepo-broker-service: condition: service_started - authentication-service: + dbrepo-authentication-service: condition: service_healthy logging: driver: json-file - container-service: + dbrepo-container-service: restart: on-failure container_name: dbrepo-container-service hostname: container-service @@ -111,12 +111,12 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock depends_on: - authentication-service: + dbrepo-authentication-service: condition: service_healthy logging: driver: json-file - authentication-service: + dbrepo-authentication-service: restart: on-failure container_name: dbrepo-authentication-service hostname: authentication-service @@ -126,16 +126,16 @@ services: env_file: - .env depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy - broker-service: + dbrepo-broker-service: condition: service_started - metadata-db: + dbrepo-metadata-db: condition: service_healthy logging: driver: json-file - query-service: + dbrepo-query-service: restart: on-failure container_name: dbrepo-query-service hostname: query-service @@ -148,14 +148,14 @@ services: volumes: - ${SHARED_FILESYSTEM}:/tmp depends_on: - table-service: + dbrepo-table-service: condition: service_healthy - authentication-service: + dbrepo-authentication-service: condition: service_healthy logging: driver: json-file - table-service: + dbrepo-table-service: restart: on-failure container_name: dbrepo-table-service hostname: table-service @@ -169,16 +169,16 @@ services: - /var/run/docker.sock:/var/run/docker.sock - ${SHARED_FILESYSTEM}:/tmp depends_on: - authentication-service: + dbrepo-authentication-service: condition: service_healthy - search-service: + dbrepo-search-service: condition: service_started - broker-service: + dbrepo-broker-service: condition: service_started logging: driver: json-file - identifier-service: + dbrepo-identifier-service: restart: on-failure container_name: dbrepo-identifier-service hostname: identifier-service @@ -188,16 +188,16 @@ services: env_file: - .env depends_on: - query-service: + dbrepo-query-service: condition: service_healthy - authentication-service: + dbrepo-authentication-service: condition: service_healthy volumes: - ${SHARED_FILESYSTEM}:/tmp logging: driver: json-file - metadata-service: + dbrepo-metadata-service: restart: on-failure container_name: dbrepo-metadata-service hostname: metadata-service @@ -207,12 +207,12 @@ services: env_file: - .env depends_on: - metadata-db: + dbrepo-metadata-db: condition: service_started logging: driver: json-file - analyse-service: + dbrepo-analyse-service: restart: on-failure container_name: dbrepo-analyse-service hostname: analyse-service @@ -226,12 +226,12 @@ services: - ${SHARED_FILESYSTEM}:/tmp - /var/run/docker.sock:/var/run/docker.sock depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy logging: driver: json-file - semantics-service: + dbrepo-semantics-service: restart: on-failure container_name: dbrepo-semantics-service hostname: semantics-service @@ -244,14 +244,14 @@ services: - ${SHARED_FILESYSTEM}:/tmp - /var/run/docker.sock:/var/run/docker.sock depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy - metadata-db: + dbrepo-metadata-db: condition: service_healthy logging: driver: json-file - broker-service: + dbrepo-broker-service: restart: on-failure container_name: dbrepo-broker-service hostname: broker-service @@ -263,14 +263,14 @@ services: env_file: - .env depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy volumes: - broker-service-data:/var/lib/rabbitmq/ logging: driver: json-file - search-service: + dbrepo-search-service: restart: always container_name: dbrepo-search-service hostname: search-service @@ -278,7 +278,7 @@ services: networks: core: depends_on: - discovery-service: + dbrepo-discovery-service: condition: service_healthy env_file: - .env @@ -287,7 +287,7 @@ services: logging: driver: json-file - ui: + dbrepo-ui: restart: on-failure container_name: dbrepo-ui hostname: ui @@ -295,14 +295,16 @@ services: networks: core: public: + ports: + - "3000:3000" env_file: - .env volumes: - ${SHARED_FILESYSTEM}:/tmp depends_on: - identifier-service: + dbrepo-identifier-service: condition: service_healthy - database-service: + dbrepo-database-service: condition: service_healthy logging: driver: json-file diff --git a/docker-compose.yml b/docker-compose.yml index fd864bd166bfed2443e866de714ba9c4ce269c49..955a59d41ca1896c6915aaf8aa4cf3916567f141 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,27 +10,26 @@ volumes: broker-service-data: networks: - public: - name: public + userdb: + name: userdb driver: bridge ipam: config: - - subnet: 172.29.0.0/16 - userdb: - name: userdb + - subnet: 172.30.0.0/16 + public: + name: public driver: bridge ipam: config: - - subnet: 172.28.0.0/16 + - subnet: 172.31.0.0/16 core: name: core driver: bridge ipam: config: - - subnet: 172.27.0.0/16 + - subnet: 172.32.0.0/16 services: - fda-metadata-db: restart: on-failure container_name: dbrepo-metadata-db diff --git a/fda-analyse-service/coverage.txt b/fda-analyse-service/coverage.txt deleted file mode 100644 index bd9242486b093b8e72178a4ea40d827b672b6788..0000000000000000000000000000000000000000 --- a/fda-analyse-service/coverage.txt +++ /dev/null @@ -1,9 +0,0 @@ -Name Stmts Miss Cover ------------------------------------------------ -determine_dt.py 56 16 71% -determine_pk.py 49 0 100% -test/__init__.py 0 0 100% -test/test_determine_dt.py 34 7 79% -test/test_determine_pk.py 41 1 98% ------------------------------------------------ -TOTAL 180 24 87% diff --git a/fda-analyse-service/report.xml b/fda-analyse-service/report.xml deleted file mode 100644 index 5495aa234b14a375786b0e8c9c482875c2573e2b..0000000000000000000000000000000000000000 --- a/fda-analyse-service/report.xml +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="12" time="1.131" timestamp="2023-01-26T09:31:19.024531" hostname="medusa"><testcase classname="test.test_determine_dt.DetermineDatatypesTest" name="test_determine_datatypes_fileDoesNotExist_fails" time="0.001" /><testcase classname="test.test_determine_dt.DetermineDatatypesTest" name="test_determine_datatypes_fileEmpty_fails" time="0.002" /><testcase classname="test.test_determine_dt.DetermineDatatypesTest" name="test_determine_datatypes_separatorSemicolon_succeeds" time="0.004" /><testcase classname="test.test_determine_dt.DetermineDatatypesTest" name="test_determine_datatypes_succeeds" time="0.166" /><testcase classname="test.test_determine_pk.DeterminePrimaryKeyTest" name="test_determine_pk_largeFileIdFirst_succeeds" time="0.073" /><testcase classname="test.test_determine_pk.DeterminePrimaryKeyTest" name="test_determine_pk_largeFileIdInBetween_succeeds" time="0.072" /><testcase classname="test.test_determine_pk.DeterminePrimaryKeyTest" name="test_determine_pk_largeFileNoPrimaryKey_fails" time="0.062" /><testcase classname="test.test_determine_pk.DeterminePrimaryKeyTest" name="test_determine_pk_largeFileNullInUnique_fails" time="0.106" /><testcase classname="test.test_determine_pk.DeterminePrimaryKeyTest" name="test_determine_pk_smallFileIdFirst_fails" time="0.013" /><testcase classname="test.test_determine_pk.DeterminePrimaryKeyTest" name="test_determine_pk_smallFileIdIntBetween_fails" time="0.013" /><testcase classname="test.test_determine_pk.DeterminePrimaryKeyTest" name="test_determine_pk_smallFileNoPrimaryKey_fails" time="0.011" /><testcase classname="test.test_determine_pk.DeterminePrimaryKeyTest" name="test_determine_pk_smallFileNullInUnique_fails" time="0.012" /></testsuite></testsuites> \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java index be355787b98d46890355f5ddabf7fe1e5c38a86e..c6c927f9b93472b3f544dfae2544ce024375bfb7 100644 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java +++ b/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java @@ -294,7 +294,7 @@ public abstract class BaseUnitTest { public final static Long CONTAINER_BROKER_ID = 1L; public final static String CONTAINER_BROKER_NAME = "broker-service"; public final static String CONTAINER_BROKER_INTERNAL_NAME = "broker-service"; - public final static String CONTAINER_BROKER_IP = "172.29.0.2"; + public final static String CONTAINER_BROKER_IP = "172.31.0.2"; public final static Container CONTAINER_BROKER = Container.builder() .id(CONTAINER_BROKER_ID) diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java index fc15443f9e2a8843a187ccebba2e57a75be38546..37b2ca7d1b18fb9dacc025176e9967fc3c990021 100644 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java +++ b/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java @@ -4,7 +4,9 @@ import at.tuwien.entities.container.Container; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.exception.NotModifiedException; import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.Network; import com.github.dockerjava.api.model.RestartPolicy; import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientBuilder; @@ -13,6 +15,7 @@ import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import com.github.dockerjava.transport.DockerHttpClient; import lombok.extern.log4j.Log4j2; +import java.util.Arrays; import java.util.Objects; @Log4j2 @@ -56,7 +59,7 @@ public class DockerConfig { final CreateContainerResponse response = dockerClient.createContainerCmd(container.getImage().getRepository() + ":" + container.getImage().getTag()) .withHostConfig(hostConfig.withNetworkMode("fda-userdb")) .withName(container.getInternalName()) - .withIpv4Address("172.28.0.5") + .withIpv4Address("172.30.0.5") .withHostName(container.getInternalName()) .withEnv("MARIADB_USER=mariadb", "MARIADB_PASSWORD=mariadb", "MARIADB_ROOT_PASSWORD=mariadb", "MARIADB_DATABASE=weather") .exec(); @@ -86,4 +89,59 @@ public class DockerConfig { log.debug("container {} was removed", container.getHash()); } + public static void removeAllContainers() { + dockerClient.listContainersCmd() + .withShowAll(true) + .exec() + .forEach(container -> { + log.info("Delete container {}", Arrays.asList(container.getNames())); + try { + dockerClient.stopContainerCmd(container.getId()).exec(); + } catch (NotModifiedException e) { + // ignore + } + dockerClient.removeContainerCmd(container.getId()).exec(); + }); + dockerClient.listVolumesCmd() + .withDanglingFilter(true) + .exec() + .getVolumes() + .forEach(volume -> { + log.info("Delete volume {}", volume.getName()); + try { + dockerClient.removeVolumeCmd(volume.getName()).exec(); + } catch (NotModifiedException e) { + // ignore + } + }); + } + + public static void removeAllNetworks() { + dockerClient.listNetworksCmd() + .exec() + .stream() + .filter(n -> n.getName().startsWith("fda")) + .forEach(network -> { + log.info("Delete network {}", network.getName()); + dockerClient.removeNetworkCmd(network.getId()).exec(); + }); + } + + public static void createAllNetworks() { + dockerClient.createNetworkCmd() + .withName("fda-userdb") + .withIpam(new Network.Ipam() + .withConfig(new Network.Ipam.Config() + .withSubnet("172.30.0.0/16"))) + .withEnableIpv6(false) + .exec(); + dockerClient.createNetworkCmd() + .withName("fda-public") + .withIpam(new Network.Ipam() + .withConfig(new Network.Ipam.Config() + .withSubnet("172.31.0.0/16"))) + .withEnableIpv6(false) + .exec(); + } + } diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java index f887b5a86cc45732941fdfa2491c734eb6d8e55c..ba84081446b1d902dad583a0b2c0602a55684ceb 100644 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java +++ b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java @@ -2,6 +2,7 @@ package at.tuwien.service; import at.tuwien.BaseUnitTest; import at.tuwien.api.amqp.CreateUserDto; +import at.tuwien.config.DockerConfig; import at.tuwien.config.RabbitMqConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.dto.AmqpUserBriefDto; @@ -13,9 +14,7 @@ import com.github.dockerjava.api.exception.NotModifiedException; import com.github.dockerjava.api.model.Network; import com.github.dockerjava.api.model.PortBinding; import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -56,24 +55,16 @@ public class QueueServiceIntegrationTest extends BaseUnitTest { @Autowired private RabbitMqConfig amqpConfig; + @BeforeAll + public static void beforeAll() { + afterAll(); + DockerConfig.createAllNetworks(); + } + @BeforeEach public void beforeEach() throws InterruptedException { afterEach(); - /* create networks */ - dockerClient.createNetworkCmd() - .withName("fda-userdb") - .withIpam(new Network.Ipam() - .withConfig(new Network.Ipam.Config() - .withSubnet("172.28.0.0/16"))) - .withEnableIpv6(false) - .exec(); - dockerClient.createNetworkCmd() - .withName("fda-public") - .withIpam(new Network.Ipam() - .withConfig(new Network.Ipam.Config() - .withSubnet("172.29.0.0/16"))) - .withEnableIpv6(false) - .exec(); + DockerConfig.createAllNetworks(); /* create container */ final PortBinding binding = PortBinding.parse("15672:15672"); final CreateContainerResponse response1 = dockerClient.createContainerCmd(IMAGE_BROKER_IMAGE + ":" + IMAGE_BROKER_TAG) @@ -92,28 +83,14 @@ public class QueueServiceIntegrationTest extends BaseUnitTest { @AfterEach public void afterEach() { - /* stop containers and remove them */ - dockerClient.listContainersCmd() - .withShowAll(true) - .exec() - .forEach(container -> { - log.info("Delete container {}", Arrays.asList(container.getNames())); - try { - dockerClient.stopContainerCmd(container.getId()).exec(); - } catch (NotModifiedException e) { - // ignore - } - dockerClient.removeContainerCmd(container.getId()).exec(); - }); - /* remove networks */ - dockerClient.listNetworksCmd() - .exec() - .stream() - .filter(n -> n.getName().startsWith("fda")) - .forEach(network -> { - log.info("Delete network {}", network.getName()); - dockerClient.removeNetworkCmd(network.getId()).exec(); - }); + DockerConfig.removeAllContainers(); + DockerConfig.removeAllNetworks(); + } + + @AfterAll + public static void afterAll() { + DockerConfig.removeAllContainers(); + DockerConfig.removeAllNetworks(); } @Test diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java index 371ed3450a01c2f2c1803e69797e88e56f87dbc1..c72d988189d61e44b59a33015effb1a69789c94a 100644 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java +++ b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java @@ -6,6 +6,7 @@ import at.tuwien.api.user.RoleTypeDto; import at.tuwien.api.user.UserPasswordDto; import at.tuwien.api.user.UserRolesDto; import at.tuwien.config.AuthenticationConfig; +import at.tuwien.config.DockerConfig; import at.tuwien.config.RabbitMqConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.entities.user.RoleType; @@ -14,8 +15,6 @@ import at.tuwien.exception.*; import at.tuwien.repositories.ImageRepository; import at.tuwien.repositories.UserRepository; import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.api.exception.NotModifiedException; -import com.github.dockerjava.api.model.Network; import com.github.dockerjava.api.model.PortBinding; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.*; @@ -25,13 +24,10 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.DuplicateKeyException; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.web.client.RestTemplate; -import javax.validation.ConstraintViolationException; -import java.util.Arrays; import java.util.List; import static at.tuwien.config.DockerConfig.*; @@ -71,57 +67,28 @@ public class UserServiceIntegrationTest extends BaseUnitTest { @BeforeAll public static void beforeAll() { afterAll(); - /* create networks */ - dockerClient.createNetworkCmd() - .withName("fda-userdb") - .withIpam(new Network.Ipam() - .withConfig(new Network.Ipam.Config() - .withSubnet("172.28.0.0/16"))) - .withEnableIpv6(false) - .exec(); - dockerClient.createNetworkCmd() - .withName("fda-public") - .withIpam(new Network.Ipam() - .withConfig(new Network.Ipam.Config() - .withSubnet("172.29.0.0/16"))) - .withEnableIpv6(false) - .exec(); - } - - @AfterAll - public static void afterAll() { - /* remove networks */ - dockerClient.listNetworksCmd() - .exec() - .stream() - .filter(n -> n.getName().startsWith("fda")) - .forEach(network -> { - log.info("Delete network {}", network.getName()); - dockerClient.removeNetworkCmd(network.getId()).exec(); - }); + DockerConfig.createAllNetworks(); } @BeforeEach public void beforeEach() { + afterEach(); + DockerConfig.createAllNetworks(); + /* metadata database */ imageRepository.save(IMAGE_1); userRepository.save(USER_1); } @AfterEach public void afterEach() { - /* stop containers and remove them */ - dockerClient.listContainersCmd() - .withShowAll(true) - .exec() - .forEach(container -> { - log.info("Delete container {}", Arrays.asList(container.getNames())); - try { - dockerClient.stopContainerCmd(container.getId()).exec(); - } catch (NotModifiedException e) { - // ignore - } - dockerClient.removeContainerCmd(container.getId()).exec(); - }); + DockerConfig.removeAllContainers(); + DockerConfig.removeAllNetworks(); + } + + @AfterAll + public static void afterAll() { + DockerConfig.removeAllContainers(); + DockerConfig.removeAllNetworks(); } @Test diff --git a/fda-authentication-service/rest-service/src/test/resources/application.properties b/fda-authentication-service/rest-service/src/test/resources/application.properties index b0ccd00eda3328d89ea1cea158719ad810e6712b..d57104b1eaa5bb5e645f82671879ae16d2e2b015 100644 --- a/fda-authentication-service/rest-service/src/test/resources/application.properties +++ b/fda-authentication-service/rest-service/src/test/resources/application.properties @@ -25,6 +25,6 @@ spring.rabbitmq.username=guest spring.rabbitmq.password=guest # user -fda.gateway.endpoint=http://172.29.0.2:15672/api/ +fda.gateway.endpoint=http://172.31.0.2:15672/api/ fda.mail.verify=true fda.token.max=1 \ No newline at end of file diff --git a/fda-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java b/fda-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java index 9dfe9e4b53e3072a2aa40bb88b4820473fb649f4..5c3d11e564d31020f65612052bbe570c212f887d 100644 --- a/fda-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java +++ b/fda-container-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java @@ -5,6 +5,7 @@ import at.tuwien.entities.container.Container; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.mapper.ContainerMapper; +import at.tuwien.service.ContainerService; import at.tuwien.service.UserService; import at.tuwien.service.impl.ContainerServiceImpl; import io.micrometer.core.annotation.Timed; @@ -34,7 +35,7 @@ public class ContainerEndpoint { private final UserService userService; private final ContainerMapper containerMapper; - private final ContainerServiceImpl containerService; + private final ContainerService containerService; @Autowired public ContainerEndpoint(UserService userService, ContainerServiceImpl containerService, @@ -47,9 +48,10 @@ public class ContainerEndpoint { @GetMapping @Transactional(readOnly = true) @Operation(summary = "Find all containers") - public ResponseEntity<List<ContainerBriefDto>> findAll(Principal principal) { - log.debug("endpoint find all containers, principal={}", principal); - final List<Container> containers = containerService.getAll(); + public ResponseEntity<List<ContainerBriefDto>> findAll(Principal principal, + @RequestParam(required = false) Integer limit) { + log.debug("endpoint find all containers, principal={}, limit={}", principal, limit); + final List<Container> containers = containerService.getAll(limit); return ResponseEntity.ok() .body(containers.stream() .map(containerMapper::containerToDatabaseContainerBriefDto) @@ -95,7 +97,7 @@ public class ContainerEndpoint { @Valid @RequestBody ContainerChangeDto changeDto, @NotNull Principal principal) throws ContainerNotFoundException, ContainerAlreadyRunningException, ContainerAlreadyStoppedException, - UserNotFoundException, NotAllowedException { + UserNotFoundException, NotAllowedException, DockerClientException { log.debug("endpoint modify container, containerId={}, changeDto={}, principal={}", containerId, changeDto, principal); final User user = userService.findByUsername(principal.getName()); final Container container = containerService.find(containerId); @@ -124,7 +126,7 @@ public class ContainerEndpoint { @Operation(summary = "Delete some container", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<?> delete(@NotNull @PathVariable("id") Long containerId, @NotNull Principal principal) throws ContainerNotFoundException, - ContainerStillRunningException, ContainerAlreadyRemovedException { + ContainerStillRunningException, ContainerAlreadyRemovedException, DockerClientException { log.debug("endpoint delete container, containerId={}, principal={}", containerId, principal); containerService.remove(containerId); return ResponseEntity.status(HttpStatus.ACCEPTED) diff --git a/fda-container-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java b/fda-container-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java index 7f02363385b4618a4d5430694de644ac54e54284..bbb5d2ecba97a1acd2314ce9a9cbb9b16de68e82 100644 --- a/fda-container-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java +++ b/fda-container-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java @@ -10,10 +10,6 @@ import at.tuwien.mapper.ImageMapper; import at.tuwien.service.impl.ImageServiceImpl; import io.micrometer.core.annotation.Timed; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; @@ -46,9 +42,6 @@ public class ImageEndpoint { } @GetMapping - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Found images") - }) @Transactional(readOnly = true) @Timed(value = "image.list", description = "Time needed to list the container images") @Operation(summary = "Find all images") @@ -62,10 +55,6 @@ public class ImageEndpoint { } @PostMapping - @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "Created image"), - @ApiResponse(responseCode = "404", description = "Image not found"), - }) @Transactional @Timed(value = "image.create", description = "Time needed to create a container image") @PreAuthorize("hasRole('ROLE_DEVELOPER')") @@ -86,10 +75,6 @@ public class ImageEndpoint { } @GetMapping("/{id}") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Found some image"), - @ApiResponse(responseCode = "404", description = "Image not found"), - }) @Transactional(readOnly = true) @Timed(value = "image.find", description = "Time needed to find a container image") @Operation(summary = "Find some image") @@ -104,10 +89,6 @@ public class ImageEndpoint { } @PutMapping("/{id}") - @ApiResponses(value = { - @ApiResponse(responseCode = "202", description = "Updated image"), - @ApiResponse(responseCode = "404", description = "Image not found"), - }) @Transactional @Timed(value = "image.update", description = "Time needed to update a container image") @PreAuthorize("hasRole('DEVELOPER')") @@ -125,11 +106,6 @@ public class ImageEndpoint { } @DeleteMapping("/{id}") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Deleted image"), - @ApiResponse(responseCode = "403", description = "Unable to delete image"), - @ApiResponse(responseCode = "404", description = "Image not found"), - }) @Transactional @Timed(value = "image.delete", description = "Time needed to delete a container image") @PreAuthorize("hasRole('DEVELOPER')") diff --git a/fda-container-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java b/fda-container-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java index c22a810fea54b58c45b71cf70b23efcde52ae031..0a59ea1f4519a9212ddabb9f05ed96276fc07441 100644 --- a/fda-container-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java +++ b/fda-container-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java @@ -12,6 +12,7 @@ import at.tuwien.entities.container.image.ContainerImageEnvironmentItemType; import at.tuwien.entities.user.RoleType; import at.tuwien.entities.user.User; import com.github.dockerjava.api.command.InspectContainerResponse; +import com.github.dockerjava.api.model.HealthCheck; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -284,30 +285,36 @@ public abstract class BaseUnitTest { public final static Long CONTAINER_1_ID = 1L; public final static String CONTAINER_1_HASH = "deadbeef"; - public final static String CONTAINER_1_NAME = "fda-userdb-u01"; - public final static String CONTAINER_1_INTERNALNAME = "dbrepo-userdb-fda-userdb-u01"; - public final static String CONTAINER_1_DATABASE = "univie"; - public final static String CONTAINER_1_IP = "172.28.0.5"; - public final static Instant CONTAINER_1_CREATED = Instant.now().minus(1, HOURS); + public final static String CONTAINER_1_IP = "172.30.0.5"; + public final static String CONTAINER_1_NAME = "u01"; + public final static String CONTAINER_1_INTERNALNAME = "dbrepo-userdb-u01"; + public final static Instant CONTAINER_1_CREATED = Instant.now().minus(2, HOURS); + public final static Instant CONTAINER_1_UPDATED = Instant.now(); + public final static HealthCheck CONTAINER_1_HEALTHCHECK = new HealthCheck() + .withTest(List.of("CMD", "mysqladmin", "ping", "--host=127.0.0.1", "--password=mariadb")); + + public final static String[] CONTAINER_1_ENV = new String[]{"MARIADB_ROOT_PASSWORD=mariadb", "MARIADB_USER=mariadb", + "MARIADB_PASSWORD=mariadb", "MARIADB_DATABASE=weather"}; public final static Container CONTAINER_1 = Container.builder() .id(CONTAINER_1_ID) .name(CONTAINER_1_NAME) + .hash(CONTAINER_1_HASH) .internalName(CONTAINER_1_INTERNALNAME) + .created(CONTAINER_1_CREATED) + .lastModified(CONTAINER_1_UPDATED) + .ipAddress(CONTAINER_1_IP) .imageId(IMAGE_1_ID) .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) .creator(USER_1) + .owner(USER_1) .build(); public final static Long CONTAINER_2_ID = 2L; public final static String CONTAINER_2_HASH = "deadbeef"; - public final static String CONTAINER_2_NAME = "fda-userdb-u02"; - public final static String CONTAINER_2_INTERNALNAME = "dbrepo-userdb-fda-userdb-u02"; - public final static String CONTAINER_2_DATABASE = "univie"; - public final static String CONTAINER_2_IP = "172.28.0.6"; + public final static String CONTAINER_2_NAME = "u02"; + public final static String CONTAINER_2_INTERNALNAME = "dbrepo-userdb-u02"; + public final static String CONTAINER_2_IP = "172.30.0.6"; public final static Instant CONTAINER_2_CREATED = Instant.now().minus(2, HOURS); public final static Container CONTAINER_2 = Container.builder() @@ -324,8 +331,8 @@ public abstract class BaseUnitTest { public final static Long CONTAINER_3_ID = 3L; public final static String CONTAINER_3_HASH = "deadbeef"; - public final static String CONTAINER_3_NAME = "fda-userdb-u03"; - public final static String CONTAINER_3_INTERNALNAME = "dbrepo-userdb-fda-userdb-u03"; + public final static String CONTAINER_3_NAME = "u03"; + public final static String CONTAINER_3_INTERNALNAME = "dbrepo-userdb-u03"; public final static String CONTAINER_3_DATABASE = "u03"; public final static String CONTAINER_3_IP = "173.38.0.7"; public final static Instant CONTAINER_3_CREATED = Instant.now().minus(2, HOURS); diff --git a/fda-container-service/rest-service/src/test/java/at/tuwien/endpoint/ContainerEndpointUnitTest.java b/fda-container-service/rest-service/src/test/java/at/tuwien/endpoint/ContainerEndpointUnitTest.java index 7407254558e316d28a8fbccc878d9b789712cd81..cd82d0b81d9e7befddfea0c1018863fcf7c8670d 100644 --- a/fda-container-service/rest-service/src/test/java/at/tuwien/endpoint/ContainerEndpointUnitTest.java +++ b/fda-container-service/rest-service/src/test/java/at/tuwien/endpoint/ContainerEndpointUnitTest.java @@ -2,7 +2,7 @@ package at.tuwien.endpoint; import at.tuwien.BaseUnitTest; import at.tuwien.api.container.*; -import at.tuwien.config.DockerUtil; +import at.tuwien.config.DockerConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.endpoints.ContainerEndpoint; import at.tuwien.entities.container.Container; @@ -48,7 +48,7 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { private ContainerEndpoint containerEndpoint; @Autowired - private DockerUtil dockerUtil; + private DockerConfig dockerUtil; @Test public void findById_anonymous_succeeds() throws DockerClientException, ContainerNotFoundException, @@ -142,7 +142,7 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) public void delete_developer_succeeds() throws ContainerStillRunningException, ContainerAlreadyRemovedException, - ContainerNotFoundException { + ContainerNotFoundException, DockerClientException { /* mock */ when(userRepository.findByUsername(USER_2_USERNAME)) @@ -170,7 +170,7 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { public void findAll_anonymous_succeeds() { /* test */ - findAll_generic(null); + findAll_generic(null, null); } @Test @@ -178,7 +178,7 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { public void findAll_anonymous2_succeeds() { /* test */ - findAll_generic(null); + findAll_generic(null, null); } @Test @@ -190,7 +190,7 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { .thenReturn(Optional.of(USER_1)); /* test */ - findAll_generic(USER_1_PRINCIPAL); + findAll_generic(USER_1_PRINCIPAL, null); } @Test @@ -202,7 +202,7 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { .thenReturn(Optional.of(USER_2)); /* test */ - findAll_generic(USER_2_PRINCIPAL); + findAll_generic(USER_2_PRINCIPAL, null); } @Test @@ -214,7 +214,7 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { .thenReturn(Optional.of(USER_3)); /* test */ - findAll_generic(USER_3_PRINCIPAL); + findAll_generic(USER_3_PRINCIPAL, null); } @Test @@ -337,7 +337,8 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) public void modify_researcherStart_succeeds() throws ContainerAlreadyRunningException, - ContainerAlreadyStoppedException, ContainerNotFoundException, UserNotFoundException, NotAllowedException { + ContainerAlreadyStoppedException, ContainerNotFoundException, UserNotFoundException, NotAllowedException, + DockerClientException { /* mock */ when(userRepository.findByUsername(USER_1_USERNAME)) @@ -367,7 +368,8 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) public void modify_researcherStop_succeeds() throws ContainerAlreadyRunningException, - ContainerAlreadyStoppedException, ContainerNotFoundException, UserNotFoundException, NotAllowedException { + ContainerAlreadyStoppedException, ContainerNotFoundException, UserNotFoundException, NotAllowedException, + DockerClientException { /* mock */ when(userRepository.findByUsername(USER_1_USERNAME)) @@ -398,7 +400,7 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) public void modify_developerForeignStart_succeeds() throws UserNotFoundException, ContainerAlreadyRunningException, - NotAllowedException, ContainerAlreadyStoppedException, ContainerNotFoundException { + NotAllowedException, ContainerAlreadyStoppedException, ContainerNotFoundException, DockerClientException { /* mock */ when(userRepository.findByUsername(USER_2_USERNAME)) @@ -411,7 +413,7 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { @Test @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) public void modify_developerForeignStop_succeeds() throws UserNotFoundException, ContainerAlreadyRunningException, - NotAllowedException, ContainerAlreadyStoppedException, ContainerNotFoundException { + NotAllowedException, ContainerAlreadyStoppedException, ContainerNotFoundException, DockerClientException { /* mock */ when(userRepository.findByUsername(USER_2_USERNAME)) @@ -471,7 +473,7 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { } public void delete_generic(Long containerId, Container container, Principal principal) throws ContainerNotFoundException, - ContainerStillRunningException, ContainerAlreadyRemovedException { + ContainerStillRunningException, ContainerAlreadyRemovedException, DockerClientException { /* mock */ when(containerService.find(containerId)) @@ -486,14 +488,14 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { assertNull(response.getBody()); } - public void findAll_generic(Principal principal) { + public void findAll_generic(Principal principal, Integer limit) { /* mock */ - when(containerService.getAll()) + when(containerService.getAll(limit)) .thenReturn(List.of(CONTAINER_1, CONTAINER_2)); /* test */ - final ResponseEntity<List<ContainerBriefDto>> response = containerEndpoint.findAll(principal); + final ResponseEntity<List<ContainerBriefDto>> response = containerEndpoint.findAll(principal, limit); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); final List<ContainerBriefDto> body = response.getBody(); @@ -522,7 +524,8 @@ public class ContainerEndpointUnitTest extends BaseUnitTest { } public void modify_generic(ContainerActionTypeDto data, Long containerId, Container container, Principal principal) - throws ContainerAlreadyRunningException, ContainerNotFoundException, ContainerAlreadyStoppedException, UserNotFoundException, NotAllowedException { + throws ContainerAlreadyRunningException, ContainerNotFoundException, ContainerAlreadyStoppedException, + UserNotFoundException, NotAllowedException, DockerClientException { final ContainerChangeDto request = ContainerChangeDto.builder() .action(data) .build(); diff --git a/fda-container-service/rest-service/src/test/java/at/tuwien/endpoint/ImageEndpointUnitTest.java b/fda-container-service/rest-service/src/test/java/at/tuwien/endpoint/ImageEndpointUnitTest.java index f84ae04db59091c81e86ff91854ff0106cb6bd5e..9ef775d53dde5ee5f99ede094d4a1944dbd7a879 100644 --- a/fda-container-service/rest-service/src/test/java/at/tuwien/endpoint/ImageEndpointUnitTest.java +++ b/fda-container-service/rest-service/src/test/java/at/tuwien/endpoint/ImageEndpointUnitTest.java @@ -1,21 +1,17 @@ package at.tuwien.endpoint; import at.tuwien.BaseUnitTest; -import at.tuwien.api.container.*; import at.tuwien.api.container.image.ImageBriefDto; import at.tuwien.api.container.image.ImageChangeDto; import at.tuwien.api.container.image.ImageCreateDto; import at.tuwien.api.container.image.ImageDto; -import at.tuwien.config.DockerUtil; +import at.tuwien.config.DockerConfig; import at.tuwien.config.ReadyConfig; -import at.tuwien.endpoints.ContainerEndpoint; import at.tuwien.endpoints.ImageEndpoint; -import at.tuwien.entities.container.Container; import at.tuwien.entities.container.image.ContainerImage; import at.tuwien.exception.*; import at.tuwien.repository.jpa.ImageRepository; import at.tuwien.repository.jpa.UserRepository; -import at.tuwien.service.impl.ContainerServiceImpl; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -31,7 +27,6 @@ import org.springframework.security.test.context.support.WithAnonymousUser; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit.jupiter.SpringExtension; -import javax.ws.rs.NotAllowedException; import java.security.Principal; import java.util.List; import java.util.Optional; @@ -57,7 +52,7 @@ public class ImageEndpointUnitTest extends BaseUnitTest { private ImageEndpoint imageEndpoint; @Autowired - private DockerUtil dockerUtil; + private DockerConfig dockerUtil; @Test public void findAll_anonymous_succeeds() { diff --git a/fda-container-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java b/fda-container-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java index 8aef59c9fcc56627c6a651a6fe457875abc91932..fd1c316006658919c030f290a359b3a3bdb115fe 100644 --- a/fda-container-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java +++ b/fda-container-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java @@ -5,7 +5,6 @@ import at.tuwien.api.container.ContainerCreateRequestDto; import at.tuwien.config.DockerUtil; import at.tuwien.config.ReadyConfig; import at.tuwien.entities.container.Container; -import at.tuwien.entities.container.image.ContainerImage; import at.tuwien.exception.*; import at.tuwien.repository.jpa.ContainerRepository; import at.tuwien.repository.jpa.ImageRepository; @@ -66,32 +65,19 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { .withName("fda-userdb") .withIpam(new Network.Ipam() .withConfig(new Network.Ipam.Config() - .withSubnet("172.28.0.0/16"))) + .withSubnet("172.30.0.0/16"))) .withEnableIpv6(false) .exec(); dockerClient.createNetworkCmd() .withName("fda-public") .withIpam(new Network.Ipam() .withConfig(new Network.Ipam.Config() - .withSubnet("172.29.0.0/16"))) + .withSubnet("172.31.0.0/16"))) .withEnableIpv6(false) .exec(); /* mock data */ userRepository.save(USER_1); - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); imageRepository.save(IMAGE_1); } @@ -151,30 +137,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { final Principal principal = new BasicUserPrincipal(USER_1_USERNAME); /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); containerRepository.save(CONTAINER_1); /* test */ @@ -221,30 +183,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { ContainerAlreadyRunningException { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); dockerUtil.createContainer(CONTAINER_1); containerRepository.save(CONTAINER_1); @@ -257,30 +195,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { ContainerAlreadyStoppedException { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); dockerUtil.createContainer(CONTAINER_1); dockerUtil.startContainer(CONTAINER_1); containerRepository.save(CONTAINER_1); @@ -293,30 +207,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void change_startSavedButNotFound_fails() { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); containerRepository.save(CONTAINER_1); /* test */ @@ -329,30 +219,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void change_removeSavedButNotFound_fails() { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); containerRepository.save(CONTAINER_1); /* test */ @@ -365,77 +231,31 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void getAll_succeeds() { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - final Container CONTAINER_2 = Container.builder() - .id(CONTAINER_2_ID) - .name(CONTAINER_2_NAME) - .internalName(CONTAINER_2_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_2_HASH) - .ipAddress(CONTAINER_2_IP) - .created(CONTAINER_2_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); containerRepository.save(CONTAINER_1); containerRepository.save(CONTAINER_2); /* test */ - final List<Container> response = containerService.getAll(); + final List<Container> response = containerService.getAll(null); assertEquals(2, response.size()); } + @Test + public void getAll_limit_succeeds() { + + /* mock */ + containerRepository.save(CONTAINER_1); + containerRepository.save(CONTAINER_2); + + /* test */ + final List<Container> response = containerService.getAll(1); + assertEquals(1, response.size()); + } + @Test public void remove_succeeds() throws DockerClientException, ContainerStillRunningException, ContainerNotFoundException, ContainerAlreadyRemovedException { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); dockerUtil.createContainer(CONTAINER_1); dockerUtil.stopContainer(CONTAINER_1); containerRepository.save(CONTAINER_1); @@ -457,30 +277,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void remove_stillRunning_fails() throws InterruptedException { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); dockerUtil.createContainer(CONTAINER_1); dockerUtil.startContainer(CONTAINER_1); containerRepository.save(CONTAINER_1); @@ -495,30 +291,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void change_alreadyRunning_fails() throws InterruptedException { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); dockerUtil.createContainer(CONTAINER_1); dockerUtil.startContainer(CONTAINER_1); containerRepository.save(CONTAINER_1); @@ -533,30 +305,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void change_startNotFound_fails() { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); dockerUtil.createContainer(CONTAINER_1); /* test */ @@ -569,30 +317,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void change_alreadyStopped_fails() throws InterruptedException { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); dockerUtil.createContainer(CONTAINER_1); dockerUtil.startContainer(CONTAINER_1); dockerUtil.stopContainer(CONTAINER_1); @@ -608,30 +332,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void change_stopNeverStarted_fails() { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); dockerUtil.createContainer(CONTAINER_1); containerRepository.save(CONTAINER_1); @@ -645,30 +345,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void change_stopSavedButNotFound_fails() { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); containerRepository.save(CONTAINER_1); /* test */ @@ -682,30 +358,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { ContainerNotRunningException { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); dockerUtil.createContainer(CONTAINER_1); dockerUtil.startContainer(CONTAINER_1); containerRepository.save(CONTAINER_1); @@ -731,30 +383,6 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void inspect_notRunning_fails() { /* mock */ - final ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .jdbcMethod(IMAGE_1_JDBC) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .compiled(IMAGE_1_BUILT) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENV) - .defaultPort(IMAGE_1_PORT) - .build(); - final Container CONTAINER_1 = Container.builder() - .id(CONTAINER_1_ID) - .name(CONTAINER_1_NAME) - .internalName(CONTAINER_1_INTERNALNAME) - .imageId(IMAGE_1_ID) - .image(IMAGE_1) - .hash(CONTAINER_1_HASH) - .ipAddress(CONTAINER_1_IP) - .created(CONTAINER_1_CREATED) - .build(); - log.info("Container id {}", CONTAINER_1.getId()); dockerUtil.createContainer(CONTAINER_1); containerRepository.save(CONTAINER_1); diff --git a/fda-container-service/services/src/main/java/at/tuwien/mapper/DatabaseMapper.java b/fda-container-service/services/src/main/java/at/tuwien/mapper/DatabaseMapper.java index 6a7b557780c2eae87d2f4e741358c9cd11272973..1879065a2df2056603679e9d8a505633a702178a 100644 --- a/fda-container-service/services/src/main/java/at/tuwien/mapper/DatabaseMapper.java +++ b/fda-container-service/services/src/main/java/at/tuwien/mapper/DatabaseMapper.java @@ -1,5 +1,6 @@ package at.tuwien.mapper; +import at.tuwien.api.database.DatabaseBriefDto; import at.tuwien.api.database.DatabaseDto; import at.tuwien.entities.database.Database; import org.mapstruct.Mapper; @@ -14,4 +15,10 @@ public interface DatabaseMapper { }) DatabaseDto databaseToDatabaseDto(Database data); + /* keep */ + @Mappings({ + @Mapping(target = "container", ignore = true) + }) + DatabaseBriefDto databaseToDatabaseBriefDto(Database data); + } diff --git a/fda-container-service/services/src/main/java/at/tuwien/repository/jpa/ContainerRepository.java b/fda-container-service/services/src/main/java/at/tuwien/repository/jpa/ContainerRepository.java index 37b8abb7735952160ae69ddb85c748441050a575..df2a6eba16e4bdbefa3a5de7eaff1dea507ed8ad 100644 --- a/fda-container-service/services/src/main/java/at/tuwien/repository/jpa/ContainerRepository.java +++ b/fda-container-service/services/src/main/java/at/tuwien/repository/jpa/ContainerRepository.java @@ -9,8 +9,6 @@ import java.util.Optional; @Repository public interface ContainerRepository extends JpaRepository<Container, Long> { - Optional<Container> findByHash(String hash); - Optional<Container> findByInternalName(String internalName); } diff --git a/fda-container-service/services/src/main/java/at/tuwien/repository/jpa/DatabaseRepository.java b/fda-container-service/services/src/main/java/at/tuwien/repository/jpa/DatabaseRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..688993ab3ca2e32edeb59b6c66dc838f58333b02 --- /dev/null +++ b/fda-container-service/services/src/main/java/at/tuwien/repository/jpa/DatabaseRepository.java @@ -0,0 +1,10 @@ +package at.tuwien.repository.jpa; + +import at.tuwien.entities.database.Database; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface DatabaseRepository extends JpaRepository<Database, Long> { + +} diff --git a/fda-container-service/services/src/main/java/at/tuwien/repository/jpa/IdentifierRepository.java b/fda-container-service/services/src/main/java/at/tuwien/repository/jpa/IdentifierRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..f76f9976d6de9884579ac242ce616d6c0caf099e --- /dev/null +++ b/fda-container-service/services/src/main/java/at/tuwien/repository/jpa/IdentifierRepository.java @@ -0,0 +1,10 @@ +package at.tuwien.repository.jpa; + +import at.tuwien.entities.identifier.Identifier; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface IdentifierRepository extends JpaRepository<Identifier, Long> { + +} diff --git a/fda-container-service/services/src/main/java/at/tuwien/service/ContainerService.java b/fda-container-service/services/src/main/java/at/tuwien/service/ContainerService.java index 2528621ed475406bb055f32457420bc116d158e2..26654fc34f51bb974b91036fadeb26eca12a1f55 100644 --- a/fda-container-service/services/src/main/java/at/tuwien/service/ContainerService.java +++ b/fda-container-service/services/src/main/java/at/tuwien/service/ContainerService.java @@ -56,9 +56,12 @@ public interface ContainerService { Container inspect(Long id) throws ContainerNotFoundException, DockerClientException, ContainerNotRunningException; /** - * @return + * Retrieve a list of all containers from the metadata database + * + * @param limit Return at most this amount of results, optional. + * @return The list of containers. */ - List<Container> getAll(); + List<Container> getAll(Integer limit); /** * @param containerId diff --git a/fda-container-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java b/fda-container-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java index 4dc699622912b6ac69c2ab2d2a71246be2ee4c62..9753d9687e7ec0113b00bde7cede4a4d3e41b1e6 100644 --- a/fda-container-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java +++ b/fda-container-service/services/src/main/java/at/tuwien/service/impl/ContainerServiceImpl.java @@ -22,6 +22,7 @@ import com.github.dockerjava.api.exception.NotModifiedException; import com.github.dockerjava.api.model.*; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.util.SocketUtils; @@ -31,6 +32,7 @@ import org.springframework.transaction.annotation.Transactional; import java.security.Principal; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Log4j2 @Service @@ -79,7 +81,7 @@ public class ContainerServiceImpl implements ContainerService { /* check duplicate */ final Optional<Container> optional = containerRepository.findByInternalName(container.getInternalName()); if (optional.isPresent()) { - log.error("Failed to create container with internal name {}", container.getInternalName()); + log.error("Failed to create container with internal name {}, it already exists", container.getInternalName()); throw new ContainerAlreadyExistsException("Container name already exists"); } /* create the volume */ @@ -212,9 +214,15 @@ public class ContainerServiceImpl implements ContainerService { } @Override - @Transactional - public List<Container> getAll() { - final List<Container> containers = containerRepository.findAll(Sort.by(Sort.Direction.DESC, "created")); + @Transactional(readOnly = true) + public List<Container> getAll(Integer limit) { + final List<Container> containers; + if (limit == null) { + containers = containerRepository.findAll(Sort.by(Sort.Direction.DESC, "created")); + } else { + containers = containerRepository.findAll(PageRequest.of(0, limit, Sort.by(Sort.Direction.DESC, "created"))) + .toList(); + } log.info("Found {} containers", containers.size()); return containers; } diff --git a/fda-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/fda-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java index 57820c3a21bde65e110d829e6b7413ba4678768d..bfeb98549b44ad1c38c2dbc84c399aa99f475cf6 100644 --- a/fda-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java +++ b/fda-database-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java @@ -3,12 +3,9 @@ package at.tuwien.endpoints; import at.tuwien.api.database.*; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; -import at.tuwien.entities.identifier.Identifier; -import at.tuwien.entities.identifier.IdentifierType; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.mapper.DatabaseMapper; -import at.tuwien.mapper.IdentifierMapper; import at.tuwien.repository.jpa.DatabaseAccessRepository; import at.tuwien.service.*; import at.tuwien.service.impl.MariaDbServiceImpl; @@ -27,7 +24,6 @@ import javax.validation.Valid; import javax.validation.constraints.NotNull; import java.security.Principal; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; @Log4j2 @@ -39,27 +35,22 @@ public class DatabaseEndpoint extends AbstractEndpoint { private final UserService userService; private final AccessService accessService; private final DatabaseMapper databaseMapper; - private final IdentifierMapper identifierMapper; private final MariaDbServiceImpl databaseService; private final QueryStoreService queryStoreService; - private final IdentifierService identifierService; private final MessageQueueService messageQueueService; private final DatabaseAccessRepository databaseAccessRepository; @Autowired public DatabaseEndpoint(DatabaseMapper databaseMapper, ContainerService containerService, UserService userService, MariaDbServiceImpl databaseService, QueryStoreService queryStoreService, - IdentifierService identifierService, IdentifierMapper identifierMapper, MessageQueueService messageQueueService, AccessService accessService, DatabaseAccessRepository databaseAccessRepository) { super(databaseService, containerService, databaseAccessRepository); this.userService = userService; this.accessService = accessService; this.databaseMapper = databaseMapper; - this.identifierMapper = identifierMapper; this.databaseService = databaseService; this.queryStoreService = queryStoreService; - this.identifierService = identifierService; this.messageQueueService = messageQueueService; this.databaseAccessRepository = databaseAccessRepository; } @@ -71,18 +62,10 @@ public class DatabaseEndpoint extends AbstractEndpoint { public ResponseEntity<List<DatabaseBriefDto>> list(@NotNull @PathVariable("id") Long containerId, @NotNull Principal principal) { log.debug("endpoint list databases, containerId={}, principal={}", containerId, principal); - final List<Identifier> identifiers = identifierService.findAll(containerId); final List<DatabaseBriefDto> databases = databaseService.findAll(containerId) .stream() .map(databaseMapper::databaseToDatabaseBriefDto) .collect(Collectors.toList()); - databases.forEach(db -> { - final Optional<Identifier> id = identifiers.stream() - .filter(i -> i.getContainerId().equals(containerId) && i.getDatabaseId().equals(containerId) && - i.getType().equals(IdentifierType.DATABASE)) - .findFirst(); - id.ifPresent(identifier -> db.setIdentifier(identifierMapper.identifierToIdentifierDto(identifier))); - }); log.trace("list databases resulted in databases {}", databases); return ResponseEntity.ok(databases); } @@ -175,12 +158,6 @@ public class DatabaseEndpoint extends AbstractEndpoint { log.debug("endpoint find database, containerId={}, databaseId={}", containerId, databaseId); final Database database = databaseService.findById(containerId, databaseId); final DatabaseDto dto = databaseMapper.databaseToDatabaseDto(database); - try { - final Identifier identifier = identifierService.find(containerId, databaseId, IdentifierType.DATABASE); - dto.setIdentifier(identifierMapper.identifierToIdentifierDto(identifier)); - } catch (IdentifierNotFoundException e) { - // ignore - } if (principal != null && database.getOwner().getUsername().equals(principal.getName())) { /* only owner sees the access rights */ final List<DatabaseAccess> accesses = accessService.list(databaseId); diff --git a/fda-database-service/rest-service/src/main/resources/init/querystore.sql b/fda-database-service/rest-service/src/main/resources/init/querystore.sql index c98ce27ecf601053d39820a82810958644e0254f..26fc6e54370ec268fa2cfa683f37c825d7cfa81b 100644 --- a/fda-database-service/rest-service/src/main/resources/init/querystore.sql +++ b/fda-database-service/rest-service/src/main/resources/init/querystore.sql @@ -1,5 +1,5 @@ -CREATE SEQUENCE `qs_queries_seq`; -CREATE TABLE `qs_queries`( `id` bigint not null primary key default nextval(`qs_queries_seq`), `created` datetime not null default now(), `executed` datetime not null default now(), `created_by` varchar(255) not null, `query` text not null, `query_normalized` text not null, `is_persisted` boolean not null, `query_hash` varchar(255) not null, `result_hash` varchar(255), `result_number` bigint); -CREATE PROCEDURE hash_table(IN name VARCHAR(255), OUT hash VARCHAR(255))BEGIN DECLARE _sql TEXT; SELECT CONCAT('SELECT SHA2(GROUP_CONCAT(CONCAT_WS(\'\',', GROUP_CONCAT(CONCAT('`', column_name, '`') ORDER BY column_name), ') SEPARATOR \',\'), 256) AS hash FROM `', name, '` INTO @hash;') FROM `information_schema`.`columns` WHERE `table_schema` = DATABASE() AND `table_name` = name INTO _sql; PREPARE stmt FROM _sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET hash = @hash;END -CREATE PROCEDURE store_query(IN query TEXT, IN executed DATETIME, OUT queryId BIGINT)BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _username varchar(255) DEFAULT REGEXP_REPLACE(current_user(), '@.*', ''); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash); SELECT COUNT(*) FROM _tmp INTO @count; IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END -CREATE DEFINER = 'root' PROCEDURE _store_query(IN _username VARCHAR(255), IN query TEXT, IN executed DATETIME, OUT queryId BIGINT)BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash); SELECT COUNT(*) FROM _tmp INTO @count; IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END \ No newline at end of file +CREATE SEQUENCE `qs_queries_seq`; +CREATE TABLE `qs_queries` ( `id` bigint not null primary key default nextval(`qs_queries_seq`), `created` datetime not null default now(), `executed` datetime not null default now(), `created_by` varchar(255) not null, `query` text not null, `query_normalized` text not null, `is_persisted` boolean not null, `query_hash` varchar(255) not null, `result_hash` varchar(255), `result_number` bigint ); +CREATE PROCEDURE hash_table(IN name VARCHAR(255), OUT hash VARCHAR(255), OUT count BIGINT) BEGIN DECLARE _sql TEXT; SELECT CONCAT('SELECT SHA2(GROUP_CONCAT(CONCAT_WS(\'\',', GROUP_CONCAT(CONCAT('`', column_name, '`') ORDER BY column_name), ') SEPARATOR \',\'), 256) AS hash, COUNT(*) AS count FROM `', name, '` INTO @hash, @count;') FROM `information_schema`.`columns` WHERE `table_schema` = DATABASE() AND `table_name` = name INTO _sql; PREPARE stmt FROM _sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET hash = @hash; SET count = @count; END; +CREATE PROCEDURE store_query(IN query TEXT, IN executed DATETIME, OUT queryId BIGINT) BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _username varchar(255) DEFAULT REGEXP_REPLACE(current_user(), '@.*', ''); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END; +CREATE DEFINER = 'root' PROCEDURE _store_query(IN _username VARCHAR(255), IN query TEXT, IN executed DATETIME, OUT queryId BIGINT) BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END; \ No newline at end of file diff --git a/fda-database-service/rest-service/src/main/resources/init/querystore_manual.sql b/fda-database-service/rest-service/src/main/resources/init/querystore_manual.sql index 2762d130a0044c439b41c0215b0d87924bc8f072..5580d0fd260b6ba4fddd24b411a6e74467d151d2 100644 --- a/fda-database-service/rest-service/src/main/resources/init/querystore_manual.sql +++ b/fda-database-service/rest-service/src/main/resources/init/querystore_manual.sql @@ -1,6 +1,5 @@ CREATE SEQUENCE `qs_queries_seq`; -CREATE TABLE `qs_queries` -( +CREATE TABLE `qs_queries` ( `id` bigint not null primary key default nextval(`qs_queries_seq`), `created` datetime not null default now(), `executed` datetime not null default now(), @@ -12,13 +11,14 @@ CREATE TABLE `qs_queries` `result_hash` varchar(255), `result_number` bigint ); + DELIMITER $$ -CREATE PROCEDURE hash_table(IN name VARCHAR(255), OUT hash VARCHAR(255)) +CREATE PROCEDURE hash_table(IN name VARCHAR(255), OUT hash VARCHAR(255), OUT count BIGINT) BEGIN DECLARE _sql TEXT; SELECT CONCAT('SELECT SHA2(GROUP_CONCAT(CONCAT_WS(\'\',', GROUP_CONCAT(CONCAT('`', column_name, '`') ORDER BY column_name), - ') SEPARATOR \',\'), 256) AS hash FROM `', name, '` INTO @hash;') + ') SEPARATOR \',\'), 256) AS hash, COUNT(*) AS count FROM `', name, '` INTO @hash, @count;') FROM `information_schema`.`columns` WHERE `table_schema` = DATABASE() AND `table_name` = name @@ -27,15 +27,16 @@ BEGIN EXECUTE stmt; DEALLOCATE PREPARE stmt; SET hash = @hash; -END $$ + SET count = @count; +END; $$ + DELIMITER $$ CREATE PROCEDURE store_query(IN query TEXT, IN executed DATETIME, OUT queryId BIGINT) BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _username varchar(255) DEFAULT REGEXP_REPLACE(current_user(), '@.*', ''); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); - PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash); - SELECT COUNT(*) FROM _tmp INTO @count; + PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) @@ -49,15 +50,15 @@ BEGIN WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; -END $$ +END; $$ + DELIMITER $$ CREATE DEFINER = 'root' PROCEDURE _store_query(IN _username VARCHAR(255), IN query TEXT, IN executed DATETIME, OUT queryId BIGINT) BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); - PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash); - SELECT COUNT(*) FROM _tmp INTO @count; + PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) @@ -71,5 +72,6 @@ BEGIN WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; -END $$ +END; $$ + DELIMITER ; \ No newline at end of file diff --git a/fda-database-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java b/fda-database-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java index 7a1890e25df228c438451cca2ce8efee0a589d53..e32140f007c003e33e6a48b9224080ea216d3d58 100644 --- a/fda-database-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java +++ b/fda-database-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java @@ -50,7 +50,7 @@ public abstract class BaseUnitTest { .build(); public final static String GATEWAY_NAME = "fda-gateway-service"; - public final static String GATEWAY_IP = "172.29.0.3"; + public final static String GATEWAY_IP = "172.31.0.3"; public final static String GATEWAY_HOSTNAME = "gateway-service"; public final static Integer GATEWAY_PORT = 9095; public final static String GATEWAY_IMAGE = "nginx"; @@ -58,7 +58,7 @@ public abstract class BaseUnitTest { public final static String CONTAINER_SEARCH_NAME = "search-mock-service"; public final static String CONTAINER_SEARCH_INTERNAL_NAME = "search-mock-service"; - public final static String CONTAINER_SEARCH_IP = "172.29.0.3"; + public final static String CONTAINER_SEARCH_IP = "172.31.0.3"; public final static String CONTAINER_SEARCH_REPOSITORY = "elasticsearch"; public final static String CONTAINER_SEARCH_TAG = "7.13.4"; @@ -330,9 +330,9 @@ public abstract class BaseUnitTest { .build(); public final static Long CONTAINER_BROKER_ID = 5L; - public final static String CONTAINER_BROKER_NAME = "broker-service"; - public final static String CONTAINER_BROKER_INTERNAL_NAME = "broker-service"; - public final static String CONTAINER_BROKER_IP = "172.29.0.2"; + public final static String CONTAINER_BROKER_NAME = "dbrepo-broker-service"; + public final static String CONTAINER_BROKER_INTERNAL_NAME = "dbrepo-broker-service"; + public final static String CONTAINER_BROKER_IP = "172.31.0.2"; public final static String CONTAINER_BROKER_HASH = "deadbeef"; public final static Instant CONTAINER_BROKER_CREATED = Instant.now().minus(1, HOURS); public final static HealthCheck CONTAINER_BROKER_HEALTHCHECK = new HealthCheck() @@ -366,9 +366,9 @@ public abstract class BaseUnitTest { public final static Long CONTAINER_1_ID = 1L; public final static String CONTAINER_1_HASH = "deadbeef"; - public final static String CONTAINER_1_IP = "172.28.0.5"; - public final static String CONTAINER_1_NAME = "fda-userdb-u01"; - public final static String CONTAINER_1_INTERNALNAME = "fda-userdb-u01"; + public final static String CONTAINER_1_IP = "172.30.0.5"; + public final static String CONTAINER_1_NAME = "u01"; + public final static String CONTAINER_1_INTERNALNAME = "dbrepo-userdb-u01"; public final static Instant CONTAINER_1_CREATED = Instant.now().minus(2, HOURS); public final static Instant CONTAINER_1_UPDATED = Instant.now(); public final static HealthCheck CONTAINER_1_HEALTHCHECK = new HealthCheck() @@ -393,9 +393,9 @@ public abstract class BaseUnitTest { public final static Long CONTAINER_2_ID = 2L; public final static String CONTAINER_2_HASH = "deadbeef"; - public final static String CONTAINER_2_IP = "172.28.0.6"; - public final static String CONTAINER_2_NAME = "fda-userdb-u02"; - public final static String CONTAINER_2_INTERNALNAME = "fda-userdb-u02"; + public final static String CONTAINER_2_IP = "172.30.0.6"; + public final static String CONTAINER_2_NAME = "u02"; + public final static String CONTAINER_2_INTERNALNAME = "dbrepo-userdb-u02"; public final static Instant CONTAINER_2_CREATED = Instant.now().minus(2, HOURS); public final static Instant CONTAINER_2_UPDATED = Instant.now(); public final static HealthCheck CONTAINER_2_HEALTHCHECK = new HealthCheck() @@ -420,9 +420,9 @@ public abstract class BaseUnitTest { public final static Long CONTAINER_3_ID = 3L; public final static String CONTAINER_3_HASH = "deadbeef"; - public final static String CONTAINER_3_IP = "172.28.0.7"; - public final static String CONTAINER_3_NAME = "fda-userdb-u03"; - public final static String CONTAINER_3_INTERNALNAME = "fda-userdb-u03"; + public final static String CONTAINER_3_IP = "172.30.0.7"; + public final static String CONTAINER_3_NAME = "u03"; + public final static String CONTAINER_3_INTERNALNAME = "dbrepo-userdb-u03"; public final static Instant CONTAINER_3_CREATED = Instant.now().minus(2, HOURS); public final static Instant CONTAINER_3_UPDATED = Instant.now(); public final static HealthCheck CONTAINER_3_HEALTHCHECK = new HealthCheck() @@ -447,9 +447,9 @@ public abstract class BaseUnitTest { public final static Long CONTAINER_4_ID = 4L; public final static String CONTAINER_4_HASH = "deadbeef"; - public final static String CONTAINER_4_IP = "172.28.0.8"; - public final static String CONTAINER_4_NAME = "fda-userdb-u04"; - public final static String CONTAINER_4_INTERNALNAME = "fda-userdb-u04"; + public final static String CONTAINER_4_IP = "172.30.0.8"; + public final static String CONTAINER_4_NAME = "u04"; + public final static String CONTAINER_4_INTERNALNAME = "dbrepo-userdb-u04"; public final static Instant CONTAINER_4_CREATED = Instant.now().minus(2, HOURS); public final static Instant CONTAINER_4_UPDATED = Instant.now(); public final static HealthCheck CONTAINER_4_HEALTHCHECK = new HealthCheck() @@ -473,7 +473,7 @@ public abstract class BaseUnitTest { public final static Long CONTAINER_ELASTIC_ID = 5L; public final static String CONTAINER_ELASTIC_NAME = "fda-elastic-service"; public final static String CONTAINER_ELASTIC_INTERNAL_NAME = "search-mock-service"; - public final static String CONTAINER_ELASTIC_IP = "172.29.0.3"; + public final static String CONTAINER_ELASTIC_IP = "172.31.0.3"; public final static String CONTAINER_ELASTIC_HASH = "deadbeef"; public final static Instant CONTAINER_ELASTIC_CREATED = Instant.now().minus(1, HOURS); diff --git a/fda-database-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java b/fda-database-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java index 5340810c86ad9dd7b7cd93a7e7d874cdb31d2c2e..7c55429c92d2f748d706cc990843b0f86883929e 100644 --- a/fda-database-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java +++ b/fda-database-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java @@ -157,14 +157,14 @@ public class DockerConfig extends BaseUnitTest { .withName("fda-userdb") .withIpam(new Network.Ipam() .withConfig(new Network.Ipam.Config() - .withSubnet("172.28.0.0/16"))) + .withSubnet("172.30.0.0/16"))) .withEnableIpv6(false) .exec(); dockerClient.createNetworkCmd() .withName("fda-public") .withIpam(new Network.Ipam() .withConfig(new Network.Ipam.Config() - .withSubnet("172.29.0.0/16"))) + .withSubnet("172.31.0.0/16"))) .withEnableIpv6(false) .exec(); } diff --git a/fda-database-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java b/fda-database-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java index 4808082aff0dbaadbd9cf296306c3be6a3b62759..61cbf30d0255572bc1ca4d62ec5df76672aa478c 100644 --- a/fda-database-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java +++ b/fda-database-service/rest-service/src/test/java/at/tuwien/endpoint/DatabaseEndpointUnitTest.java @@ -143,49 +143,67 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @Test public void list_anonymousPublic_succeeds() { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* test */ - list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_1), null); + list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_3), null); } @Test @WithAnonymousUser public void list_anonymous2Public_succeeds() { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* test */ - list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_1), null); + list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_3), null); } @Test public void list_anonymousPrivate_succeeds() { + /* pre-condition */ + assertFalse(DATABASE_1_PUBLIC); + /* test */ - list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_2), null); + list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_1), null); } @Test @WithAnonymousUser public void list_anonymous2Private_succeeds() { + /* pre-condition */ + assertFalse(DATABASE_1_PUBLIC); + /* test */ - list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_2), null); + list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_1), null); } @Test @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) public void list_researcherPublic_succeeds() { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_1_USERNAME)) .thenReturn(Optional.of(USER_1)); /* test */ - list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_1), USER_1_PRINCIPAL); + list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_3), USER_1_PRINCIPAL); } @Test @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) public void list_researcherPublicWithIdentifiers_succeeds() { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_1_USERNAME)) .thenReturn(Optional.of(USER_1)); @@ -193,13 +211,16 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { .thenReturn(List.of(IDENTIFIER_1)); /* test */ - list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_1), USER_1_PRINCIPAL); + list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_3), USER_1_PRINCIPAL); } @Test @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) public void list_researcherPrivate_succeeds() { + /* pre-condition */ + assertFalse(DATABASE_2_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_1_USERNAME)) .thenReturn(Optional.of(USER_1)); @@ -212,18 +233,24 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) public void list_researcherPublicForeignContainer_succeeds() { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_1_USERNAME)) .thenReturn(Optional.of(USER_1)); /* test */ - list_generic(CONTAINER_2_ID, CONTAINER_2, List.of(DATABASE_1), USER_1_PRINCIPAL); + list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_3), USER_1_PRINCIPAL); } @Test @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) public void list_researcherPrivateForeignContainer_succeeds() { + /* pre-condition */ + assertFalse(DATABASE_2_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_1_USERNAME)) .thenReturn(Optional.of(USER_1)); @@ -236,18 +263,24 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) public void list_developerPublic_succeeds() { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_2_USERNAME)) .thenReturn(Optional.of(USER_2)); /* test */ - list_generic(CONTAINER_2_ID, CONTAINER_2, List.of(DATABASE_1), USER_2_PRINCIPAL); + list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_3), USER_2_PRINCIPAL); } @Test @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) public void list_developerPrivate_succeeds() { + /* pre-condition */ + assertFalse(DATABASE_2_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_2_USERNAME)) .thenReturn(Optional.of(USER_2)); @@ -260,66 +293,84 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) public void list_developerPublicForeignContainer_succeeds() { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_2_USERNAME)) .thenReturn(Optional.of(USER_2)); /* test */ - list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_1), USER_2_PRINCIPAL); + list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_3), USER_2_PRINCIPAL); } @Test @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) public void list_developerPrivateForeignContainer_succeeds() { + /* pre-condition */ + assertFalse(DATABASE_1_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_2_USERNAME)) .thenReturn(Optional.of(USER_2)); /* test */ - list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_2), USER_2_PRINCIPAL); + list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_1), USER_2_PRINCIPAL); } @Test @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) public void list_dataStewardPublic_succeeds() { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_3_USERNAME)) .thenReturn(Optional.of(USER_3)); /* test */ - list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_1), USER_3_PRINCIPAL); + list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_3), USER_3_PRINCIPAL); } @Test @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) public void list_dataStewardPrivate_succeeds() { + /* pre-condition */ + assertFalse(DATABASE_1_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_3_USERNAME)) .thenReturn(Optional.of(USER_3)); /* test */ - list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_2), USER_3_PRINCIPAL); + list_generic(CONTAINER_1_ID, CONTAINER_1, List.of(DATABASE_1), USER_3_PRINCIPAL); } @Test @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) public void list_dataStewardPublicForeignContainer_succeeds() { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_3_USERNAME)) .thenReturn(Optional.of(USER_3)); /* test */ - list_generic(CONTAINER_2_ID, CONTAINER_2, List.of(DATABASE_1), USER_3_PRINCIPAL); + list_generic(CONTAINER_3_ID, CONTAINER_3, List.of(DATABASE_3), USER_3_PRINCIPAL); } @Test @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) public void list_dataStewardPrivateForeignContainer_succeeds() { + /* pre-condition */ + assertFalse(DATABASE_2_PUBLIC); + /* mock */ when(userRepository.findByUsername(USER_3_USERNAME)) .thenReturn(Optional.of(USER_3)); @@ -497,8 +548,11 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) public void findById_researcherPublic_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* test */ - findById_generic(CONTAINER_1_ID, CONTAINER_1, DATABASE_1_ID, DATABASE_1, USER_1_PRINCIPAL); + findById_generic(CONTAINER_3_ID, CONTAINER_3, DATABASE_3_ID, DATABASE_3, USER_1_PRINCIPAL); } @Test @@ -506,6 +560,9 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { public void findById_researcherPublicForeignDatabase_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* test */ findById_generic(CONTAINER_3_ID, CONTAINER_3, DATABASE_3_ID, DATABASE_3, USER_1_PRINCIPAL); } @@ -514,6 +571,9 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) public void findById_researcherPrivate_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + /* pre-condition */ + assertFalse(DATABASE_2_PUBLIC); + /* test */ findById_generic(CONTAINER_2_ID, CONTAINER_2, DATABASE_2_ID, DATABASE_2, USER_1_PRINCIPAL); } @@ -523,6 +583,9 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { public void findById_researcherPrivateForeignDatabase_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + /* pre-condition */ + assertFalse(DATABASE_2_PUBLIC); + /* test */ findById_generic(CONTAINER_2_ID, CONTAINER_2, DATABASE_2_ID, DATABASE_2, USER_1_PRINCIPAL); } @@ -532,6 +595,9 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { public void findById_developerPublicForeignDatabase_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* test */ findById_generic(CONTAINER_3_ID, CONTAINER_3, DATABASE_3_ID, DATABASE_3, USER_2_PRINCIPAL); } @@ -540,6 +606,9 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) public void findById_developerPrivate_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + /* pre-condition */ + assertFalse(DATABASE_2_PUBLIC); + /* test */ findById_generic(CONTAINER_2_ID, CONTAINER_2, DATABASE_2_ID, DATABASE_2, USER_2_PRINCIPAL); } @@ -548,6 +617,9 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) public void findById_dataStewardPublic_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* test */ findById_generic(CONTAINER_3_ID, CONTAINER_3, DATABASE_3_ID, DATABASE_3, USER_3_PRINCIPAL); } @@ -557,8 +629,11 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { public void findById_dataStewardPublicForeignDatabase_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + /* pre-condition */ + assertTrue(DATABASE_3_PUBLIC); + /* test */ - findById_generic(CONTAINER_1_ID, CONTAINER_1, DATABASE_1_ID, DATABASE_1, USER_3_PRINCIPAL); + findById_generic(CONTAINER_3_ID, CONTAINER_3, DATABASE_3_ID, DATABASE_3, USER_3_PRINCIPAL); } @Test @@ -566,6 +641,9 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { public void findById_dataStewardPrivateForeignDatabase_succeeds() throws AccessDeniedException, DatabaseNotFoundException { + /* pre-condition */ + assertFalse(DATABASE_2_PUBLIC); + /* test */ findById_generic(CONTAINER_2_ID, CONTAINER_2, DATABASE_2_ID, DATABASE_2, USER_3_PRINCIPAL); } diff --git a/fda-database-service/rest-service/src/test/resources/application.properties b/fda-database-service/rest-service/src/test/resources/application.properties index 33b8c25e25a0fe7db1a47bc8349d3d31e834e179..ddfddd8b33f6b4807c1047703d873571b4f89c24 100644 --- a/fda-database-service/rest-service/src/test/resources/application.properties +++ b/fda-database-service/rest-service/src/test/resources/application.properties @@ -22,8 +22,8 @@ logging.level.root=error logging.level.at.tuwien.=info # rabbitmq -fda.gateway.endpoint=http://broker-service:15672 -spring.rabbitmq.host=broker-service +fda.gateway.endpoint=http://dbrepo-broker-service:15672 +spring.rabbitmq.host=dbrepo-broker-service # elastic fda.elastic.endpoint=search-mock-service:9200 \ No newline at end of file diff --git a/fda-database-service/rest-service/src/test/resources/nginx/nginx.conf b/fda-database-service/rest-service/src/test/resources/nginx/nginx.conf index f4ada65dc83ba0cb149a1136ef36bbaf87fff513..6604865024d8e32d66f23a8f49fd0bca5e2c3e70 100644 --- a/fda-database-service/rest-service/src/test/resources/nginx/nginx.conf +++ b/fda-database-service/rest-service/src/test/resources/nginx/nginx.conf @@ -39,7 +39,7 @@ http { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://broker-service:15672; + proxy_pass http://dbrepo-broker-service:15672; proxy_read_timeout 90; } } diff --git a/fda-database-service/services/src/main/java/at/tuwien/mapper/DatabaseMapper.java b/fda-database-service/services/src/main/java/at/tuwien/mapper/DatabaseMapper.java index 26aca837e2aa6721fbf29c42bd0cdb6336bd11db..f986f7577245bb0161e9d5d046da92fd22e40012 100644 --- a/fda-database-service/services/src/main/java/at/tuwien/mapper/DatabaseMapper.java +++ b/fda-database-service/services/src/main/java/at/tuwien/mapper/DatabaseMapper.java @@ -85,6 +85,7 @@ public interface DatabaseMapper { @Mapping(target = "id", source = "id"), @Mapping(target = "engine", source = "container.image", qualifiedByName = "engineMapping"), @Mapping(target = "created", source = "created", dateFormat = "dd-MM-yyyy HH:mm"), + @Mapping(target = "container", ignore = true), }) DatabaseBriefDto databaseToDatabaseBriefDto(Database data); diff --git a/fda-discovery-service/rest-service/ready b/fda-discovery-service/rest-service/ready deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java index c8a6ff55aa0d1bd132e4980faf983eed383fa230..e48292d75310ad48545ef11501778970e166fe6e 100644 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/container/ContainerBriefDto.java @@ -1,5 +1,6 @@ package at.tuwien.api.container; +import at.tuwien.api.database.DatabaseBriefDto; import at.tuwien.api.user.UserBriefDto; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; @@ -36,6 +37,9 @@ public class ContainerBriefDto { @Schema(example = "air-quality") private String internalName; + @org.springframework.data.annotation.Transient + private DatabaseBriefDto database; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") private Instant created; } diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java index 699889b1c14ae5b0dea7b269859a75f7d5ec0b41..d91f7ce74dfda4a4ef483c774ab25f3b8f7e763e 100644 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java @@ -1,6 +1,7 @@ package at.tuwien.api.database; import at.tuwien.api.container.ContainerBriefDto; +import at.tuwien.api.identifier.IdentifierBriefDto; import at.tuwien.api.identifier.IdentifierDto; import at.tuwien.api.user.UserBriefDto; import com.fasterxml.jackson.annotation.JsonFormat; @@ -30,7 +31,7 @@ public class DatabaseBriefDto { @Schema(example = "Air Quality in Austria") private String description; - private IdentifierDto identifier; + private IdentifierBriefDto identifier; @JsonProperty("is_public") @Schema(example = "true") @@ -39,11 +40,10 @@ public class DatabaseBriefDto { @Schema(example = "mariadb:10.5") private String engine; + @ToString.Exclude @org.springframework.data.annotation.Transient private ContainerBriefDto container; - private UserBriefDto creator; - @Schema(example = "2020-08-04 11:12:00") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") private Instant created; diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/identifier/CreatorBriefDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/identifier/CreatorBriefDto.java new file mode 100644 index 0000000000000000000000000000000000000000..ad0910407b22e7c89f9e9f10a18e622a475e6cf7 --- /dev/null +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/identifier/CreatorBriefDto.java @@ -0,0 +1,28 @@ + +package at.tuwien.api.identifier; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +@Data +@Getter +@Setter +@Builder +public class CreatorBriefDto { + + @NotBlank + @Schema(example = "Josiah") + private String firstname; + + @NotBlank + @Schema(example = "Carberry") + private String lastname; + + @Schema(example = "Wesleyan University") + private String affiliation; + +} diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java index a3e816be75a185466c64e6c91b86a5318e072771..c17feec05c19de48c19767e6cf083a8a7a8d01d8 100644 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierBriefDto.java @@ -12,6 +12,7 @@ import lombok.Setter; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.time.Instant; +import java.util.List; @Data @Getter @@ -43,6 +44,24 @@ public class IdentifierBriefDto { @NotNull private IdentifierTypeDto type; + @NotNull + @Schema(example = "everyone") + private VisibilityTypeDto visibility; + + @Schema(example = "10.1038/nphys1170") + private String doi; + + @Schema(example = "TU Wien") + private String publisher; + + @NotNull + @JsonProperty("publication_year") + @Schema(example = "2022") + private Integer publicationYear; + + @NotNull + private List<CreatorBriefDto> creators; + @JsonIgnore @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") private Instant created; diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/Database.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/Database.java index 90a49b81b8ff9b4d7606ccab40af547aa8850e55..a3233d71357167b90055506402e4d873f59a3c5c 100644 --- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/Database.java +++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/Database.java @@ -5,16 +5,15 @@ import at.tuwien.entities.database.table.Table; import at.tuwien.entities.identifier.Identifier; import at.tuwien.entities.user.User; import lombok.*; -import net.sf.jsqlparser.statement.select.FromItem; -import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.SQLDelete; -import org.hibernate.annotations.Where; +import org.hibernate.annotations.*; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.*; +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import java.io.Serializable; import java.time.Instant; import java.util.List; @@ -28,7 +27,7 @@ import java.util.List; @javax.persistence.Table(name = "mdb_databases", uniqueConstraints = { @UniqueConstraint(columnNames = {"id", "internalName"}) }) -public class Database { +public class Database implements Serializable { @Id @EqualsAndHashCode.Include @@ -73,6 +72,13 @@ public class Database { }) private User contact; + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumnsOrFormulas({ + @JoinColumnOrFormula(formula = @JoinFormula(value = "'DATABASE'", referencedColumnName = "identifier_type")), + @JoinColumnOrFormula(column = @JoinColumn(name = "id", referencedColumnName = "dbid", insertable = false, updatable = false)), + }) + private Identifier identifier; + @ToString.Exclude @org.springframework.data.annotation.Transient @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/View.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/View.java index f75494670fbd8f6c1e3b9dced505028d1b9c2507..09e85f03b7267363bfdda8411642351703cdc61a 100644 --- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/View.java +++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/database/View.java @@ -1,5 +1,6 @@ package at.tuwien.entities.database; +import at.tuwien.entities.database.table.columns.TableColumn; import at.tuwien.entities.user.User; import lombok.*; import net.sf.jsqlparser.statement.select.FromItem; @@ -11,6 +12,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.*; import java.time.Instant; +import java.util.List; @Data @Entity @@ -82,6 +84,21 @@ public class View { return this.getInternalName().equals(name); } + @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) + @JoinTable(name = "mdb_view_columns", + joinColumns = { + @JoinColumn(name = "vid", referencedColumnName = "id", insertable = false, updatable = false), + @JoinColumn(name = "vcid", referencedColumnName = "vcid", insertable = false, updatable = false), + @JoinColumn(name = "vdbid", referencedColumnName = "vdbid", insertable = false, updatable = false) + }, + inverseJoinColumns = { + @JoinColumn(name = "cid", referencedColumnName = "id", insertable = false, updatable = false), + @JoinColumn(name = "ctid", referencedColumnName = "tid", insertable = false, updatable = false), + @JoinColumn(name = "cdbid", referencedColumnName = "cdbid", insertable = false, updatable = false) + }) + @OrderColumn(name = "position") + private List<TableColumn> columns; + @Column(nullable = false, updatable = false) @CreatedDate private Instant created; diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java index a6385bb9088b4e703a479f229213141401839b76..95ffd29e5907c9585f1f64402e396dc5d24065d1 100644 --- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java +++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java @@ -3,19 +3,16 @@ package at.tuwien.entities.identifier; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.LanguageType; import at.tuwien.entities.database.License; -import at.tuwien.entities.user.Token; import at.tuwien.entities.user.User; import lombok.*; import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.SQLDelete; -import org.hibernate.annotations.Where; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.*; import javax.validation.constraints.NotBlank; +import java.io.Serializable; import java.time.Instant; import java.util.List; @@ -29,7 +26,7 @@ import java.util.List; @javax.persistence.Table(name = "mdb_identifiers", uniqueConstraints = { @UniqueConstraint(columnNames = {"qid", "cid", "dbid"}) }) -public class Identifier { +public class Identifier implements Serializable { @Id @EqualsAndHashCode.Include diff --git a/fda-metadata-db/querystore/src/main/java/at/tuwien/querystore/Query.java b/fda-metadata-db/querystore/src/main/java/at/tuwien/querystore/Query.java index 8df1ced8691c05bb429a5aa56c0d2553c5b843af..f589913ec36b76fdc99fe90dff41b3ecae5d060f 100644 --- a/fda-metadata-db/querystore/src/main/java/at/tuwien/querystore/Query.java +++ b/fda-metadata-db/querystore/src/main/java/at/tuwien/querystore/Query.java @@ -62,6 +62,9 @@ public class Query implements Serializable { @CreatedDate private Instant created; + @javax.persistence.Column(nullable = false, updatable = false) + private Instant executed; + @javax.persistence.Column(nullable = false) private String createdBy; diff --git a/fda-metadata-db/setup-schema.sql b/fda-metadata-db/setup-schema.sql index 9503fa90bf5417dc186c9eb07c63a3acf1735554..ff4afcc63083db7e0b0de80b0fe4cfc69b88c8da 100644 --- a/fda-metadata-db/setup-schema.sql +++ b/fda-metadata-db/setup-schema.sql @@ -402,7 +402,22 @@ CREATE TABLE IF NOT EXISTS mdb_view created_by bigint NOT NULL, FOREIGN KEY (created_by) REFERENCES mdb_users (UserID), FOREIGN KEY (vdbid) REFERENCES mdb_databases (id), - PRIMARY KEY (id, vdbid) + PRIMARY KEY (id, vcid, vdbid) +) WITH SYSTEM VERSIONING; + +CREATE TABLE mdb_view_columns +( + id BIGINT NOT NULL AUTO_INCREMENT, + cid BIGINT NOT NULL, + ctid BIGINT NOT NULL, + cdbid BIGINT NOT NULL, + vid BIGINT NOT NULL, + vcid BIGINT NOT NULL, + vdbid BIGINT NOT NULL, + position INTEGER NULL, + PRIMARY KEY (id), + FOREIGN KEY (vid, vcid, vdbid) REFERENCES mdb_view (id, vcid, vdbid), + FOREIGN KEY (cid, cdbid, ctid) REFERENCES mdb_columns (ID, cDBID, tID) ) WITH SYSTEM VERSIONING; CREATE TABLE IF NOT EXISTS mdb_identifiers diff --git a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ExportEndpoint.java b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ExportEndpoint.java index 263c3ff8629a4d1d1bd191ee57e49632f9baa427..33a9c761a6ebd7d6ffd065362c5db011faf9483f 100644 --- a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ExportEndpoint.java +++ b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ExportEndpoint.java @@ -53,7 +53,7 @@ public class ExportEndpoint extends AbstractEndpoint { throw new NotAllowedException("Missing data export permission"); } final HttpHeaders headers = new HttpHeaders(); - final ExportResource resource = queryService.findAll(containerId, databaseId, tableId, timestamp, principal); + final ExportResource resource = queryService.tableFindAll(containerId, databaseId, tableId, timestamp, principal); headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\""); log.trace("export table resulted in resource {}", resource); return ResponseEntity.ok() diff --git a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/QueryEndpoint.java b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/QueryEndpoint.java index 05db9d96edbec6859fe89bd718f38e4bf5dca9da..6f4faa2b4197be398b573ea68f76eddc8c42ab8b 100644 --- a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/QueryEndpoint.java +++ b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/QueryEndpoint.java @@ -108,6 +108,32 @@ public class QueryEndpoint extends AbstractEndpoint { .body(result); } + @GetMapping("/{queryId}/data/count") + @Transactional(readOnly = true) + @Timed(value = "query.reexecute.count", description = "Time needed to re-execute a query") + @Operation(summary = "Re-execute some query", security = @SecurityRequirement(name = "bearerAuth")) + public ResponseEntity<Long> reExecuteCount(@NotNull @PathVariable("id") Long containerId, + @NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @PathVariable("queryId") Long queryId, + Principal principal) + throws QueryStoreException, QueryNotFoundException, DatabaseNotFoundException, ImageNotSupportedException, + QueryMalformedException, TableMalformedException, ColumnParseException, NotAllowedException, + DatabaseConnectionException, UserNotFoundException { + log.debug("endpoint re-execute query count, containerId={}, databaseId={}, queryId={}, principal={}", + containerId, databaseId, queryId, principal); + /* check */ + if (!hasQueryPermission(containerId, databaseId, queryId, "QUERY_RE_EXECUTE", principal)) { + log.error("Missing re-execute query permission"); + throw new NotAllowedException("Missing re-execute query permission"); + } + /* execute */ + final Query query = storeService.findOne(containerId, databaseId, queryId, principal); + final Long result = queryService.reExecuteCount(containerId, databaseId, query, principal); + log.trace("re-execute query count resulted in result {}", result); + return ResponseEntity.status(HttpStatus.ACCEPTED) + .body(result); + } + @GetMapping("/{queryId}/export") @Transactional(readOnly = true) @Timed(value = "query.export", description = "Time needed to export query data") diff --git a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java index a1d1e8648268418e1c0efa75efa2faa8379106a5..277e622b6273f66827b116972b8498e30b20dc43 100644 --- a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java +++ b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/TableDataEndpoint.java @@ -14,7 +14,6 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -148,7 +147,7 @@ public class TableDataEndpoint extends AbstractEndpoint { @RequestParam(required = false) String sortColumn) throws TableNotFoundException, DatabaseNotFoundException, DatabaseConnectionException, ImageNotSupportedException, TableMalformedException, PaginationException, ContainerNotFoundException, - QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { + NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { log.debug("endpoint find table data, containerId={}, databaseId={}, tableId={}, principal={}, timestamp={}, page={}, size={}, sortDirection={}, sortColumn={}", containerId, databaseId, tableId, principal, timestamp, page, size, sortDirection, sortColumn); /* check */ @@ -157,17 +156,35 @@ public class TableDataEndpoint extends AbstractEndpoint { throw new NotAllowedException("Missing data view permission"); } validateDataParams(page, size, sortDirection, sortColumn); - /* find */ - final Long count = queryService.count(containerId, databaseId, tableId, timestamp, principal); - log.debug("find table data has produced {} tuples", count); - final HttpHeaders headers = new HttpHeaders(); - headers.set("FDA-COUNT", count.toString()); - final QueryResultDto response = queryService.findAll(containerId, databaseId, tableId, timestamp, page, size, principal); + final QueryResultDto response = queryService.tableFindAll(containerId, databaseId, tableId, timestamp, page, size, principal); log.trace("find table data resulted in result {}", response); return ResponseEntity.ok() - .headers(headers) .body(response); } + @GetMapping("/count") + @Timed(value = "data.all.count", description = "Time needed to get count of all data from a table") + @Operation(summary = "Find data", security = @SecurityRequirement(name = "bearerAuth")) + public ResponseEntity<Long> getCount(@NotNull @PathVariable("id") Long containerId, + @NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @PathVariable("tableId") Long tableId, + @NotNull Principal principal, + @RequestParam(required = false) Instant timestamp) + throws TableNotFoundException, DatabaseNotFoundException, DatabaseConnectionException, + ImageNotSupportedException, TableMalformedException, PaginationException, ContainerNotFoundException, + QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { + log.debug("endpoint find table data, containerId={}, databaseId={}, tableId={}, principal={}, timestamp={}", + containerId, databaseId, tableId, principal, timestamp); + /* check */ + if (!hasTablePermission(containerId, databaseId, tableId, "DATA_VIEW", principal)) { + log.error("Missing data view permission"); + throw new NotAllowedException("Missing data view permission"); + } + /* find */ + final Long count = queryService.tableCount(containerId, databaseId, tableId, timestamp, principal); + log.debug("table data count is {} tuples", count); + return ResponseEntity.ok() + .body(count); + } } diff --git a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ViewEndpoint.java b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ViewEndpoint.java index 244280cac6a6a35723526a9aef867d187e1d9a7c..b5b2bf6954025a38f46c79de4b489067324b0945 100644 --- a/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ViewEndpoint.java +++ b/fda-query-service/rest-service/src/main/java/at/tuwien/endpoint/ViewEndpoint.java @@ -92,7 +92,8 @@ public class ViewEndpoint extends AbstractEndpoint { } final Database database = databaseService.find(containerId, databaseId); log.trace("create view for database {}", database); - final View view = viewService.create(containerId, databaseId, data, principal); + final View view; + view = viewService.create(containerId, databaseId, data, principal); final ViewBriefDto dto = viewMapper.viewToViewBriefDto(view); log.trace("create view resulted in view {}", dto); return ResponseEntity.status(HttpStatus.CREATED) @@ -154,7 +155,7 @@ public class ViewEndpoint extends AbstractEndpoint { @RequestParam(required = false) Long size) throws DatabaseNotFoundException, NotAllowedException, ViewNotFoundException, PaginationException, QueryStoreException, DatabaseConnectionException, TableMalformedException, QueryMalformedException, - ImageNotSupportedException, ColumnParseException, UserNotFoundException, ContainerNotFoundException { + ImageNotSupportedException, ColumnParseException, UserNotFoundException, ContainerNotFoundException, ViewMalformedException { log.debug("endpoint find view data, containerId={}, databaseId={}, viewId={}, principal={}, page={}, size={}", containerId, databaseId, viewId, principal, page, size); /* check */ @@ -167,12 +168,37 @@ public class ViewEndpoint extends AbstractEndpoint { final Database database = databaseService.find(containerId, databaseId); log.trace("find view data for database {}", database); final View view = viewService.findById(databaseId, viewId, principal); - final ExecuteStatementDto statement = ExecuteStatementDto.builder() - .statement(view.getQuery()) - .build(); - log.trace("find view execute statement {}", statement); - final QueryResultDto result = queryService.execute(containerId, databaseId, statement, principal, page, size, - null, null); + final QueryResultDto result = queryService.viewFindAll(containerId, databaseId, view, page, size, principal); + log.trace("execute view {}", view); + log.trace("find view data resulted in result {}", result); + return ResponseEntity.ok() + .body(result); + } + + @GetMapping("/{viewId}/data/count") + @Transactional(readOnly = true) + @Timed(value = "view.data.count", description = "Time needed to retrieve data count from a view") + @Operation(summary = "Find view data count", security = @SecurityRequirement(name = "bearerAuth")) + public ResponseEntity<Long> count(@NotNull @PathVariable("id") Long containerId, + @NotNull @PathVariable("databaseId") Long databaseId, + @NotNull @PathVariable("viewId") Long viewId, + Principal principal) + throws DatabaseNotFoundException, NotAllowedException, ViewNotFoundException, PaginationException, + QueryStoreException, DatabaseConnectionException, TableMalformedException, QueryMalformedException, + ImageNotSupportedException, ColumnParseException, UserNotFoundException, ContainerNotFoundException, ViewMalformedException { + log.debug("endpoint find view data count, containerId={}, databaseId={}, viewId={}, principal={}", + containerId, databaseId, viewId, principal); + /* check */ + if (!hasDatabasePermission(containerId, databaseId, "DATA_VIEW", principal)) { + log.error("Missing view data in view permission"); + throw new NotAllowedException("Missing view data in view permission"); + } + /* find */ + final Database database = databaseService.find(containerId, databaseId); + log.trace("find view data for database {}", database); + final View view = viewService.findById(databaseId, viewId, principal); + final Long result = queryService.viewCount(containerId, databaseId, view, principal); + log.trace("execute view {}", view); log.trace("find view data resulted in result {}", result); return ResponseEntity.ok() .body(result); diff --git a/fda-query-service/rest-service/src/main/resources/application-local.yml b/fda-query-service/rest-service/src/main/resources/application-local.yml index 8e36da9b7be6e9aa2e1ba5aee7d69ab244f1cd24..9451262fbd588419c058cfa6f760a81500a7b1e6 100644 --- a/fda-query-service/rest-service/src/main/resources/application-local.yml +++ b/fda-query-service/rest-service/src/main/resources/application-local.yml @@ -32,7 +32,7 @@ logging: pattern.console: "%d %highlight(%-5level) %msg%n" level: root: warn - at.tuwien.: info + at.tuwien.: trace org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug eureka: instance.hostname: query-service diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java index 5df209241867821d3efc77320d363c49420a091a..05746427c70f6e306974911ae7778d970da6019e 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java @@ -301,12 +301,22 @@ public abstract class BaseUnitTest { .tag(IMAGE_ELASTIC_TAG) .build(); + public final static Long IMAGE_PROXY_ID = 4L; + public final static String IMAGE_PROXY_REPOSITORY = "nginx"; + public final static String IMAGE_PROXY_TAG = "latest"; + + public final static ContainerImage IMAGE_PROXY = ContainerImage.builder() + .id(IMAGE_PROXY_ID) + .repository(IMAGE_PROXY_REPOSITORY) + .tag(IMAGE_PROXY_TAG) + .build(); + public final static Long CONTAINER_1_ID = 1L; public final static String CONTAINER_1_HASH = "deadbeef"; public final static ContainerImage CONTAINER_1_IMAGE = IMAGE_1; public final static String CONTAINER_1_NAME = "u01"; - public final static String CONTAINER_1_INTERNALNAME = "fda-userdb-u01"; - public final static String CONTAINER_1_IP = "172.28.0.5"; + public final static String CONTAINER_1_INTERNALNAME = "dbrepo-userdb-u01"; + public final static String CONTAINER_1_IP = "172.30.0.5"; public final static Instant CONTAINER_1_CREATED = Instant.now().minus(1, HOURS); public final static String[] CONTAINER_1_ENV = new String[]{"MARIADB_USER=mariadb", "MARIADB_PASSWORD=mariadb", "MARIADB_ROOT_PASSWORD=mariadb", "MARIADB_DATABASE=weather"}; @@ -329,8 +339,8 @@ public abstract class BaseUnitTest { public final static String CONTAINER_2_HASH = "deadbeef"; public final static ContainerImage CONTAINER_2_IMAGE = IMAGE_1; public final static String CONTAINER_2_NAME = "u02"; - public final static String CONTAINER_2_INTERNALNAME = "fda-userdb-u02"; - public final static String CONTAINER_2_IP = "172.28.0.6"; + public final static String CONTAINER_2_INTERNALNAME = "dbrepo-userdb-u02"; + public final static String CONTAINER_2_IP = "172.30.0.6"; public final static Instant CONTAINER_2_CREATED = Instant.now().minus(1, HOURS); public final static String[] CONTAINER_2_ENV = new String[]{"MARIADB_USER=mariadb", "MARIADB_PASSWORD=mariadb", "MARIADB_ROOT_PASSWORD=mariadb", "MARIADB_DATABASE=zoo"}; @@ -353,8 +363,8 @@ public abstract class BaseUnitTest { public final static String CONTAINER_3_HASH = "deadbeef"; public final static ContainerImage CONTAINER_3_IMAGE = IMAGE_1; public final static String CONTAINER_3_NAME = "u03"; - public final static String CONTAINER_3_INTERNALNAME = "fda-userdb-u03"; - public final static String CONTAINER_3_IP = "172.28.0.7"; + public final static String CONTAINER_3_INTERNALNAME = "dbrepo-userdb-u03"; + public final static String CONTAINER_3_IP = "172.30.0.7"; public final static Instant CONTAINER_3_CREATED = Instant.now().minus(1, HOURS); public final static HealthCheck CONTAINER_3_HEALTHCHECK = new HealthCheck() .withTest(List.of("CMD", "mysqladmin", "ping", "--host=127.0.0.1", "--password=mariadb")); @@ -374,8 +384,8 @@ public abstract class BaseUnitTest { public final static String CONTAINER_4_HASH = "deadbeef"; public final static ContainerImage CONTAINER_4_IMAGE = IMAGE_1; public final static String CONTAINER_4_NAME = "u04"; - public final static String CONTAINER_4_INTERNALNAME = "fda-userdb-u04"; - public final static String CONTAINER_4_IP = "172.28.0.8"; + public final static String CONTAINER_4_INTERNALNAME = "dbrepo-userdb-u04"; + public final static String CONTAINER_4_IP = "172.30.0.8"; public final static Instant CONTAINER_4_CREATED = Instant.now().minus(1, HOURS); public final static HealthCheck CONTAINER_4_HEALTHCHECK = new HealthCheck() .withTest(List.of("CMD", "mysqladmin", "ping", "--host=127.0.0.1", "--password=mariadb")); @@ -395,9 +405,9 @@ public abstract class BaseUnitTest { .build(); public final static Long CONTAINER_BROKER_ID = 5L; - public final static String CONTAINER_BROKER_NAME = "broker-service"; - public final static String CONTAINER_BROKER_INTERNAL_NAME = "broker-service"; - public final static String CONTAINER_BROKER_IP = "172.29.0.2"; + public final static String CONTAINER_BROKER_NAME = "dbrepo-broker-service"; + public final static String CONTAINER_BROKER_INTERNAL_NAME = "dbrepo-broker-service"; + public final static String CONTAINER_BROKER_IP = "172.31.0.2"; public final static String CONTAINER_BROKER_HASH = "deadbeef"; public final static Instant CONTAINER_BROKER_CREATED = Instant.now().minus(1, HOURS); public final static HealthCheck CONTAINER_BROKER_HEALTHCHECK = new HealthCheck() @@ -419,7 +429,7 @@ public abstract class BaseUnitTest { public final static Long CONTAINER_ELASTIC_ID = 6L; public final static String CONTAINER_ELASTIC_NAME = "fda-elastic-service"; public final static String CONTAINER_ELASTIC_INTERNAL_NAME = "search-mock-service"; - public final static String CONTAINER_ELASTIC_IP = "172.29.0.3"; + public final static String CONTAINER_ELASTIC_IP = "172.31.0.3"; public final static String CONTAINER_ELASTIC_HASH = "deadbeef"; public final static Instant CONTAINER_ELASTIC_CREATED = Instant.now().minus(1, HOURS); @@ -435,6 +445,28 @@ public abstract class BaseUnitTest { .creator(USER_1) .build(); + public final static Long CONTAINER_PROXY_ID = 7L; + public final static String CONTAINER_PROXY_NAME = "dbrepo-proxy"; + public final static String CONTAINER_PROXY_INTERNAL_NAME = "dbrepo-proxy"; + public final static String CONTAINER_PROXY_IP = "172.31.0.4"; + public final static String CONTAINER_PROXY_HASH = "deadbeef"; + public final static Instant CONTAINER_PROXY_CREATED = Instant.now().minus(1, HOURS); + public final static String[] CONTAINER_PROXY_ENV = new String[]{}; + public final static HealthCheck CONTAINER_PROXY_HEALTHCHECK = new HealthCheck() + .withTest(List.of("CMD", "service", "nginx", "status")); + + public final static Container CONTAINER_PROXY = Container.builder() + .id(CONTAINER_PROXY_ID) + .name(CONTAINER_PROXY_NAME) + .internalName(CONTAINER_PROXY_INTERNAL_NAME) + .imageId(IMAGE_PROXY_ID) + .image(IMAGE_PROXY) + .hash(CONTAINER_PROXY_HASH) + .ipAddress(CONTAINER_PROXY_IP) + .created(CONTAINER_PROXY_CREATED) + .creator(USER_1) + .build(); + public final static Long DATABASE_1_ID = 1L; public final static String DATABASE_1_NAME = "Weather"; public final static String DATABASE_1_INTERNALNAME = "weather"; @@ -1003,9 +1035,10 @@ public abstract class BaseUnitTest { public final static String QUERY_1_DOI = "1111/1"; public final static Long QUERY_1_CONTAINER_ID = CONTAINER_1_ID; public final static Long QUERY_1_DATABASE_ID = DATABASE_1_ID; + public final static Long QUERY_1_RESULT_NUMBER = 2L; public final static String QUERY_1_QUERY_HASH = "a3b8ac39e38167d14cf3a9c20a69e4b6954d049525390b973a2c23064953a992"; public final static String QUERY_1_RESULT_HASH = "8358c8ade4849d2094ab5bb29127afdae57e6bb5acb1db7af603813d406c467a"; - public final static Instant QUERY_1_CREATED = Instant.now(); + public final static Instant QUERY_1_CREATED = Instant.ofEpochSecond(1677648377); public final static Instant QUERY_1_EXECUTION = Instant.now(); public final static Boolean QUERY_1_PERSISTED = false; @@ -1014,9 +1047,12 @@ public abstract class BaseUnitTest { .query(QUERY_1_STATEMENT) .queryHash(QUERY_1_QUERY_HASH) .resultHash(QUERY_1_RESULT_HASH) + .resultNumber(QUERY_1_RESULT_NUMBER) .created(QUERY_1_CREATED) .createdBy(USER_1_USERNAME) .isPersisted(QUERY_1_PERSISTED) + .executed(QUERY_1_EXECUTION) + .created(QUERY_1_CREATED) .build(); public final static QueryDto QUERY_1_DTO = QueryDto.builder() @@ -1050,6 +1086,7 @@ public abstract class BaseUnitTest { public final static String QUERY_2_QUERY_HASH = "a2d2dd94ebc7653bb5a3b55dd8ed5e91d3d13c225c6855a1eb4eb7ca14c36ced"; public final static Long QUERY_2_CONTAINER_ID = CONTAINER_2_ID; public final static Long QUERY_2_DATABASE_ID = DATABASE_2_ID; + public final static Long QUERY_2_RESULT_NUMBER = 2L; public final static String QUERY_2_RESULT_HASH = "ff3f7cbe1b96d296957f6e39e55b8b1b577fa3d205d4795af99594cfd20cb80d"; public final static Instant QUERY_2_CREATED = Instant.now().minus(2, MINUTES); public final static Instant QUERY_2_EXECUTION = Instant.now().minus(1, MINUTES); @@ -1060,9 +1097,12 @@ public abstract class BaseUnitTest { .query(QUERY_2_STATEMENT) .queryHash(QUERY_2_QUERY_HASH) .resultHash(QUERY_2_RESULT_HASH) + .resultNumber(QUERY_2_RESULT_NUMBER) .created(QUERY_2_CREATED) .createdBy(USER_1_USERNAME) .isPersisted(QUERY_2_PERSISTED) + .created(QUERY_2_CREATED) + .executed(QUERY_2_EXECUTION) .build(); public final static List<TableColumn> TABLE_2_COLUMNS = List.of(TableColumn.builder() @@ -2056,6 +2096,20 @@ public abstract class BaseUnitTest { .creator(USER_1) .build(); + public final static Table TABLE_4_NOCOLS = Table.builder() + .id(TABLE_4_ID) + .created(Instant.now()) + .internalName(TABLE_4_INTERNALNAME) + .description(TABLE_4_DESCRIPTION) + .name(TABLE_4_NAME) + .lastModified(TABLE_4_LAST_MODIFIED) + .tdbid(DATABASE_1_ID) + .queueName(TABLE_4_QUEUE_NAME) + .routingKey(TABLE_4_ROUTING_KEY) + .columns(List.of()) + .creator(USER_1) + .build(); + public final static List<TableColumn> TABLE_5_COLUMNS = List.of(TableColumn.builder() .id(COLUMN_5_1_ID) .ordinalPosition(COLUMN_5_1_ORDINALPOS) @@ -2189,6 +2243,20 @@ public abstract class BaseUnitTest { .creator(USER_1) .build(); + public final static Table TABLE_7_NOCOLS = Table.builder() + .id(TABLE_7_ID) + .created(Instant.now()) + .internalName(TABLE_7_INTERNAL_NAME) + .description(TABLE_7_DESCRIPTION) + .name(TABLE_7_NAME) + .lastModified(TABLE_7_LAST_MODIFIED) + .tdbid(DATABASE_1_ID) + .queueName(TABLE_7_QUEUE_NAME) + .routingKey(TABLE_7_ROUTING_KEY) + .columns(List.of()) + .creator(USER_1) + .build(); + public final static Long VIEW_1_ID = 1L; public final static Boolean VIEW_1_INITIAL_VIEW = false; public final static String VIEW_1_NAME = "JUnit"; @@ -2227,7 +2295,7 @@ public abstract class BaseUnitTest { public final static Long VIEW_2_CONTAINER_ID = CONTAINER_1_ID; public final static Long VIEW_2_DATABASE_ID = DATABASE_1_ID; public final static Boolean VIEW_2_PUBLIC = true; - public final static String VIEW_2_QUERY = "select `date`, `location`, `mintemp`, `rainfall` from `weather_aus`"; + public final static String VIEW_2_QUERY = "select `date`, `location`, `mintemp`, `rainfall` from `weather_aus` where `location` = 'Albury'"; public final static View VIEW_2 = View.builder() .id(VIEW_2_ID) @@ -2258,7 +2326,7 @@ public abstract class BaseUnitTest { public final static Long VIEW_3_CONTAINER_ID = CONTAINER_1_ID; public final static Long VIEW_3_DATABASE_ID = DATABASE_1_ID; public final static Boolean VIEW_3_PUBLIC = false; - public final static String VIEW_3_QUERY = "select w.`mintemp`, w.`rainfall`, w.`location`, m.`lat`, m.`lng` from `weather_aus` w join `mock_view` m on m.`location` = w.`location`"; + public final static String VIEW_3_QUERY = "select w.`mintemp`, w.`rainfall`, w.`location`, m.`lat`, m.`lng` from `weather_aus` w join `junit2` m on m.`location` = w.`location`"; public final static View VIEW_3 = View.builder() .id(VIEW_3_ID) @@ -2313,8 +2381,8 @@ public abstract class BaseUnitTest { .exchangeName(DATABASE_1_EXCHANGE) .creator(USER_1) .owner(USER_1) - .tables(List.of()) /* TABLE_1, TABLE_2, TABLE_3 */ - .views(List.of()) + .tables(List.of(TABLE_1, TABLE_2, TABLE_3, TABLE_7)) + .views(List.of(VIEW_1)) .build(); public final static Database DATABASE_2 = Database.builder() @@ -2348,7 +2416,6 @@ public abstract class BaseUnitTest { .build(); public final static Long QUERY_1_RESULT_ID = 1L; - public final static Long QUERY_1_RESULT_NUMBER = 2L; public final static List<Map<String, Object>> QUERY_1_RESULT_RESULT = List.of( new HashMap<>() {{ put("location", "Albury"); diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/auth/AuthTokenFilterTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/auth/AuthTokenFilterTest.java index eac19243984f0eb4936c6121ade14f9b9ece160f..bf3df721bafd91903a06bc74726a6347a1a293e5 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/auth/AuthTokenFilterTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/auth/AuthTokenFilterTest.java @@ -5,7 +5,6 @@ import at.tuwien.config.H2Utils; import at.tuwien.config.IndexConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.gateway.AuthenticationServiceGateway; -import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.jpa.UserRepository; import com.rabbitmq.client.Channel; import lombok.extern.log4j.Log4j2; @@ -46,9 +45,6 @@ public class AuthTokenFilterTest extends BaseUnitTest { @MockBean private UserRepository userRepository; - @MockBean - private RabbitMqListenerImpl rabbitMqListener; - @MockBean private AuthenticationServiceGateway authenticationServiceGateway; diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java b/fda-query-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java index 2f03e666f19c250ae0afe19f3bdc548586e2acc3..f91219b29e5831a008e39cc3656d47d9bbdcf6e0 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java @@ -90,12 +90,16 @@ public class DockerConfig extends BaseUnitTest { final HostConfig hostConfig1; final String network = (container.getInternalName().contains("userdb") ? "fda-userdb" : "fda-public"); if (bind == null) { + log.trace("map standard binding /tmp:/tmp"); hostConfig1 = hostConfig.withNetworkMode(network) .withBinds(Bind.parse("/tmp:/tmp")); } else { - hostConfig1 = hostConfig.withNetworkMode(network).withBinds(Bind.parse(bind), Bind.parse("/tmp:/tmp")); + log.trace("map non-standard binding {}, /tmp:/tmp", bind); + hostConfig1 = hostConfig.withNetworkMode(network) + .withBinds(Bind.parse(bind), Bind.parse("/tmp:/tmp")); } if (port != null) { + log.trace("map port binding {}:{}", port, port); hostConfig1.withPortBindings(PortBinding.parse(port + ":" + port)); } final CreateContainerCmd cmd = dockerClient.createContainerCmd(container.getImage().getRepository() + ":" + container.getImage().getTag()) @@ -158,14 +162,14 @@ public class DockerConfig extends BaseUnitTest { .withName("fda-userdb") .withIpam(new Network.Ipam() .withConfig(new Network.Ipam.Config() - .withSubnet("172.28.0.0/16"))) + .withSubnet("172.30.0.0/16"))) .withEnableIpv6(false) .exec(); dockerClient.createNetworkCmd() .withName("fda-public") .withIpam(new Network.Ipam() .withConfig(new Network.Ipam.Config() - .withSubnet("172.29.0.0/16"))) + .withSubnet("172.31.0.0/16"))) .withEnableIpv6(false) .exec(); } @@ -191,6 +195,9 @@ public class DockerConfig extends BaseUnitTest { case 5: log.debug("container with id {} has a health check config", containerId); return CONTAINER_BROKER_HEALTHCHECK; + case 7: + log.debug("container with id {} has a health check config", containerId); + return CONTAINER_PROXY_HEALTHCHECK; } log.trace("container with id {} does not have a healthcheck config", containerId); return null; diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java b/fda-query-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java index 076c1b05ccacc15ff2a66d188f118663c5207e54..8d00a9e77e0cb7674bdfd13d5d348244a08e0f92 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/config/MariaDbConfig.java @@ -18,12 +18,44 @@ public class MariaDbConfig { final String jdbc = "jdbc:mariadb://" + hostname + "/" + database; log.trace("connect to database {}", jdbc); final Connection connection = DriverManager.getConnection(jdbc, "root", "mariadb"); - final Statement statement = connection.createStatement(); - statement.execute("INSERT INTO qs_queries (created_by, query, query_normalized, is_persisted, query_hash, result_hash, result_number) " + - "VALUES ('" + username + "', '" + query.getQuery() + "', '" + query.getQuery() + "', true, '" + query.getQueryHash() + "', '" + query.getResultHash() + "', " + query.getResultNumber() + ")"); + final PreparedStatement prepareStatement = connection.prepareStatement("INSERT INTO qs_queries (created_by, query, query_normalized, is_persisted, query_hash, result_hash, result_number, created, executed) VALUES (?,?,?,?,?,?,?,?,?)"); + prepareStatement.setString(1, username); + prepareStatement.setString(2, query.getQuery()); + prepareStatement.setString(3, query.getQuery()); + prepareStatement.setBoolean(4, query.getIsPersisted()); + prepareStatement.setString(5, query.getQueryHash()); + prepareStatement.setString(6, query.getResultHash()); + prepareStatement.setLong(7, query.getResultNumber()); + prepareStatement.setTimestamp(8, Timestamp.from(query.getCreated())); + prepareStatement.setTimestamp(9, Timestamp.from(query.getExecuted())); + log.trace("prepared statement: {}", prepareStatement); + prepareStatement.executeUpdate(); connection.close(); } + public static List<Map<String, Object>> listQueryStore(String hostname, String database) throws SQLException { + final String jdbc = "jdbc:mariadb://" + hostname + "/" + database; + log.trace("connect to database {}", jdbc); + final Connection connection = DriverManager.getConnection(jdbc, "root", "mariadb"); + final Statement statement = connection.createStatement(); + final ResultSet result = statement.executeQuery("SELECT created_by, query, query_normalized, is_persisted, query_hash, result_hash, result_number, created, executed FROM qs_queries"); + final List<Map<String, Object>> rows = new LinkedList<>(); + while (result.next()) { + rows.add(new HashMap<>() {{ + put("created_by", result.getString(1)); + put("query", result.getString(2)); + put("query_normalized", result.getString(3)); + put("is_persisted", result.getBoolean(4)); + put("query_hash", result.getString(5)); + put("result_hash", result.getString(6)); + put("result_number", result.getLong(7)); + put("created", result.getTimestamp(8)); + put("executed", result.getTimestamp(9)); + }}); + } + return rows; + } + public static List<Map<String, String>> selectQuery(String hostname, String database, String query, String... columns) throws SQLException { final String jdbc = "jdbc:mariadb://" + hostname + "/" + database; diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/config/RabbitMqConfig.java b/fda-query-service/rest-service/src/test/java/at/tuwien/config/RabbitMqConfig.java index 0ee03e69ad4e85d2f54c09488fb5bf94265946d6..72ef54c966d37a7f92566228f9e00cbf6f8643a1 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/config/RabbitMqConfig.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/config/RabbitMqConfig.java @@ -33,7 +33,7 @@ public class RabbitMqConfig { public List<ConsumerDto> findAllConsumers() throws IOException { log.trace("gateway broker find all consumers"); - final URI findUri = URI.create("http://broker-service:15672/api/consumers/%2F"); + final URI findUri = URI.create("http://dbrepo-broker-service:15672/api/consumers/%2F"); final ResponseEntity<List<ConsumerDto>> response = restTemplate.exchange(findUri, HttpMethod.GET, new HttpEntity<>(null, getHeaders()), new ParameterizedTypeReference<>() { }); @@ -52,7 +52,7 @@ public class RabbitMqConfig { } } for (Map.Entry<String, Integer> consumer : consumers.entrySet()) { - log.debug("queue {} has {} consumers", consumer.getKey(), consumer.getValue()); + log.trace("queue {} has {} consumers", consumer.getKey(), consumer.getValue()); } return response.getBody(); } diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/ExportEndpointUnitTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/ExportEndpointUnitTest.java index 6e5062cff9ea8ef90689cb4bbb5873ce789ef58e..f5126e08b48fb31b936fd2e56d4ab8ff7bd3f685 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/ExportEndpointUnitTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/ExportEndpointUnitTest.java @@ -7,6 +7,8 @@ import at.tuwien.config.ReadyConfig; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.jpa.DatabaseAccessRepository; import at.tuwien.repository.jpa.TableRepository; @@ -49,9 +51,14 @@ public class ExportEndpointUnitTest extends BaseUnitTest { @MockBean private IndexConfig indexInitializer; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private QueryService queryService; @@ -240,7 +247,7 @@ public class ExportEndpointUnitTest extends BaseUnitTest { } when(tableRepository.find(containerId, databaseId, tableId)) .thenReturn(Optional.of(TABLE_1)); - when(queryService.findAll(containerId, databaseId, tableId, timestamp, principal)) + when(queryService.tableFindAll(containerId, databaseId, tableId, timestamp, principal)) .thenReturn(resource); /* test */ diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/QueryEndpointUnitTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/QueryEndpointUnitTest.java index 7b1e9ade2c5e6b276207d2ebcc714da4b95cd464..8f315d2b404e95c7e0675ef36e13f6d9a88b0485 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/QueryEndpointUnitTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/QueryEndpointUnitTest.java @@ -10,6 +10,8 @@ import at.tuwien.config.ReadyConfig; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.jpa.ContainerRepository; import at.tuwien.repository.jpa.DatabaseAccessRepository; @@ -52,9 +54,14 @@ public class QueryEndpointUnitTest extends BaseUnitTest { @MockBean private IndexConfig indexInitializer; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private DatabaseAccessRepository databaseAccessRepository; diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/StoreEndpointUnitTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/StoreEndpointUnitTest.java index f323836cf21f413ec01d6ae0374c52915e966e64..42782640bde805393dcf28bc3e735c5e9ecd1461 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/StoreEndpointUnitTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/StoreEndpointUnitTest.java @@ -9,6 +9,8 @@ import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.user.User; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.querystore.Query; import at.tuwien.repository.jpa.IdentifierRepository; @@ -51,9 +53,14 @@ public class StoreEndpointUnitTest extends BaseUnitTest { @MockBean private IndexConfig indexInitializer; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @Autowired private StoreEndpoint storeEndpoint; diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/TableDataEndpointUnitTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/TableDataEndpointUnitTest.java index 7766c2d041d25fcd9fa6c20be8c2c0f374e74a78..4cf2392ed0a94319043e7738151d0c86123b7cf7 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/TableDataEndpointUnitTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/TableDataEndpointUnitTest.java @@ -11,6 +11,8 @@ import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.database.table.Table; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.service.AccessService; import at.tuwien.service.DatabaseService; @@ -18,8 +20,10 @@ import at.tuwien.service.TableService; import at.tuwien.service.impl.QueryServiceImpl; import com.rabbitmq.client.Channel; import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -29,6 +33,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import java.security.Principal; import java.time.Instant; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -44,9 +49,14 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { @MockBean private Channel channel; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private QueryServiceImpl queryService; @@ -65,490 +75,299 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { @Autowired private TableDataEndpoint dataEndpoint; - @Test - public void import_publicAnonymous_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_import(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, null, - null, null); - }); - } - - @Test - public void import_publicRead_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_import(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_2_USERNAME, - DATABASE_1_READ_ACCESS, USER_2_PRINCIPAL); - }); - } - - @Test - public void import_publicWriteOwn_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_import(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_2_USERNAME, - DATABASE_1_WRITE_OWN_ACCESS, USER_2_PRINCIPAL); - }); - } - - @Test - public void import_publicWriteAll_succeeds() throws UserNotFoundException, TableNotFoundException, - NotAllowedException, TableMalformedException, DatabaseConnectionException, QueryMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException { - - /* test */ - generic_import(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_2_USERNAME, - DATABASE_1_WRITE_ALL_ACCESS, USER_2_PRINCIPAL); - } - - @Test - public void import_publicOwner_succeds() throws UserNotFoundException, TableNotFoundException, NotAllowedException, - TableMalformedException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, - ImageNotSupportedException, ContainerNotFoundException { - - /* test */ - generic_import(CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_1_USERNAME, - DATABASE_1_WRITE_ALL_ACCESS, USER_1_PRINCIPAL); - } - - @Test - public void insert_publicAnonymous_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_insert(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_2_USERNAME, - null, TABLE_1_CSV_DTO, null); - }); - } - - @Test - public void insert_publicRead_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_insert(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_2_USERNAME, - DATABASE_1_READ_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL); - }); - } - - @Test - public void insert_publicWriteOwn_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_insert(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_2_USERNAME, - DATABASE_1_WRITE_OWN_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL); - }); - } - - @Test - public void insert_publicWriteAll_succeeds() throws UserNotFoundException, TableNotFoundException, NotAllowedException, - TableMalformedException, DatabaseConnectionException, DatabaseNotFoundException, ImageNotSupportedException, - ContainerNotFoundException { - - /* test */ - generic_insert(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_2_USERNAME, - DATABASE_1_WRITE_ALL_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL); - } - - @Test - public void insert_publicOwner_succeeds() throws UserNotFoundException, TableNotFoundException, NotAllowedException, - TableMalformedException, DatabaseConnectionException, DatabaseNotFoundException, ImageNotSupportedException, - ContainerNotFoundException { - - /* test */ - generic_insert(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_1_USERNAME, - DATABASE_1_WRITE_ALL_ACCESS, TABLE_1_CSV_DTO, USER_1_PRINCIPAL); - } - - @Test - public void insert_publicOwnerDataNull_succeeds() throws UserNotFoundException, TableNotFoundException, - NotAllowedException, TableMalformedException, DatabaseConnectionException, DatabaseNotFoundException, - ImageNotSupportedException, ContainerNotFoundException { - - /* test */ - generic_insert(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_1_USERNAME, - DATABASE_1_WRITE_ALL_ACCESS, null, USER_1_PRINCIPAL); - } - - @Test - public void getAll_publicAnonymous_succeeds() throws TableNotFoundException, DatabaseConnectionException, TableMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException, - QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { - - /* test */ - generic_getAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, null, - null, null, null, null, null, null, null); - } - - @Test - public void getAll_publicRead_succeeds() throws TableNotFoundException, DatabaseConnectionException, TableMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException, - QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { - - /* test */ - generic_getAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_2_USERNAME, - DATABASE_1_READ_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null); - } - - @Test - public void getAll_publicWriteOwn_succeeds() throws TableNotFoundException, DatabaseConnectionException, TableMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException, - QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { - - /* test */ - generic_getAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_2_USERNAME, - DATABASE_1_WRITE_OWN_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null); - } - - @Test - public void getAll_publicWriteAll_succeeds() throws TableNotFoundException, DatabaseConnectionException, TableMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException, - QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { - - /* test */ - generic_getAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_2_USERNAME, - DATABASE_1_WRITE_ALL_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null); - } - - @Test - public void getAll_publicOwner_succeeds() throws TableNotFoundException, DatabaseConnectionException, TableMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException, - QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { - - /* test */ - generic_getAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_1_USERNAME, - DATABASE_1_WRITE_ALL_ACCESS, USER_1_PRINCIPAL, null, null, null, null, null); - } - - @Test - public void getAll_publicAnonymousPageNull_fails() { - final Long page = null; - final Long size = 1L; - - /* test */ - assertThrows(PaginationException.class, () -> { - generic_getAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, null, - null, null, null, page, size, null, null); - }); - } - - @Test - public void getAll_publicAnonymousSizeNull_fails() { - final Long page = 1L; - final Long size = null; - - /* test */ - assertThrows(PaginationException.class, () -> { - generic_getAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, null, - null, null, null, page, size, null, null); - }); - } - - @Test - public void getAll_publicAnonymousPageNegative_fails() { - final Long page = -1L; - final Long size = 1L; - - /* test */ - assertThrows(PaginationException.class, () -> { - generic_getAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, null, - null, null, null, page, size, null, null); - }); - } - - @Test - public void getAll_publicAnonymousSizeZero_fails() { - final Long page = 0L; - final Long size = 0L; - - /* test */ - assertThrows(PaginationException.class, () -> { - generic_getAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, null, - null, null, null, page, size, null, null); - }); - } - - @Test - public void getAll_publicAnonymousSizeNegative_fails() { - final Long page = 0L; - final Long size = -1L; - - /* test */ - assertThrows(PaginationException.class, () -> { - generic_getAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, null, - null, null, null, page, size, null, null); - }); - } - - /* ################################################################################################### */ - /* ## PRIVATE DATABASES ## */ - /* ################################################################################################### */ - - @Test - public void import_privateAnonymous_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_import(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, TABLE_1_ID, TABLE_1, null, null, null); + public static Stream<Arguments> import_fails_parameters() { + return Stream.of( + Arguments.arguments("public anonymous", NotAllowedException.class, CONTAINER_1_ID, DATABASE_1_ID, + DATABASE_1, + TABLE_1_ID, TABLE_1, null, null, null), + Arguments.arguments("public read", NotAllowedException.class, CONTAINER_1_ID, DATABASE_1_ID, + DATABASE_1, TABLE_1_ID, + TABLE_1, USER_2_USERNAME, DATABASE_1_READ_ACCESS, USER_2_PRINCIPAL), + Arguments.arguments("public write-own", NotAllowedException.class, CONTAINER_1_ID, DATABASE_1_ID, + DATABASE_1, TABLE_1_ID, + TABLE_1, USER_2_USERNAME, DATABASE_1_WRITE_OWN_ACCESS, USER_2_PRINCIPAL), + Arguments.arguments("private anonymous", NotAllowedException.class, CONTAINER_2_ID, DATABASE_2_ID, + DATABASE_2, TABLE_1_ID, TABLE_1, null, null, null), + Arguments.arguments("private read", NotAllowedException.class, CONTAINER_2_ID, DATABASE_2_ID, + DATABASE_2, TABLE_1_ID, TABLE_1, USER_2_USERNAME, + DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL), + Arguments.arguments("private write-own", NotAllowedException.class, CONTAINER_2_ID, DATABASE_2_ID, + DATABASE_2, TABLE_1_ID, TABLE_1, USER_2_USERNAME, + DATABASE_2_WRITE_OWN_ACCESS, USER_2_PRINCIPAL) + ); + } + + @ParameterizedTest + @MethodSource("import_fails_parameters") + public <T extends Throwable> void import_fails(String test, Class<T> expectedException, Long containerId, + Long databaseId, Database database, Long tableId, Table table, + String username, DatabaseAccess access, Principal principal) { + + /* test */ + assertThrows(expectedException, () -> { + generic_import(containerId, databaseId, database, tableId, table, username, access, principal); }); } - @Test - public void import_privateRead_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_import(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, TABLE_1_ID, TABLE_1, USER_2_USERNAME, - DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL); + public static Stream<Arguments> import_succeeds_parameters() { + return Stream.of( + Arguments.arguments("public write-all", CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, + USER_2_USERNAME, DATABASE_1_WRITE_ALL_ACCESS, USER_2_PRINCIPAL), + Arguments.arguments("public owner", CONTAINER_1_ID, DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, + USER_1_USERNAME, DATABASE_1_WRITE_ALL_ACCESS, USER_1_PRINCIPAL), + Arguments.arguments("private write-all", CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, TABLE_1_ID, + TABLE_1, USER_2_USERNAME, + DATABASE_2_WRITE_ALL_ACCESS, USER_2_PRINCIPAL), + Arguments.arguments("private owner", CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, TABLE_1_ID, TABLE_1, + USER_1_USERNAME, + DATABASE_2_WRITE_ALL_ACCESS, USER_1_PRINCIPAL) + ); + } + + @ParameterizedTest + @MethodSource("import_succeeds_parameters") + public void import_succeeds(String test, Long containerId, Long databaseId, Database database, Long tableId, + Table table, String username, DatabaseAccess access, Principal principal) throws UserNotFoundException, TableNotFoundException, NotAllowedException, TableMalformedException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException { + + /* test */ + generic_import(containerId, databaseId, database, tableId, table, username, access, principal); + } + + public static Stream<Arguments> insert_fails_parameters() { + return Stream.of( + Arguments.arguments("public anonymous", NotAllowedException.class, CONTAINER_1_ID, DATABASE_1_ID, + TABLE_1_ID, + DATABASE_1, TABLE_1, USER_2_USERNAME, null, TABLE_1_CSV_DTO, null), + Arguments.arguments("public read", NotAllowedException.class, CONTAINER_1_ID, DATABASE_1_ID, + TABLE_1_ID, DATABASE_1, + TABLE_1, USER_2_USERNAME, DATABASE_1_READ_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL), + Arguments.arguments("public write-own", NotAllowedException.class, CONTAINER_1_ID, DATABASE_1_ID, + TABLE_1_ID, DATABASE_1, + TABLE_1, USER_2_USERNAME, DATABASE_1_WRITE_OWN_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL), + Arguments.arguments("private anonymous", NotAllowedException.class, CONTAINER_2_ID, DATABASE_2_ID, + TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, null, + TABLE_1_CSV_DTO, null), + Arguments.arguments("private read", NotAllowedException.class, CONTAINER_2_ID, DATABASE_2_ID, + TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, + DATABASE_2_READ_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL), + Arguments.arguments("private write-own", NotAllowedException.class, CONTAINER_2_ID, DATABASE_2_ID, + TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, + DATABASE_2_WRITE_OWN_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL) + ); + } + + @ParameterizedTest + @MethodSource("insert_fails_parameters") + public <T extends Throwable> void insert_fails(String test, Class<T> expectedException, Long containerId, + Long databaseId, Long tableId, Database database, Table table, + String username, DatabaseAccess access, TableCsvDto data, + Principal principal) { + + /* test */ + assertThrows(expectedException, () -> { + generic_insert(containerId, databaseId, tableId, database, table, username, access, data, principal); }); } - @Test - public void import_privateWriteOwn_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_import(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, TABLE_1_ID, TABLE_1, USER_2_USERNAME, - DATABASE_2_WRITE_OWN_ACCESS, USER_2_PRINCIPAL); - }); - } - - @Test - public void import_privateWriteAll_succeeds() throws UserNotFoundException, TableNotFoundException, - NotAllowedException, TableMalformedException, DatabaseConnectionException, QueryMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException { - - /* test */ - generic_import(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, TABLE_1_ID, TABLE_1, USER_2_USERNAME, - DATABASE_2_WRITE_ALL_ACCESS, USER_2_PRINCIPAL); - } - - @Test - public void import_privateOwner_succeds() throws UserNotFoundException, TableNotFoundException, NotAllowedException, - TableMalformedException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, - ImageNotSupportedException, ContainerNotFoundException { - - /* test */ - generic_import(CONTAINER_2_ID, DATABASE_2_ID, DATABASE_2, TABLE_1_ID, TABLE_1, USER_1_USERNAME, - DATABASE_2_WRITE_ALL_ACCESS, USER_1_PRINCIPAL); - } - - @Test - public void insert_privateAnonymous_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_insert(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - null, TABLE_1_CSV_DTO, null); - }); - } - - @Test - public void insert_privateRead_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_insert(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_READ_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL); - }); - } - - @Test - public void insert_privateWriteOwn_fails() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_insert(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_WRITE_OWN_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL); - }); - } - - @Test - public void insert_privateWriteAll_succeeds() throws UserNotFoundException, TableNotFoundException, NotAllowedException, - TableMalformedException, DatabaseConnectionException, DatabaseNotFoundException, ImageNotSupportedException, - ContainerNotFoundException { - - /* test */ - generic_insert(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_WRITE_ALL_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL); - } - - @Test - public void insert_privateOwner_succeeds() throws UserNotFoundException, TableNotFoundException, NotAllowedException, - TableMalformedException, DatabaseConnectionException, DatabaseNotFoundException, ImageNotSupportedException, - ContainerNotFoundException { - - /* test */ - generic_insert(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_1_USERNAME, - DATABASE_2_WRITE_ALL_ACCESS, TABLE_1_CSV_DTO, USER_1_PRINCIPAL); - } - - @Test - public void insert_privateOwnerDataNull_succeeds() throws UserNotFoundException, TableNotFoundException, + public static Stream<Arguments> insert_succeeds_parameters() { + return Stream.of( + Arguments.arguments("public write-all", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, USER_2_USERNAME, + DATABASE_1_WRITE_ALL_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL), + Arguments.arguments("public owner", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, + USER_1_USERNAME, + DATABASE_1_WRITE_ALL_ACCESS, TABLE_1_CSV_DTO, USER_1_PRINCIPAL), + Arguments.arguments("public owner, data null", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, USER_1_USERNAME, + DATABASE_1_WRITE_ALL_ACCESS, null, USER_1_PRINCIPAL), + Arguments.arguments("private write-all", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, + TABLE_1, USER_2_USERNAME, + DATABASE_2_WRITE_ALL_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL), + Arguments.arguments("private owner", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, + USER_1_USERNAME, + DATABASE_2_WRITE_ALL_ACCESS, TABLE_1_CSV_DTO, USER_1_PRINCIPAL), + Arguments.arguments("private owner, data null", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2 + , TABLE_1, USER_1_USERNAME, + DATABASE_2_WRITE_ALL_ACCESS, null, USER_1_PRINCIPAL) + ); + } + + @ParameterizedTest + @MethodSource("insert_succeeds_parameters") + public void insert_succeeds(String test, Long containerId, Long databaseId, Long tableId, Database database, + Table table, String username, DatabaseAccess access, TableCsvDto data, + Principal principal) throws UserNotFoundException, TableNotFoundException, NotAllowedException, TableMalformedException, DatabaseConnectionException, DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException { /* test */ - generic_insert(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_1_USERNAME, - DATABASE_2_WRITE_ALL_ACCESS, null, USER_1_PRINCIPAL); - } - - @Test - public void getAll_privateAnonymous_succeeds() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - generic_getAll(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, null, - null, null, null, null, null, null, null); - }); - } - - @Test - public void getAll_privateRead_succeeds() throws TableNotFoundException, DatabaseConnectionException, TableMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException, - QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { - - /* test */ - generic_getAll(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null); - } - - @Test - public void getAll_privateWriteOwn_succeeds() throws TableNotFoundException, DatabaseConnectionException, TableMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException, - QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { - - /* test */ - generic_getAll(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_WRITE_OWN_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null); - } - - @Test - public void getAll_privateWriteAll_succeeds() throws TableNotFoundException, DatabaseConnectionException, TableMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException, - QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { - - /* test */ - generic_getAll(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_WRITE_ALL_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null); - } - - @Test - public void getAll_privateOwner_succeeds() throws TableNotFoundException, DatabaseConnectionException, TableMalformedException, - DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException, - QueryStoreException, NotAllowedException, QueryMalformedException, SortException, UserNotFoundException { - - /* test */ - generic_getAll(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_1_USERNAME, - DATABASE_2_WRITE_ALL_ACCESS, USER_1_PRINCIPAL, null, null, null, null, null); - } - - @Test - public void getAll_privateReadPageNull_fails() { - final Long page = null; - final Long size = 1L; - - /* test */ - assertThrows(PaginationException.class, () -> { - generic_getAll(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, page, size, null, null); - }); - } - - @Test - public void getAll_privateReadSizeNull_fails() { - final Long page = 1L; - final Long size = null; - - /* test */ - assertThrows(PaginationException.class, () -> { - generic_getAll(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, page, size, null, null); - }); - } - - @Test - public void getAll_privateReadPageNegative_fails() { - final Long page = -1L; - final Long size = 1L; - - /* test */ - assertThrows(PaginationException.class, () -> { - generic_getAll(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, page, size, null, null); + generic_insert(containerId, databaseId, tableId, database, table, username, access, data, principal); + } + + public static Stream<Arguments> getAll_fails_parameters() { + return Stream.of( + Arguments.arguments("public anonymous page null", PaginationException.class, CONTAINER_1_ID, + DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, null, null, null, null, null, 1L, null, null), + Arguments.arguments("public anonymous size null", PaginationException.class, CONTAINER_1_ID, + DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, null, null, null, null, 1L, null, null, null), + Arguments.arguments("public anonymous page negative", PaginationException.class, CONTAINER_1_ID, + DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, null, null, null, null, -1L, 1L, null, null), + Arguments.arguments("public anonymous size zero", PaginationException.class, CONTAINER_1_ID, + DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, null, null, null, null, 0L, 0L, null, null), + Arguments.arguments("public anonymous size negative", PaginationException.class, CONTAINER_1_ID, + DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, null, null, null, null, 0L, -1L, null, null), + Arguments.arguments("private anonymous", NotAllowedException.class, CONTAINER_2_ID, DATABASE_2_ID, + TABLE_1_ID, DATABASE_2, TABLE_1, null, null, null, null, + null, null, null, null), + Arguments.arguments("private read, page null", PaginationException.class, CONTAINER_2_ID, + DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, + DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, null, 1L, null, null), + Arguments.arguments("private read, size null", PaginationException.class, CONTAINER_2_ID, + DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, + DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, 1L, null, null, null), + Arguments.arguments("private read, page negative", PaginationException.class, CONTAINER_2_ID, + DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, + DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, -1L, 1L, null, null), + Arguments.arguments("private read, size zero", PaginationException.class, CONTAINER_2_ID, + DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, + DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, 0L, 0L, null, null), + Arguments.arguments("private read, size negative", PaginationException.class, CONTAINER_2_ID, + DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, + DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, 0L, -1L, null, null) + ); + } + + @ParameterizedTest + @MethodSource("getAll_fails_parameters") + public <T extends Throwable> void getAll_fails(String test, Class<T> expectedException, Long containerId, + Long databaseId, Long tableId, Database database, Table table, + String username, DatabaseAccess access, Principal principal, + Instant timestamp, Long page, Long size, SortType sortDirection, + String sortColumn) { + + /* test */ + assertThrows(expectedException, () -> { + generic_getAll(containerId, databaseId, tableId, database, table, username, access, principal, timestamp, + page, size, sortDirection, sortColumn); }); } - @Test - public void getAll_privateReadSizeZero_fails() { - final Long page = 0L; - final Long size = 0L; - - /* test */ - assertThrows(PaginationException.class, () -> { - generic_getAll(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, page, size, null, null); - }); + public static Stream<Arguments> getAll_succeeds_parameters() { + return Stream.of( + Arguments.arguments("public anonymous", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, null, null, null, + null, null, null, null, null), + Arguments.arguments("public read", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, + USER_2_USERNAME, + DATABASE_1_READ_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null), + Arguments.arguments("public write-own", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, USER_2_USERNAME, + DATABASE_1_WRITE_OWN_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null), + Arguments.arguments("public write-all", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, USER_2_USERNAME, + DATABASE_1_WRITE_ALL_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null), + Arguments.arguments("public owner", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, + USER_1_USERNAME, + DATABASE_1_WRITE_ALL_ACCESS, USER_1_PRINCIPAL, null, null, null, null, null), + Arguments.arguments("private read", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, + USER_2_USERNAME, + DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null), + Arguments.arguments("private write-own", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, + TABLE_1, USER_2_USERNAME, + DATABASE_2_WRITE_OWN_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null), + Arguments.arguments("private write-all", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, + TABLE_1, USER_2_USERNAME, + DATABASE_2_WRITE_ALL_ACCESS, USER_2_PRINCIPAL, null, null, null, null, null), + Arguments.arguments("private owner", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, + USER_1_USERNAME, + DATABASE_2_WRITE_ALL_ACCESS, USER_1_PRINCIPAL, null, null, null, null, null) + ); + } + + @ParameterizedTest + @MethodSource("getAll_succeeds_parameters") + public void getAll_succeeds(String test, Long containerId, Long databaseId, Long tableId, Database database, + Table table, String username, DatabaseAccess access, Principal principal, + Instant timestamp, Long page, Long size, SortType sortDirection, String sortColumn) throws UserNotFoundException, TableNotFoundException, QueryStoreException, SortException, TableMalformedException, NotAllowedException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException { + + /* test */ + generic_getAll(containerId, databaseId, tableId, database, table, username, access, principal, timestamp, + page, size, sortDirection, sortColumn); + } + + public static Stream<Arguments> getCount_succeeds_parameters() { + return Stream.of( + Arguments.arguments("public anonymous", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, null, null, null, null), + Arguments.arguments("public read", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, + USER_2_USERNAME, + DATABASE_1_READ_ACCESS, USER_2_PRINCIPAL, null), + Arguments.arguments("public write-own", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, USER_2_USERNAME, + DATABASE_1_WRITE_OWN_ACCESS, USER_2_PRINCIPAL, null), + Arguments.arguments("public write-all", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, + TABLE_1, USER_2_USERNAME, + DATABASE_1_WRITE_ALL_ACCESS, USER_2_PRINCIPAL, null), + Arguments.arguments("public owner", CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, + USER_1_USERNAME, + DATABASE_1_WRITE_ALL_ACCESS, USER_1_PRINCIPAL, null), + Arguments.arguments("private read", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, + USER_2_USERNAME, + DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null), + Arguments.arguments("private write-own", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, + TABLE_1, USER_2_USERNAME, + DATABASE_2_WRITE_OWN_ACCESS, USER_2_PRINCIPAL, null), + Arguments.arguments("private write-all", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, + TABLE_1, USER_2_USERNAME, + DATABASE_2_WRITE_ALL_ACCESS, USER_2_PRINCIPAL, null), + Arguments.arguments("private owner", CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, + USER_1_USERNAME, + DATABASE_2_WRITE_ALL_ACCESS, USER_1_PRINCIPAL, null) + ); + } + + @ParameterizedTest + @MethodSource("getAll_succeeds_parameters") + public void getCount_succeeds(String test, Long containerId, Long databaseId, Long tableId, Database database, + Table table, String username, DatabaseAccess access, Principal principal, + Instant timestamp) throws UserNotFoundException, TableNotFoundException, QueryStoreException, SortException, TableMalformedException, NotAllowedException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, PaginationException, ContainerNotFoundException { + + /* test */ + generic_getCount(containerId, databaseId, tableId, database, table, username, access, principal, timestamp); } - @Test - public void getAll_privateReadSizeNegative_fails() { - final Long page = 0L; - final Long size = -1L; - - /* test */ - assertThrows(PaginationException.class, () -> { - generic_getAll(CONTAINER_2_ID, DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_USERNAME, - DATABASE_2_READ_ACCESS, USER_2_PRINCIPAL, null, page, size, null, null); - }); - } /* ################################################################################################### */ /* ## GENERIC TEST CASES ## */ /* ################################################################################################### */ public void generic_import(Long containerId, Long databaseId, Database database, Long tableId, Table table, - String username, DatabaseAccess access, Principal principal) - throws DatabaseNotFoundException, TableNotFoundException, NotAllowedException, UserNotFoundException, - TableMalformedException, DatabaseConnectionException, QueryMalformedException, ImageNotSupportedException, - ContainerNotFoundException { - final ImportDto request = ImportDto.builder() - .location("test:csv/csv_01.csv") - .build(); + String username, DatabaseAccess access, Principal principal) throws DatabaseNotFoundException, TableNotFoundException, NotAllowedException, UserNotFoundException, TableMalformedException, DatabaseConnectionException, QueryMalformedException, ImageNotSupportedException, ContainerNotFoundException { + final ImportDto request = ImportDto.builder().location("test:csv/csv_01.csv").build(); /* mock */ - when(databaseService.find(containerId, databaseId)) - .thenReturn(database); - when(tableService.find(containerId, databaseId, tableId)) - .thenReturn(table); - when(accessService.find(databaseId, username)) - .thenReturn(access); + when(databaseService.find(containerId, databaseId)).thenReturn(database); + when(tableService.find(containerId, databaseId, tableId)).thenReturn(table); + when(accessService.find(databaseId, username)).thenReturn(access); /* test */ - final ResponseEntity<?> response = dataEndpoint.importCsv(containerId, databaseId, tableId, request, - principal); + final ResponseEntity<?> response = dataEndpoint.importCsv(containerId, databaseId, tableId, request, principal); assertNotNull(response); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } public void generic_insert(Long containerId, Long databaseId, Long tableId, Database database, Table table, - String username, DatabaseAccess access, TableCsvDto data, Principal principal) - throws DatabaseNotFoundException, TableNotFoundException, NotAllowedException, UserNotFoundException, - TableMalformedException, DatabaseConnectionException, ImageNotSupportedException, - ContainerNotFoundException { + String username, DatabaseAccess access, TableCsvDto data, Principal principal) throws DatabaseNotFoundException, TableNotFoundException, NotAllowedException, UserNotFoundException, TableMalformedException, DatabaseConnectionException, ImageNotSupportedException, ContainerNotFoundException { /* mock */ - when(databaseService.find(containerId, databaseId)) - .thenReturn(database); - when(tableService.find(containerId, databaseId, tableId)) - .thenReturn(table); - when(accessService.find(databaseId, username)) - .thenReturn(access); + when(databaseService.find(containerId, databaseId)).thenReturn(database); + when(tableService.find(containerId, databaseId, tableId)).thenReturn(table); + when(accessService.find(databaseId, username)).thenReturn(access); /* test */ final ResponseEntity<?> response = dataEndpoint.insert(containerId, databaseId, tableId, data, principal); @@ -558,23 +377,17 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void generic_getAll(Long containerId, Long databaseId, Long tableId, Database database, Table table, String username, DatabaseAccess access, Principal principal, Instant timestamp, - Long page, Long size, SortType sortDirection, String sortColumn) - throws UserNotFoundException, TableMalformedException, NotAllowedException, PaginationException, - TableNotFoundException, QueryStoreException, SortException, DatabaseConnectionException, - QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException { + Long page, Long size, SortType sortDirection, String sortColumn) throws UserNotFoundException, TableMalformedException, NotAllowedException, PaginationException, TableNotFoundException, QueryStoreException, SortException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException { /* mock */ - when(databaseService.find(containerId, databaseId)) - .thenReturn(database); - when(tableService.find(containerId, databaseId, tableId)) - .thenReturn(table); - when(accessService.find(databaseId, username)) - .thenReturn(access); - when(queryService.findAll(containerId, databaseId, tableId, timestamp, page, size, principal)) - .thenReturn(QUERY_1_RESULT_DTO); + when(databaseService.find(containerId, databaseId)).thenReturn(database); + when(tableService.find(containerId, databaseId, tableId)).thenReturn(table); + when(accessService.find(databaseId, username)).thenReturn(access); + when(queryService.tableFindAll(containerId, databaseId, tableId, timestamp, page, size, principal)).thenReturn(QUERY_1_RESULT_DTO); /* test */ - final ResponseEntity<QueryResultDto> response = dataEndpoint.getAll(containerId, databaseId, tableId, principal, timestamp, page, size, sortDirection, sortColumn); + final ResponseEntity<QueryResultDto> response = dataEndpoint.getAll(containerId, databaseId, tableId, + principal, timestamp, page, size, sortDirection, sortColumn); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); assertEquals(QUERY_1_RESULT_ID, response.getBody().getId()); @@ -582,4 +395,21 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { assertEquals(QUERY_1_RESULT_RESULT, response.getBody().getResult()); } + public void generic_getCount(Long containerId, Long databaseId, Long tableId, Database database, Table table, + String username, DatabaseAccess access, Principal principal, Instant timestamp) throws UserNotFoundException, TableMalformedException, NotAllowedException, PaginationException, TableNotFoundException, QueryStoreException, SortException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException { + + /* mock */ + when(databaseService.find(containerId, databaseId)).thenReturn(database); + when(tableService.find(containerId, databaseId, tableId)).thenReturn(table); + when(accessService.find(databaseId, username)).thenReturn(access); + when(queryService.tableCount(containerId, databaseId, tableId, timestamp, principal)).thenReturn(QUERY_1_RESULT_NUMBER); + + /* test */ + final ResponseEntity<Long> response = dataEndpoint.getCount(containerId, databaseId, tableId, + principal, timestamp); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + assertEquals(QUERY_1_RESULT_NUMBER, response.getBody()); + } + } diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/TableHistoryEndpointUnitTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/TableHistoryEndpointUnitTest.java index a06089955e982a0ae2862c283330e95b001e07d1..9a649b2e39f4a8978ebdf5b490f8aa70eab8c11d 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/TableHistoryEndpointUnitTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/TableHistoryEndpointUnitTest.java @@ -9,6 +9,8 @@ import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.database.table.Table; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.jpa.DatabaseAccessRepository; import at.tuwien.service.DatabaseService; @@ -48,9 +50,14 @@ public class TableHistoryEndpointUnitTest extends BaseUnitTest { @MockBean private Channel channel; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private QueryService queryService; diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java index 379a1ea8d90083ec46fdaf8f20a9f70efc309db3..5cf4aadd98085ace3f9bfe569c5d9e0a063c9103 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/endpoint/ViewEndpointUnitTest.java @@ -11,6 +11,8 @@ import at.tuwien.config.ReadyConfig; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.service.AccessService; import at.tuwien.service.DatabaseService; @@ -50,9 +52,14 @@ public class ViewEndpointUnitTest extends BaseUnitTest { @MockBean private IndexConfig indexInitializer; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private QueryService queryService; @@ -314,7 +321,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_publicAnonymous_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_1_ID, DATABASE_1_ID, VIEW_1_ID, DATABASE_1, null, null, null); @@ -325,7 +332,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_publicAnonymous2_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_1_ID, DATABASE_1_ID, VIEW_1_ID, DATABASE_1, null, null, null); @@ -336,7 +343,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_publicRead_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_1_ID, DATABASE_1_ID, VIEW_1_ID, DATABASE_1, USER_2_USERNAME, USER_2_PRINCIPAL, DATABASE_1_READ_ACCESS); @@ -347,7 +354,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_publicWriteOwn_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_1_ID, DATABASE_1_ID, VIEW_1_ID, DATABASE_1, USER_2_USERNAME, USER_2_PRINCIPAL, DATABASE_1_WRITE_OWN_ACCESS); @@ -358,7 +365,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_publicWriteAll_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_1_ID, DATABASE_1_ID, VIEW_1_ID, DATABASE_1, USER_2_USERNAME, USER_2_PRINCIPAL, DATABASE_1_WRITE_ALL_ACCESS); @@ -369,7 +376,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_publicOwner_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_1_ID, DATABASE_1_ID, VIEW_1_ID, DATABASE_1, USER_1_USERNAME, USER_1_PRINCIPAL, DATABASE_1_WRITE_ALL_ACCESS); @@ -380,7 +387,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_privateResearcher_succeeds() throws UserNotFoundException, QueryStoreException, NotAllowedException, DatabaseConnectionException, TableMalformedException, QueryMalformedException, ColumnParseException, DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException, - PaginationException, ViewNotFoundException { + PaginationException, ViewNotFoundException, ViewMalformedException { /* test */ data_generic(CONTAINER_2_ID, DATABASE_2_ID, VIEW_4_ID, DATABASE_2, USER_1_USERNAME, USER_1_PRINCIPAL, null); @@ -563,7 +570,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_privateAnonymous_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_2_ID, DATABASE_2_ID, VIEW_1_ID, DATABASE_2, null, null, null); @@ -573,7 +580,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_privateRead_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_2_ID, DATABASE_2_ID, VIEW_1_ID, DATABASE_2, USER_2_USERNAME, USER_2_PRINCIPAL, DATABASE_2_READ_ACCESS); @@ -583,7 +590,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_privateWriteOwn_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_2_ID, DATABASE_2_ID, VIEW_1_ID, DATABASE_2, USER_2_USERNAME, USER_2_PRINCIPAL, DATABASE_2_WRITE_OWN_ACCESS); @@ -593,7 +600,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_privateWriteAll_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_2_ID, DATABASE_2_ID, VIEW_1_ID, DATABASE_2, USER_2_USERNAME, USER_2_PRINCIPAL, DATABASE_2_WRITE_ALL_ACCESS); @@ -603,7 +610,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void data_privateOwner_succeeds() throws UserNotFoundException, NotAllowedException, DatabaseNotFoundException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, ImageNotSupportedException, - ContainerNotFoundException, PaginationException { + ContainerNotFoundException, PaginationException, ViewMalformedException { /* test */ data_generic(CONTAINER_2_ID, DATABASE_2_ID, VIEW_1_ID, DATABASE_2, USER_1_USERNAME, USER_1_PRINCIPAL, DATABASE_2_WRITE_ALL_ACCESS); @@ -735,7 +742,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { Principal principal, DatabaseAccess access) throws DatabaseNotFoundException, UserNotFoundException, NotAllowedException, ViewNotFoundException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, TableMalformedException, ColumnParseException, - ImageNotSupportedException, ContainerNotFoundException, PaginationException { + ImageNotSupportedException, ContainerNotFoundException, PaginationException, ViewMalformedException { final ExecuteStatementDto statement = ExecuteStatementDto.builder() .statement(VIEW_1_QUERY) .build(); @@ -756,7 +763,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { } when(viewService.findById(databaseId, viewId, principal)) .thenReturn(VIEW_1); - when(queryService.execute(containerId, databaseId, statement, principal, page, size, null, null)) + when(queryService.viewFindAll(containerId, databaseId, VIEW_1, page, size, principal)) .thenReturn(QUERY_1_RESULT_DTO); /* test */ diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/listener/RabbitMqListenerIntegrationTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/listener/RabbitMqListenerIntegrationTest.java deleted file mode 100644 index 1ddaac142af69e4b7dfab255160e6cd6a1f87618..0000000000000000000000000000000000000000 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/listener/RabbitMqListenerIntegrationTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package at.tuwien.listener; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.amqp.ConsumerDto; -import at.tuwien.config.*; -import at.tuwien.exception.*; -import at.tuwien.gateway.BrokerServiceGateway; -import at.tuwien.listener.impl.RabbitMqListenerImpl; -import at.tuwien.repository.jpa.*; -import com.rabbitmq.client.BuiltinExchangeType; -import com.rabbitmq.client.Channel; -import lombok.extern.log4j.Log4j2; -import org.junit.Rule; -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.rules.Timeout; -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.annotation.DirtiesContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.io.IOException; -import java.util.*; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; - - -@Log4j2 -@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class RabbitMqListenerIntegrationTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - @MockBean - private IndexConfig indexConfig; - - @MockBean - private BrokerServiceGateway brokerServiceGateway; - - @Autowired - private Channel channel; - - @Autowired - private ImageRepository imageRepository; - - @Autowired - private ContainerRepository containerRepository; - - @Autowired - private DatabaseRepository databaseRepository; - - @Autowired - private TableRepository tableRepository; - - @Autowired - private RabbitMqListenerImpl rabbitMqListener; - - @Autowired - private TableColumnRepository tableColumnRepository; - - @Autowired - private H2Utils h2Utils; - - @Autowired - private RabbitMqConfig rabbitMqConfig; - - @Autowired - private AmqpConfig amqpConfig; - - @Rule - public Timeout globalTimeout = Timeout.seconds(60); - - @BeforeAll - public static void beforeAll() throws InterruptedException { - afterAll(); - DockerConfig.createAllNetworks(); - DockerConfig.createContainer(null, CONTAINER_BROKER, 15672, CONTAINER_BROKER_ENV); - DockerConfig.startContainer(CONTAINER_BROKER); - } - - @AfterAll - public static void afterAll() { - DockerConfig.removeAllContainers(); - DockerConfig.removeAllNetworks(); - } - - @BeforeEach - public void beforeEach() { - /* metadata database */ - h2Utils.runScript("schema.sql"); - imageRepository.save(IMAGE_1); - containerRepository.save(CONTAINER_1); - DATABASE_1.setTables(List.of()); - databaseRepository.save(DATABASE_1); - DATABASE_1.setTables(List.of(TABLE_1, TABLE_2, TABLE_3)); - tableRepository.save(TABLE_1_NOCOLS); - tableRepository.save(TABLE_2_NOCOLS); - tableRepository.save(TABLE_3_NOCOLS); - } - - @Test - public void updateConsumers_succeeds() throws AmqpException, IOException, InterruptedException { - - /* mock */ - channel.exchangeDeclare(DATABASE_1_EXCHANGE, BuiltinExchangeType.FANOUT); - channel.queueDeclare(TABLE_1_QUEUE_NAME, true, false, false, null); - channel.queueBind(TABLE_1_QUEUE_NAME, DATABASE_1_EXCHANGE, TABLE_1_ROUTING_KEY); - channel.queueDeclare(TABLE_2_QUEUE_NAME, true, false, false, null); - channel.queueBind(TABLE_2_QUEUE_NAME, DATABASE_1_EXCHANGE, TABLE_2_ROUTING_KEY); - channel.queueDeclare(TABLE_3_QUEUE_NAME, true, false, false, null); - channel.queueBind(TABLE_3_QUEUE_NAME, DATABASE_1_EXCHANGE, TABLE_3_ROUTING_KEY); - when(brokerServiceGateway.findAllConsumers()) - .thenReturn(List.of()); - - /* pre-condition */ - assertEquals(0, rabbitMqConfig.findAllConsumers().size()); - assertEquals(2, amqpConfig.getAmqpConsumers()); - - /* test */ - rabbitMqListener.updateConsumers(); - Thread.sleep(10 * 1000); - final List<ConsumerDto> response = rabbitMqConfig.findAllConsumers(); - assertEquals(6, response.size()); - assertEquals(2, (int) response.stream().filter(c -> c.getQueue().getName().equals(TABLE_1_QUEUE_NAME)).count()); - assertEquals(2, (int) response.stream().filter(c -> c.getQueue().getName().equals(TABLE_2_QUEUE_NAME)).count()); - assertEquals(2, (int) response.stream().filter(c -> c.getQueue().getName().equals(TABLE_3_QUEUE_NAME)).count()); - } - -} diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/repository/ViewIdxRepositoryIntegrationTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/repository/ViewIdxRepositoryIntegrationTest.java index d8d59487f6392de975b5fc300306202c604da683..bd9644c6f5f834ccaea2249225afde6e27ec392a 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/repository/ViewIdxRepositoryIntegrationTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/repository/ViewIdxRepositoryIntegrationTest.java @@ -7,6 +7,8 @@ import at.tuwien.config.DockerConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.entities.database.View; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.elastic.ViewIdxRepository; import at.tuwien.repository.jpa.*; @@ -16,6 +18,7 @@ import lombok.extern.log4j.Log4j2; import org.junit.Rule; import org.junit.jupiter.api.AfterAll; 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.junit.rules.Timeout; @@ -45,9 +48,14 @@ public class ViewIdxRepositoryIntegrationTest extends BaseUnitTest { @MockBean private Channel channel; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private UserRepository userRepository; @@ -87,6 +95,12 @@ public class ViewIdxRepositoryIntegrationTest extends BaseUnitTest { DockerConfig.removeAllNetworks(); } + @BeforeEach + public void beforeEach() { + DATABASE_1.setTables(List.of(TABLE_1, TABLE_2, TABLE_3, TABLE_7)); + DATABASE_1.setViews(List.of(VIEW_1)); + } + @Test public void save_succeeds() throws UserNotFoundException, DatabaseConnectionException, ViewMalformedException, QueryMalformedException, DatabaseNotFoundException { diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/repository/ViewRepositoryIntegrationTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/repository/ViewRepositoryIntegrationTest.java index 4000a1559936d4cdfc754f3707f21cc9f439c85e..eddcffdc8e9e12dc638394e60ef7849286f684c0 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/repository/ViewRepositoryIntegrationTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/repository/ViewRepositoryIntegrationTest.java @@ -5,6 +5,8 @@ import at.tuwien.config.H2Utils; import at.tuwien.config.IndexConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.entities.database.View; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.jpa.*; import com.rabbitmq.client.Channel; @@ -36,9 +38,14 @@ public class ViewRepositoryIntegrationTest extends BaseUnitTest { @MockBean private Channel channel; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private IndexConfig indexConfig; diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java index 29d3e0f2d8ebf9c89d4c6ff2c234f7a0048dd473..ba53952bab69043e081cd65e2cd7c7d8d1a9c62f 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java @@ -5,6 +5,8 @@ import at.tuwien.config.IndexConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.entities.container.Container; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.jpa.*; import com.rabbitmq.client.Channel; @@ -35,9 +37,14 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { @MockBean private Channel channel; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @Autowired private ImageRepository imageRepository; diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java index c5809d8aa80f3b8e83bfda2eaf6e9fe2d2ecb4f6..ff2b07d0c10f0b07052c128bea344c24a1f53c72 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java @@ -10,6 +10,8 @@ import at.tuwien.config.IndexConfig; import at.tuwien.config.MariaDbConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.querystore.Query; import at.tuwien.repository.jpa.*; @@ -56,9 +58,14 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { @MockBean private IndexConfig indexInitializer; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private DatabaseRepository databaseRepository; @@ -79,12 +86,10 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { private final static String BIND_ZOO = new File("./src/test/resources/zoo").toPath().toAbsolutePath() + ":/docker-entrypoint-initdb.d"; @BeforeAll - public static void beforeAll() throws InterruptedException { + public static void beforeAll() { afterAll(); /* create network */ DockerConfig.createAllNetworks(); - DockerConfig.createContainer(null, CONTAINER_BROKER, 15672, CONTAINER_BROKER_ENV); - DockerConfig.startContainer(CONTAINER_BROKER); } @AfterAll @@ -133,7 +138,7 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { .thenReturn(Optional.of(TABLE_1)); /* test */ - final QueryResultDto result = queryService.findAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, Instant.now(), + final QueryResultDto result = queryService.tableFindAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, Instant.now(), null, null, USER_1_PRINCIPAL); assertEquals(3, result.getResult().size()); assertEquals(BigInteger.valueOf(1L), result.getResult().get(0).get(COLUMN_1_1_INTERNAL_NAME)); @@ -169,7 +174,7 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { .thenReturn(Optional.of(TABLE_1)); /* test */ - queryService.findAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, Instant.now(), page, size, USER_1_PRINCIPAL); + queryService.tableFindAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, Instant.now(), page, size, USER_1_PRINCIPAL); } @Test @@ -187,24 +192,7 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - queryService.findAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, Instant.now(), page, size, USER_1_PRINCIPAL); - }); - } - - @Test - public void selectAll_noDatabase_fails() throws InterruptedException { - final Long page = 0L; - final Long size = 10L; - - /* mock */ - DockerConfig.createContainer(BIND_WEATHER, CONTAINER_1, CONTAINER_1_ENV); - DockerConfig.startContainer(CONTAINER_1); - when(databaseRepository.findByContainerIdAndDatabaseId(CONTAINER_1_ID, DATABASE_1_ID)) - .thenReturn(Optional.empty()); - - /* test */ - assertThrows(DatabaseNotFoundException.class, () -> { - queryService.findAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, Instant.now(), page, size, USER_1_PRINCIPAL); + queryService.tableFindAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, Instant.now(), page, size, USER_1_PRINCIPAL); }); } @@ -374,7 +362,7 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { .thenReturn(Optional.of(TABLE_1)); /* test */ - queryService.findAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, null, null, null, USER_1_PRINCIPAL); + queryService.tableFindAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, null, null, null, USER_1_PRINCIPAL); } @Test @@ -392,8 +380,8 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { .thenReturn(Optional.of(TABLE_1)); /* test */ - queryService.findAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, timestamp, null, null, USER_1_PRINCIPAL); - queryService.findAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, timestamp, null, null, USER_1_PRINCIPAL); + queryService.tableFindAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, timestamp, null, null, USER_1_PRINCIPAL); + queryService.tableFindAll(CONTAINER_1_ID, DATABASE_1_ID, TABLE_1_ID, timestamp, null, null, USER_1_PRINCIPAL); } @Test @@ -526,7 +514,9 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { .query("SELECT `location`, `lat`, `lng` FROM `weather_location` WHERE `location` = \"Vienna\"") .queryHash(QUERY_1_QUERY_HASH) .resultHash(null) + .resultNumber(0L) .created(QUERY_1_CREATED) + .executed(QUERY_1_EXECUTION) .createdBy(USER_1_USERNAME) .isPersisted(true) .build(); diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java index 0fe3b659ef0ec5d9e7ceee04e6e7344a021f5f64..eed98297653d5f527f7a4604b00a68b84bff61e6 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java @@ -2,22 +2,19 @@ package at.tuwien.service; import at.tuwien.BaseUnitTest; import at.tuwien.amqp.RabbitMqConsumer; +import at.tuwien.api.amqp.ConsumerDto; import at.tuwien.api.database.table.TableCsvDto; -import at.tuwien.config.AmqpConfig; -import at.tuwien.config.DockerConfig; -import at.tuwien.config.IndexConfig; -import at.tuwien.config.ReadyConfig; +import at.tuwien.config.*; import at.tuwien.exception.AmqpException; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.jpa.DatabaseRepository; import at.tuwien.repository.jpa.TableRepository; import com.fasterxml.jackson.databind.ObjectMapper; import com.rabbitmq.client.*; import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -27,9 +24,12 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import java.io.File; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Optional; import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -53,15 +53,23 @@ public class QueueServiceIntegrationTest extends BaseUnitTest { @MockBean private IndexConfig indexInitializer; + @MockBean + private RabbitMqConsumer rabbitMqConsumer; + + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ @MockBean - private RabbitMqConsumer rabbitMqConsumer; + private BrokerServiceGateway brokerServiceGateway; @Autowired private AmqpConfig amqpConfig; + @Autowired + private RabbitMqConfig rabbitMqConfig; + @Autowired private Channel channel; @@ -79,7 +87,7 @@ public class QueueServiceIntegrationTest extends BaseUnitTest { DockerConfig.createAllNetworks(); DockerConfig.createContainer(BIND, CONTAINER_1, CONTAINER_1_ENV); DockerConfig.startContainer(CONTAINER_1); - DockerConfig.createContainer(null, CONTAINER_BROKER, CONTAINER_BROKER_ENV); + DockerConfig.createContainer(null, CONTAINER_BROKER, 15672, CONTAINER_BROKER_ENV); DockerConfig.startContainer(CONTAINER_BROKER); } @@ -187,4 +195,18 @@ public class QueueServiceIntegrationTest extends BaseUnitTest { channel.basicPublish(DATABASE_1_EXCHANGE, TABLE_1_ROUTING_KEY, basicProperties, objectMapper.writeValueAsBytes(TABLE_1_CSV_DTO)); } + @Test + @Disabled("Not testable") + public void restore_succeeds() throws AmqpException, IOException { + + /* mock */ + when(tableRepository.findAll()) + .thenReturn(List.of(TABLE_1)); + + /* test */ + messageQueueService.restore(); + final List<ConsumerDto> response = rabbitMqConfig.findAllConsumers(); + assertEquals(amqpConfig.getAmqpConsumers(), (int) response.stream().filter(c -> c.getQueue().getName().equals(TABLE_1_QUEUE_NAME)).count()); + } + } diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/service/StoreServiceIntegrationTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/service/StoreServiceIntegrationTest.java index 17e9cb979bc2eaecb2480ba558bbafb540b219ef..eb694845ebf851da701cce168c4f36a7178b1ae2 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/service/StoreServiceIntegrationTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/service/StoreServiceIntegrationTest.java @@ -9,6 +9,8 @@ import at.tuwien.config.MariaDbConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.exception.*; import at.tuwien.entities.database.Database; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.querystore.Query; import at.tuwien.repository.jpa.*; @@ -29,6 +31,7 @@ import java.sql.SQLException; import java.util.List; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Map; import java.util.Optional; import static java.time.temporal.ChronoUnit.HOURS; @@ -51,9 +54,14 @@ public class StoreServiceIntegrationTest extends BaseUnitTest { @MockBean private Channel channel; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private TableRepository tableRepository; @@ -93,9 +101,11 @@ public class StoreServiceIntegrationTest extends BaseUnitTest { } @BeforeEach - public void beforeEach() throws InterruptedException, SQLException { + public void beforeEach() throws InterruptedException { afterEach(); + /* create networks */ DockerConfig.createAllNetworks(); + /* create containers */ DockerConfig.createContainer(BIND, CONTAINER_1, CONTAINER_1_ENV); DockerConfig.startContainer(CONTAINER_1); /* metadata database */ @@ -136,6 +146,7 @@ public class StoreServiceIntegrationTest extends BaseUnitTest { when(databaseRepository.findByContainerIdAndDatabaseId(CONTAINER_1_ID, DATABASE_1_ID)) .thenReturn(Optional.of(DATABASE_1)); MariaDbConfig.insertQueryStore(CONTAINER_1_INTERNALNAME, DATABASE_1_INTERNALNAME, QUERY_1, USER_1_USERNAME); + MariaDbConfig.insertQueryStore(CONTAINER_1_INTERNALNAME, DATABASE_1_INTERNALNAME, QUERY_2, USER_1_USERNAME); /* test */ final List<Query> queries = storeService.findAll(CONTAINER_1_ID, DATABASE_1_ID, true, USER_1_PRINCIPAL); @@ -366,7 +377,7 @@ public class StoreServiceIntegrationTest extends BaseUnitTest { } @Test - public void findOne_notFound_fails() { + public void findOne_notFound_fails() throws SQLException { final Principal principal = new BasicUserPrincipal(USER_1_USERNAME); /* mock */ @@ -375,8 +386,23 @@ public class StoreServiceIntegrationTest extends BaseUnitTest { /* test */ assertThrows(QueryNotFoundException.class, () -> { - storeService.findOne(CONTAINER_1_ID, DATABASE_1_ID, QUERY_2_ID, principal); + storeService.findOne(CONTAINER_1_ID, DATABASE_1_ID, 9999L, principal); }); } + @Test + public void deleteStaleQueries_succeeds() throws QueryStoreException, ImageNotSupportedException, SQLException { + + /* mock */ + MariaDbConfig.insertQueryStore(CONTAINER_1_INTERNALNAME, DATABASE_1_INTERNALNAME, QUERY_1, USER_1_USERNAME); + MariaDbConfig.insertQueryStore(CONTAINER_1_INTERNALNAME, DATABASE_1_INTERNALNAME, QUERY_2, USER_1_USERNAME); + when(databaseRepository.findAll()) + .thenReturn(List.of(DATABASE_1)); + + /* test */ + storeService.deleteStaleQueries(); + final List<Map<String, Object>> response = MariaDbConfig.listQueryStore(CONTAINER_1_INTERNALNAME, DATABASE_1_INTERNALNAME); + assertEquals(1, response.size()); + } + } diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java index 14b294520f0cf4d6ba88f1d45f87b36eb47744d4..58f28e951351115b091d8a8240a6bf75e993638f 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java @@ -8,6 +8,8 @@ import at.tuwien.config.IndexConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.entities.database.table.columns.TableColumn; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.jpa.ContainerRepository; import at.tuwien.repository.jpa.DatabaseRepository; @@ -50,9 +52,14 @@ public class TableServiceIntegrationTest extends BaseUnitTest { @MockBean private Channel channel; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private TableRepository tableRepository; @@ -74,9 +81,13 @@ public class TableServiceIntegrationTest extends BaseUnitTest { final static String BIND = new File("./src/test/resources/weather").toPath().toAbsolutePath() + ":/docker-entrypoint-initdb.d"; @BeforeAll - public static void beforeAll() { + public static void beforeAll() throws InterruptedException { afterAll(); + /* create networks */ DockerConfig.createAllNetworks(); + /* start containers */ + DockerConfig.createContainer(BIND, CONTAINER_1, CONTAINER_1_ENV); + DockerConfig.startContainer(CONTAINER_1); } @AfterAll @@ -86,22 +97,20 @@ public class TableServiceIntegrationTest extends BaseUnitTest { } @BeforeEach - public void beforeEach() throws InterruptedException { - afterEach(); - DockerConfig.createContainer(BIND, CONTAINER_1, CONTAINER_1_ENV); - DockerConfig.startContainer(CONTAINER_1); - h2Utils.runScript("schema.sql"); + public void beforeEach() { /* metadata db */ + h2Utils.runScript("schema.sql"); imageRepository.save(IMAGE_1); containerRepository.save(CONTAINER_1); + tableRepository.save(TABLE_1_NOCOLS); + tableRepository.save(TABLE_2_NOCOLS); + tableRepository.save(TABLE_3_NOCOLS); + tableRepository.save(TABLE_7_NOCOLS); + DATABASE_1.setTables(List.of()); + DATABASE_1.setViews(List.of()); databaseRepository.save(DATABASE_1); } - @AfterEach - public void afterEach() { - DockerConfig.removeAllContainers(); - } - @Test public void findHistory_anonymous_succeeds() throws UserNotFoundException, TableNotFoundException, QueryStoreException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException { diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java index 69ce154d9fa9b65765110ba1a27d9468903f3df7..4a523c0a2325d56636b7519368b06a0027e39f25 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java @@ -5,6 +5,8 @@ import at.tuwien.config.IndexConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.entities.database.table.columns.TableColumn; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; +import at.tuwien.listener.MessageQueueListener; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.jpa.*; import com.rabbitmq.client.Channel; @@ -39,9 +41,14 @@ public class TableServiceUnitTest extends BaseUnitTest { @MockBean private IndexConfig indexInitializer; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @Autowired private TableService tableService; diff --git a/fda-query-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java b/fda-query-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java index d827ec5bfa4e153f6c22f27e0bb77ee7702ab329..29e074c8f21a6dd864d6e3c1a3504a588c6d0163 100644 --- a/fda-query-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java +++ b/fda-query-service/rest-service/src/test/java/at/tuwien/service/ViewServiceIntegrationTest.java @@ -9,17 +9,15 @@ import at.tuwien.config.MariaDbConfig; import at.tuwien.config.ReadyConfig; import at.tuwien.entities.database.View; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; import at.tuwien.listener.impl.RabbitMqListenerImpl; import at.tuwien.repository.elastic.ViewIdxRepository; import at.tuwien.repository.jpa.*; -import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.api.exception.NotModifiedException; -import com.github.dockerjava.api.model.Bind; -import com.github.dockerjava.api.model.Network; import com.rabbitmq.client.Channel; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.AfterAll; 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; @@ -30,12 +28,10 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import java.io.File; import java.sql.SQLException; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; -import static at.tuwien.config.DockerConfig.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; @@ -60,9 +56,14 @@ public class ViewServiceIntegrationTest extends BaseUnitTest { @MockBean private ViewIdxRepository viewIdxRepository; + /* keep */ @MockBean private RabbitMqListenerImpl rabbitMqListener; + /* keep */ + @MockBean + private BrokerServiceGateway brokerServiceGateway; + @MockBean private DatabaseRepository databaseRepository; @@ -93,13 +94,19 @@ public class ViewServiceIntegrationTest extends BaseUnitTest { DockerConfig.removeAllNetworks(); } + @BeforeEach + public void beforeEach() { + DATABASE_1.setTables(List.of(TABLE_1, TABLE_2, TABLE_3, TABLE_7)); + DATABASE_1.setViews(List.of(VIEW_2, VIEW_3)); + } + @Test public void create_viewJoinOnView_succeeds() throws DatabaseNotFoundException, UserNotFoundException, DatabaseConnectionException, ViewMalformedException, QueryMalformedException, SQLException { final ViewCreateDto request = ViewCreateDto.builder() - .name(VIEW_3_NAME) + .name("Debug") .query(VIEW_3_QUERY) - .isPublic(VIEW_3_PUBLIC) + .isPublic(true) .build(); /* mock */ @@ -119,7 +126,7 @@ public class ViewServiceIntegrationTest extends BaseUnitTest { assertEquals(VIEW_3_INTERNAL_NAME, response.getInternalName()); assertEquals(VIEW_3_QUERY, response.getQuery()); final List<Map<String, String>> resultSet = MariaDbConfig.selectQuery(CONTAINER_1_INTERNALNAME, DATABASE_1_INTERNALNAME, - "SELECT j.* FROM `junit3` j", "mintemp", "rainfall", "location", "lat", "lng"); + "SELECT j.* FROM `debug` j", "mintemp", "rainfall", "location", "lat", "lng"); assertEquals("13.4", resultSet.get(0).get("mintemp")); assertEquals("0.6", resultSet.get(0).get("rainfall")); assertEquals("Albury", resultSet.get(0).get("location")); diff --git a/fda-query-service/rest-service/src/test/resources/application.properties b/fda-query-service/rest-service/src/test/resources/application.properties index aac636db158551adcc6c17b6dc229f68a96bdd69..4eea490a9199d7e0155b1f33cf3500a5e1f2863f 100644 --- a/fda-query-service/rest-service/src/test/resources/application.properties +++ b/fda-query-service/rest-service/src/test/resources/application.properties @@ -19,12 +19,14 @@ spring.jpa.show-sql=false # additional logging logging.level.root=error -logging.level.at.tuwien.=info +logging.level.at.tuwien.=trace # broker service -spring.rabbitmq.host=broker-service +spring.rabbitmq.host=dbrepo-broker-service spring.rabbitmq.username=guest spring.rabbitmq.password=guest # search service -fda.elastic.endpoint=search-mock-service:9200 \ No newline at end of file +fda.consumers=2 +fda.gateway.endpoint: http://localhost:15672 +fda.elastic.endpoint=dbrepo-search-service:9200 \ No newline at end of file diff --git a/fda-query-service/rest-service/src/test/resources/weather/2_weather.sql b/fda-query-service/rest-service/src/test/resources/weather/2_weather.sql index 3d352607f1c3cacbbd80a7f18df7ad5881c664a2..29967f2977ef6b6f12da00b0c218382e5c024322 100644 --- a/fda-query-service/rest-service/src/test/resources/weather/2_weather.sql +++ b/fda-query-service/rest-service/src/test/resources/weather/2_weather.sql @@ -39,7 +39,7 @@ VALUES (1, '2008-12-01', 'Albury', 13.4, 0.6), ## TEST CASE PRE-REQUISITE ## ######################################################################################################################## -CREATE VIEW mock_view AS +CREATE VIEW junit2 AS ( SELECT `location`, `lat`, `lng` FROM `weather_location` diff --git a/fda-query-service/rest-service/src/test/resources/webserver/weather_aus.csv b/fda-query-service/rest-service/src/test/resources/webserver/weather_aus.csv deleted file mode 100644 index 9bfb33891d9c10db7cd04ba1cf8d32ddc1dcde96..0000000000000000000000000000000000000000 --- a/fda-query-service/rest-service/src/test/resources/webserver/weather_aus.csv +++ /dev/null @@ -1,3 +0,0 @@ -1,2008-12-01,Albury,13.4,0.6 -2,2008-12-02,Albury,7.4,0 -3,2008-12-03,Albury,12.9,0 \ No newline at end of file diff --git a/fda-query-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java b/fda-query-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java index 8a5bc1c3f10f354b0cf1bd850ed208419bb147e3..e8f0b9c364dd7c2166cc605c71572c8efc305e6f 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java +++ b/fda-query-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java @@ -5,5 +5,6 @@ import at.tuwien.api.amqp.ConsumerDto; import java.util.List; public interface BrokerServiceGateway { + List<ConsumerDto> findAllConsumers(); } diff --git a/fda-query-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java b/fda-query-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java index 16afe426ad48f3bc7733fd86f1140e886319261f..4629fbe4ccb3c567001e215a25d0c1182d78c472 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java @@ -7,7 +7,9 @@ import at.tuwien.gateway.BrokerServiceGateway; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.env.Environment; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -17,6 +19,7 @@ import org.springframework.web.client.RestTemplate; import java.net.URI; import java.nio.charset.Charset; +import java.util.Arrays; import java.util.List; @Slf4j @@ -26,20 +29,30 @@ public class BrokerServiceGatewayImpl implements BrokerServiceGateway { private final AmqpConfig amqpConfig; private final RestTemplate restTemplate; private final GatewayConfig gatewayConfig; + private final Environment environment; private final static String VIRTUAL_SERVER = "%2F"; @Autowired - public BrokerServiceGatewayImpl(AmqpConfig amqpConfig, RestTemplate restTemplate, GatewayConfig gatewayConfig) { + public BrokerServiceGatewayImpl(AmqpConfig amqpConfig, RestTemplate restTemplate, GatewayConfig gatewayConfig, + Environment environment) { this.amqpConfig = amqpConfig; this.restTemplate = restTemplate; this.gatewayConfig = gatewayConfig; + this.environment = environment; } @Override public List<ConsumerDto> findAllConsumers() { + final StringBuilder urlBuilder = new StringBuilder(gatewayConfig.getGatewayEndpoint()) + .append("/api"); + if (Arrays.stream(environment.getActiveProfiles()).noneMatch(p -> p.equals("junit"))) { + urlBuilder.append("/broker"); + } + urlBuilder.append("/consumers/") + .append(VIRTUAL_SERVER); log.trace("gateway broker find all consumers, virtual server={}", VIRTUAL_SERVER); - final URI findUri = URI.create(gatewayConfig.getGatewayEndpoint() + "/api/broker/consumers/" + VIRTUAL_SERVER); + final URI findUri = URI.create(urlBuilder.toString()); final ResponseEntity<List<ConsumerDto>> response = restTemplate.exchange(findUri, HttpMethod.GET, new HttpEntity<>(null, getHeaders()), new ParameterizedTypeReference<>() { }); diff --git a/fda-query-service/services/src/main/java/at/tuwien/listener/DatabaseListener.java b/fda-query-service/services/src/main/java/at/tuwien/listener/DatabaseListener.java new file mode 100644 index 0000000000000000000000000000000000000000..10adef889b3ef66eb2f7c37b1a806d2d7f1ed9bb --- /dev/null +++ b/fda-query-service/services/src/main/java/at/tuwien/listener/DatabaseListener.java @@ -0,0 +1,17 @@ +package at.tuwien.listener; + +import at.tuwien.exception.ImageNotSupportedException; +import at.tuwien.exception.QueryStoreException; +import org.springframework.scheduling.annotation.Scheduled; + +public interface DatabaseListener { + + /** + * Deletes stale queries that have not been persisted within 24 hours. + * + * @throws QueryStoreException The query store raised some exception. + * @throws ImageNotSupportedException The image is not supported by the service. + */ + @Scheduled + void deleteStaleQueries() throws QueryStoreException, ImageNotSupportedException; +} diff --git a/fda-query-service/services/src/main/java/at/tuwien/listener/MessageQueueListener.java b/fda-query-service/services/src/main/java/at/tuwien/listener/MessageQueueListener.java index ebdc5a473931d742f7199067a78468947ee4c597..4b8b1872272eb7375d408b1029fbf18c0a9be93d 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/listener/MessageQueueListener.java +++ b/fda-query-service/services/src/main/java/at/tuwien/listener/MessageQueueListener.java @@ -1,4 +1,15 @@ package at.tuwien.listener; +import at.tuwien.exception.AmqpException; +import org.springframework.scheduling.annotation.Scheduled; + public interface MessageQueueListener { + + /** + * Restores the consumers up to the configured limit. + * + * @throws AmqpException The consumer could not be created. + */ + @Scheduled(fixedDelay = 5000) + void updateConsumers() throws AmqpException; } diff --git a/fda-query-service/services/src/main/java/at/tuwien/listener/impl/MariadbListenerImpl.java b/fda-query-service/services/src/main/java/at/tuwien/listener/impl/MariadbListenerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..412b1f6ee5d6b7351678d8cb3154ed26dd2e4475 --- /dev/null +++ b/fda-query-service/services/src/main/java/at/tuwien/listener/impl/MariadbListenerImpl.java @@ -0,0 +1,29 @@ +package at.tuwien.listener.impl; + +import at.tuwien.exception.ImageNotSupportedException; +import at.tuwien.exception.QueryStoreException; +import at.tuwien.listener.DatabaseListener; +import at.tuwien.service.StoreService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class MariadbListenerImpl implements DatabaseListener { + + private final StoreService storeService; + + @Autowired + public MariadbListenerImpl(StoreService storeService) { + this.storeService = storeService; + } + + @Override + @Scheduled(cron = "0 0 2 * * *" /* at 2am, non-standard CRON syntax */) + @Transactional(readOnly = true) + public void deleteStaleQueries() throws QueryStoreException, ImageNotSupportedException { + storeService.deleteStaleQueries(); + } + +} diff --git a/fda-query-service/services/src/main/java/at/tuwien/listener/impl/RabbitMqListenerImpl.java b/fda-query-service/services/src/main/java/at/tuwien/listener/impl/RabbitMqListenerImpl.java index 1423461dd1d7d5a13a0b031e4ca49039283155a8..6e3670a70a74c12db7f2844968ad10272c682859 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/listener/impl/RabbitMqListenerImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/listener/impl/RabbitMqListenerImpl.java @@ -1,57 +1,28 @@ package at.tuwien.listener.impl; -import at.tuwien.api.amqp.ConsumerDto; -import at.tuwien.config.AmqpConfig; -import at.tuwien.entities.database.table.Table; import at.tuwien.exception.AmqpException; -import at.tuwien.gateway.BrokerServiceGateway; import at.tuwien.listener.MessageQueueListener; import at.tuwien.service.MessageQueueService; -import at.tuwien.service.TableService; import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.io.IOException; -import java.util.List; - @Log4j2 @Service public class RabbitMqListenerImpl implements MessageQueueListener { - private final AmqpConfig amqpConfig; - private final TableService tableService; private final MessageQueueService messageQueueService; - private final BrokerServiceGateway brokerServiceGateway; - @Autowired - public RabbitMqListenerImpl(AmqpConfig amqpConfig, TableService tableService, - MessageQueueService messageQueueService, BrokerServiceGateway brokerServiceGateway) { - this.amqpConfig = amqpConfig; - this.tableService = tableService; + public RabbitMqListenerImpl(MessageQueueService messageQueueService) { this.messageQueueService = messageQueueService; - this.brokerServiceGateway = brokerServiceGateway; } + @Override @Scheduled(fixedDelay = 5000) @Transactional(readOnly = true) public void updateConsumers() throws AmqpException { - final List<Table> tables = tableService.findAll(); - final List<ConsumerDto> consumers = brokerServiceGateway.findAllConsumers(); - for (Table table : tables) { - final long consumerCount = consumers.stream().filter(c -> c.getQueue().getName().equals(table.getQueueName())).count(); - if (consumerCount >= amqpConfig.getAmqpConsumers()) { - log.trace("listener table with name {} already has {} consumers (max. {})", table.getName(), - consumerCount, amqpConfig.getAmqpConsumers()); - continue; - } - log.debug("table with id {} has {} consumers, but needs {} in total", table.getId(), consumerCount, - amqpConfig.getAmqpConsumers()); - messageQueueService.createConsumer(table.getQueueName(), table.getDatabase().getContainer().getId(), - table.getDatabase().getId(), table.getId()); - } + messageQueueService.restore(); } } diff --git a/fda-query-service/services/src/main/java/at/tuwien/mapper/ContainerMapper.java b/fda-query-service/services/src/main/java/at/tuwien/mapper/ContainerMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..7c49211efb805355ea851c87dcbf340474f2d64f --- /dev/null +++ b/fda-query-service/services/src/main/java/at/tuwien/mapper/ContainerMapper.java @@ -0,0 +1,15 @@ +package at.tuwien.mapper; + +import at.tuwien.api.container.ContainerDto; +import at.tuwien.entities.container.Container; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface ContainerMapper { + + org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ContainerMapper.class); + + /* keep */ + ContainerDto containerToContainerDto(Container data); + +} diff --git a/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java b/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java index c3ccb209f52eec7b406fc461e189e194298d80fb..d85aaa7553b825ee12715bc4e8bb2a1176e865e1 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java +++ b/fda-query-service/services/src/main/java/at/tuwien/mapper/QueryMapper.java @@ -6,6 +6,7 @@ import at.tuwien.api.database.table.TableCsvDto; import at.tuwien.api.database.table.TableCsvUpdateDto; import at.tuwien.api.database.table.TableHistoryDto; import at.tuwien.entities.database.Database; +import at.tuwien.entities.database.View; import at.tuwien.entities.database.table.columns.TableColumnType; import at.tuwien.exception.QueryMalformedException; import at.tuwien.exception.QueryStoreException; @@ -542,16 +543,19 @@ public interface QueryMapper { statement.append(";"); try { final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); - log.trace("mapped update query {} to prepared statement {}", statement, pstmt); for (Map.Entry<String, Object> entry : data.getData().entrySet()) { if (entry.getValue() == null) { log.trace("entry is null, preparing null"); pstmt.setNull(i++, Types.NULL); + } else if (entry.getValue().equals(true) || entry.getValue().equals(false)) { + log.trace("entry is not null, preparing boolean"); + pstmt.setBoolean(i++, Boolean.parseBoolean(String.valueOf(entry.getValue()))); } else { log.trace("entry is not null, preparing string"); pstmt.setString(i++, String.valueOf(entry.getValue())); } } + log.trace("mapped update query {} to prepared statement {}", statement, pstmt); return pstmt; } catch (SQLException e) { log.error("failed to prepare statement {}, reason: {}", statement, e.getMessage()); @@ -570,8 +574,8 @@ public interface QueryMapper { return selection.toString(); } - default PreparedStatement tableToRawCountAllQuery(Connection connection, Table table, Instant timestamp) - throws ImageNotSupportedException, QueryMalformedException { + default String tableToRawCountAllQuery(Table table, Instant timestamp) + throws ImageNotSupportedException { log.trace("mapping table to raw count query, table={}, timestamp={}", table, timestamp); /* check image */ if (!table.getDatabase().getContainer().getImage().getRepository().equals("mariadb")) { @@ -582,25 +586,35 @@ public interface QueryMapper { log.trace("timestamp is null, setting it to now"); timestamp = Instant.now(); } + return columnsToRawCountAllQuery(table.getInternalName(), timestamp); + } + + default String viewToRawCountAllQuery(View view) + throws ImageNotSupportedException { + log.trace("mapping table to raw count query, view={}", view); + /* check image */ + if (!view.getDatabase().getContainer().getImage().getRepository().equals("mariadb")) { + log.error("Currently only MariaDB is supported"); + throw new ImageNotSupportedException("Image not supported."); + } + return columnsToRawCountAllQuery(view.getInternalName(), null); + } + + default String columnsToRawCountAllQuery(String tableName, Instant timestamp) { final StringBuilder statement = new StringBuilder("SELECT COUNT(*) FROM `") - .append(nameToInternalName(table.getName())) - .append("` FOR SYSTEM_TIME AS OF TIMESTAMP '") - .append(LocalDateTime.ofInstant(timestamp, ZoneId.of("UTC"))) - .append("';"); - try { - final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); - log.trace("mapped raw count query {} to prepared statement {}", statement, pstmt); - return pstmt; - } catch (SQLException e) { - log.error("Failed to prepare statement {}, reason: {}", statement, e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement", e); + .append(nameToInternalName(tableName)) + .append("`"); + if(timestamp != null) { + statement.append(" FOR SYSTEM_TIME AS OF TIMESTAMP '") + .append(LocalDateTime.ofInstant(timestamp, ZoneId.of("UTC"))) + .append("'"); } + statement.append(";"); + return statement.toString(); } - default PreparedStatement queryToRawTimestampedQuery(Connection connection, String query, Database database, - Instant timestamp, Boolean selection, Long page, Long size) - throws ImageNotSupportedException, - QueryMalformedException { + default String queryToRawTimestampedQuery(String query, Database database, Instant timestamp, Boolean selection, Long page, Long size) + throws ImageNotSupportedException, QueryMalformedException { log.trace("mapping query to timestamped query, query={}, database={}, timestamp={}, selection={}, page={}, size={}", query, database, timestamp, selection, page, size); /* param check */ @@ -670,17 +684,11 @@ public interface QueryMapper { + LocalDateTime.ofInstant(timestamp, ZoneId.of("UTC")) + "' "); } - try { - log.debug("mapped timestamped query: {}", statement); - return connection.prepareStatement(statement.toString()); - } catch (SQLException e) { - log.error("Failed to prepare statement {}, reason: {}", statement, e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement", e); - } + return statement.toString(); } - default PreparedStatement tableToRawFindAllQuery(Connection connection, Table table, Instant timestamp, Long size, Long page) - throws ImageNotSupportedException, QueryMalformedException { + default String tableToRawFindAllQuery(Table table, Instant timestamp, Long size, Long page) + throws ImageNotSupportedException { log.trace("mapping table to find all query, table={}, timestamp={}, size={}, page={}", table, timestamp, size, page); /* param check */ @@ -694,18 +702,35 @@ public interface QueryMapper { } else { log.trace("timestamp provided {}", timestamp); } + return columnsToRawFindAllQuery(table.getInternalName(), table.getColumns(), timestamp, size, page); + } + + default String viewToRawFindAllQuery(View view, Long size, Long page) + throws ImageNotSupportedException { + log.trace("mapping view to find all query, view={}, size={}, page={}", view, size, page); + /* param check */ + if (!view.getDatabase().getContainer().getImage().getRepository().equals("mariadb")) { + log.error("Currently only MariaDB is supported"); + throw new ImageNotSupportedException("Currently only MariaDB is supported"); + } + return columnsToRawFindAllQuery(view.getInternalName(), view.getColumns(), null, size, page); + } + + private String columnsToRawFindAllQuery(String tableName, List<TableColumn> columns, Instant timestamp, Long size, Long page) { final int[] idx = new int[]{0}; final StringBuilder statement = new StringBuilder("SELECT "); - table.getColumns() - .forEach(column -> statement.append(idx[0]++ > 0 ? "," : "") + columns.forEach(column -> statement.append(idx[0]++ > 0 ? "," : "") .append("`") .append(column.getInternalName()) .append("`")); statement.append(" FROM `") - .append(nameToInternalName(table.getName())) - .append("` FOR SYSTEM_TIME AS OF TIMESTAMP '") - .append(LocalDateTime.ofInstant(timestamp, ZoneId.of("UTC"))) - .append("'"); + .append(nameToInternalName(tableName)) + .append("`"); + if (timestamp != null) { + statement.append(" FOR SYSTEM_TIME AS OF TIMESTAMP '") + .append(LocalDateTime.ofInstant(timestamp, ZoneId.of("UTC"))) + .append("'"); + } if (size != null && page != null) { log.trace("pagination size/limit of {}", size); statement.append(" LIMIT ") @@ -714,34 +739,8 @@ public interface QueryMapper { statement.append(" OFFSET ") .append(page * size) .append(";"); - } - try { - final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); - log.trace("mapped timestamped query {} to prepared statement {}", statement, pstmt); - return pstmt; - } catch (SQLException e) { - log.error("Failed to prepare statement {}m reason: {}", statement, e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement", e); - } - } - - default QueryResultDto queryTableToQueryResultDto(ResultSet result, Table table) throws DateTimeException, - SQLException { - final List<Map<String, Object>> queryResult = new LinkedList<>(); - while (result.next()) { - /* map the result set to the columns through the stored metadata in the metadata database */ - int[] idx = new int[]{1}; - final Map<String, Object> map = new HashMap<>(); - for (int i = 0; i < table.getColumns().size(); i++) { - map.put(table.getColumns().get(i).getInternalName(), dataColumnToObject(result.getObject(idx[0]++), table.getColumns().get(i))); - } - queryResult.add(map); - } - log.trace("mapped result {} to query result {}", result, queryResult); - return QueryResultDto.builder() - .result(queryResult) - .build(); + return statement.toString(); } @Transactional(readOnly = true) diff --git a/fda-query-service/services/src/main/java/at/tuwien/mapper/StoreMapper.java b/fda-query-service/services/src/main/java/at/tuwien/mapper/StoreMapper.java index c594988657b4924af6581570e63cce73add71d20..9302a0ca0aac8a652ef8833657abff90c02e880a 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/mapper/StoreMapper.java +++ b/fda-query-service/services/src/main/java/at/tuwien/mapper/StoreMapper.java @@ -62,6 +62,17 @@ public interface StoreMapper { } } + default PreparedStatement queryStoreRawDeleteStaleQueries(Connection connection) throws QueryStoreException { + final String statement = "DELETE FROM `qs_queries` WHERE `is_persisted` = false AND ABS(DATEDIFF(`created`, NOW())) >= 1"; + try { + log.trace("mapped select all query '{}' to prepared statement", statement); + return connection.prepareStatement(statement); + } catch (SQLException e) { + log.error("Failed to prepare statement {}, reason: {}", statement, e.getMessage()); + throw new QueryStoreException("Failed to prepare statement", e); + } + } + default PreparedStatement queryStoreRawSelectOneQuery(Connection connection, Long queryId) throws QueryStoreException { final String statement = "SELECT `id`, `created`, `created_by`, `query`, `query_hash`, `result_hash`, `result_number`, `is_persisted` FROM `qs_queries` q WHERE q.`id` = ?"; try { diff --git a/fda-query-service/services/src/main/java/at/tuwien/mapper/ViewMapper.java b/fda-query-service/services/src/main/java/at/tuwien/mapper/ViewMapper.java index 35bcfffb455bb099e0fc067b5801dbc8eb5d1d48..04234f583fe89abaa5556f60495fadf3287f60f9 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/mapper/ViewMapper.java +++ b/fda-query-service/services/src/main/java/at/tuwien/mapper/ViewMapper.java @@ -26,7 +26,7 @@ import java.util.List; import java.util.Locale; import java.util.regex.Pattern; -@Mapper(componentModel = "spring") +@Mapper(componentModel = "spring", uses = {ContainerMapper.class}) public interface ViewMapper { org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ViewMapper.class); diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/DatabaseService.java b/fda-query-service/services/src/main/java/at/tuwien/service/DatabaseService.java index 2e27dc0b4024fe4d03687eb77bde0df89dc3c374..982871215c63bb4953fb3b68012e83c8667ce305 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/DatabaseService.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/DatabaseService.java @@ -2,11 +2,14 @@ package at.tuwien.service; import at.tuwien.entities.database.Database; import at.tuwien.exception.DatabaseNotFoundException; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; public interface DatabaseService { /** - * Finds a database by given id in the remote database service. + * Finds a database by given id in the metadata database. * * @param containerId The container id. * @param databaseId The database id. @@ -14,4 +17,11 @@ public interface DatabaseService { * @throws DatabaseNotFoundException The database was not found. */ Database find(Long containerId, Long databaseId) throws DatabaseNotFoundException; + + /** + * Finds all databases in the metadata database. + * + * @return List of databases. + */ + List<Database> findAll(); } diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/MessageQueueService.java b/fda-query-service/services/src/main/java/at/tuwien/service/MessageQueueService.java index 4bd2fd9bebdb5fa3fe2f67fcc3d75af4bba75b87..ed4342ddd40cfda326958f0b23fab34b30fc8583 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/MessageQueueService.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/MessageQueueService.java @@ -15,4 +15,11 @@ public interface MessageQueueService { * @throws AmqpException The consumer could not be created. */ void createConsumer(String queueName, Long containerId, Long databaseId, Long tableId) throws AmqpException; + + /** + * Restores missing consumers at the Broker Service. + * + * @throws AmqpException The consumer could not be created. + */ + void restore() throws AmqpException; } diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/QueryService.java b/fda-query-service/services/src/main/java/at/tuwien/service/QueryService.java index 119d887f3cce8a770db719496c51099d9227d3f3..01924d9fb29b8729987e5621bd6d4a4006cfa04d 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/QueryService.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/QueryService.java @@ -5,16 +5,20 @@ import at.tuwien.SortType; import at.tuwien.api.database.query.ExecuteStatementDto; import at.tuwien.api.database.query.ImportDto; import at.tuwien.api.database.query.QueryResultDto; -import at.tuwien.api.database.query.QueryTypeDto; import at.tuwien.api.database.table.TableCsvDeleteDto; import at.tuwien.api.database.table.TableCsvDto; import at.tuwien.api.database.table.TableCsvUpdateDto; +import at.tuwien.entities.database.Database; +import at.tuwien.entities.database.View; +import at.tuwien.entities.database.table.columns.TableColumn; import at.tuwien.exception.*; import at.tuwien.querystore.Query; +import net.sf.jsqlparser.JSQLParserException; import org.springframework.stereotype.Service; import java.security.Principal; import java.time.Instant; +import java.util.List; @Service public interface QueryService { @@ -71,6 +75,26 @@ public interface QueryService { throws QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, ColumnParseException, DatabaseConnectionException, TableMalformedException, QueryStoreException, UserNotFoundException; + /** + * Re-Executes the count-statement of an arbitrary query on the database container. We allow the user to only view + * the data, therefore the default "mariadb" user is allowed read-only access "SELECT". + * + * @param containerId The container id. + * @param databaseId The database id. + * @param query The query. + * @param principal The user principal. + * @return The result. + * @throws QueryStoreException The query store is not reachable. + * @throws QueryMalformedException The query is malformed. + * @throws DatabaseNotFoundException The database was not found in the metdata database. + * @throws ImageNotSupportedException The image is not supported. + * @throws TableMalformedException The table is malformed. + * @throws ColumnParseException The column mapping/parsing failed. + */ + Long reExecuteCount(Long containerId, Long databaseId, Query query, Principal principal) + throws QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, ColumnParseException, + TableMalformedException, QueryStoreException; + /** * Select all data known in the database-table id tuple at a given time and return a page of specific size, using * Instant to better abstract time concept (JDK 8) from SQL. We use the "mariadb" user for this. @@ -90,8 +114,8 @@ public interface QueryService { * @throws TableMalformedException The table is malformed. * @throws QueryMalformedException The query is malformed. */ - QueryResultDto findAll(Long containerId, Long databaseId, Long tableId, Instant timestamp, - Long page, Long size, Principal principal) throws TableNotFoundException, DatabaseNotFoundException, + QueryResultDto tableFindAll(Long containerId, Long databaseId, Long tableId, Instant timestamp, + Long page, Long size, Principal principal) throws TableNotFoundException, DatabaseNotFoundException, ImageNotSupportedException, DatabaseConnectionException, TableMalformedException, PaginationException, ContainerNotFoundException, QueryMalformedException, UserNotFoundException; @@ -115,11 +139,34 @@ public interface QueryService { * @throws FileStorageException The file could not be exported. * @throws QueryMalformedException The query is malformed. */ - ExportResource findAll(Long containerId, Long databaseId, Long tableId, Instant timestamp, Principal principal) + ExportResource tableFindAll(Long containerId, Long databaseId, Long tableId, Instant timestamp, Principal principal) throws TableNotFoundException, DatabaseNotFoundException, ImageNotSupportedException, DatabaseConnectionException, TableMalformedException, PaginationException, ContainerNotFoundException, FileStorageException, QueryMalformedException, UserNotFoundException; + /** + * Select all data known in the view id tuple and return a page of specific size. + * We use the "mariadb" user for this. + * + * @param containerId The container-database id pair. + * @param databaseId The container-database id pair. + * @param view The view. + * @param page The page. + * @param size The page size. + * @param principal The user principal. + * @return The select all data result + * @throws ViewNotFoundException The view was not found in the metadata database. + * @throws DatabaseNotFoundException The database was not found in the metdata database. + * @throws ImageNotSupportedException The image is not supported. + * @throws ContainerNotFoundException The container was not found in the metadata database. + * @throws ViewMalformedException The table is malformed. + * @throws QueryMalformedException The query is malformed. + */ + QueryResultDto viewFindAll(Long containerId, Long databaseId, View view, + Long page, Long size, Principal principal) throws ViewNotFoundException, DatabaseNotFoundException, + ImageNotSupportedException, DatabaseConnectionException, ViewMalformedException, PaginationException, + ContainerNotFoundException, QueryMalformedException, UserNotFoundException, TableMalformedException; + /** * Finds one query by container-database-query triple. * @@ -156,10 +203,27 @@ public interface QueryService { * @throws TableMalformedException The table columns are messed up what we got from the metadata database. * @throws ImageNotSupportedException The image is not supported. */ - Long count(Long containerId, Long databaseId, Long tableId, Instant timestamp, Principal principal) + Long tableCount(Long containerId, Long databaseId, Long tableId, Instant timestamp, Principal principal) throws ContainerNotFoundException, DatabaseNotFoundException, TableNotFoundException, TableMalformedException, ImageNotSupportedException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, UserNotFoundException; + /** + * Count the total tuples for a given table id within a container-database id tuple at a given time. + * + * @param containerId The container id. + * @param databaseId The database id. + * @param view The view. + * @param principal The user principal. + * @return The number of records, if successful + * @throws ContainerNotFoundException The container was not found in the metadata database. + * @throws DatabaseNotFoundException The database was not found in the remote database. + * @throws TableMalformedException The view columns are messed up what we got from the metadata database. + * @throws ImageNotSupportedException The image is not supported. + */ + Long viewCount(Long containerId, Long databaseId, View view, Principal principal) + throws ContainerNotFoundException, DatabaseNotFoundException, + TableMalformedException, ImageNotSupportedException, DatabaseConnectionException, QueryMalformedException, QueryStoreException, UserNotFoundException; + /** * @param containerId The container id. * @param databaseId The database id. @@ -232,4 +296,14 @@ public interface QueryService { */ void insert(Long containerId, Long databaseId, Long tableId, ImportDto data, Principal principal) throws ImageNotSupportedException, TableMalformedException, DatabaseNotFoundException, TableNotFoundException, ContainerNotFoundException, DatabaseConnectionException, QueryMalformedException, UserNotFoundException; + + /** + * Parses the stored columns from a given query. + * + * @param query The query. + * @param database The database that contains the list of tables with list of columns. + * @return List of columns in the order they are referenced in the query. + * @throws JSQLParserException The columns could not be extracted from the query. + */ + List<TableColumn> parseColumns(String query, Database database) throws JSQLParserException; } diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/StoreService.java b/fda-query-service/services/src/main/java/at/tuwien/service/StoreService.java index f2e206abb0ac473227a1b7cb6300ddf782faa1ba..baebb84c4a04ad3abf8dadef00706c9ae7bc9e20 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/StoreService.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/StoreService.java @@ -74,11 +74,18 @@ public interface StoreService { * @param principal The user principal. * @return The stored query on success. * @throws DatabaseNotFoundException The database id was not found in the metadata database - * @throws ImageNotSupportedException The image is not supported + * @throws ImageNotSupportedException The image is not supported. * @throws DatabaseConnectionException The database connection to the remote container failed. - * @throws QueryStoreException The query store raised some error + * @throws QueryStoreException The query store raised some error. */ Query persist(Long containerId, Long databaseId, Long queryId, Principal principal) throws DatabaseNotFoundException, ImageNotSupportedException, DatabaseConnectionException, QueryStoreException, UserNotFoundException; + /** + * Deletes the stale queries that have not been persisted within 24 hozrs. + * + * @throws ImageNotSupportedException The image is not supported. + * @throws QueryStoreException The query store raised some error. + */ + void deleteStaleQueries() throws ImageNotSupportedException, QueryStoreException; } diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java b/fda-query-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java index c5c6a30008b9b28435f45cf3fbd1216989d148f6..79910ab9bf4aaec93670d22d6678eaac437ef59c 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java @@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; import java.util.Optional; @Log4j2 @@ -32,4 +33,10 @@ public class DatabaseServiceImpl implements DatabaseService { } return database.get(); } + + @Override + @Transactional(readOnly = true) + public List<Database> findAll() { + return databaseRepository.findAll(); + } } diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java b/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java index a1d0c2ca114ef3244a75a0143f1416c9e3d4c0f8..a1dc0a91b336c9f1f8485ec7a167a6477487291e 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java @@ -39,7 +39,6 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.time.DateTimeException; import java.time.Instant; import java.time.format.DateTimeParseException; import java.util.ArrayList; @@ -102,63 +101,149 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService columns = parseColumns(query.getQuery(), database); } catch (JSQLParserException e) { log.error("Failed to map/parse columns: {}", e.getMessage()); - throw new ColumnParseException(e.getMessage(), e); + throw new ColumnParseException("Failed to map/parse columns: " + e.getMessage(), e); } - final QueryResultDto dto; + final String statement = queryMapper.queryToRawTimestampedQuery(query.getQuery(), database, query.getCreated(), true, page, size); + final QueryResultDto dto = executeNonPersistent(containerId, databaseId, statement, columns); + + dto.setId(query.getId()); + dto.setResultNumber(query.getResultNumber()); + return dto; + } + + @Override + @Transactional(readOnly = true) + public Long reExecuteCount(Long containerId, Long databaseId, Query query, Principal principal) + throws QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, ColumnParseException, + TableMalformedException, QueryStoreException { + /* find */ + final Database database = databaseService.find(containerId, databaseId); + if (!database.getContainer().getImage().getRepository().equals("mariadb")) { + throw new ImageNotSupportedException("Currently only MariaDB is supported"); + } + /* run query */ + try { + parseColumns(query.getQuery(), database); + } catch (JSQLParserException e) { + log.error("Failed to map/parse columns: {}", e.getMessage()); + throw new ColumnParseException("Failed to map/parse columns: " + e.getMessage(), e); + } + final String statement = queryMapper.queryToRawTimestampedQuery(query.getQuery(), database, query.getCreated(), false, null, null); + return executeCountNonPersistent(containerId, databaseId, statement); + } + + private PreparedStatement prepareStatement(Connection connection, String statement) throws QueryMalformedException { + try { + final PreparedStatement pstmt = connection.prepareStatement(statement); + log.trace("mapped timestamped query {} to prepared statement {}", statement, pstmt); + return pstmt; + } catch (SQLException e) { + log.error("Failed to prepare statement {}m reason: {}", statement, e.getMessage()); + throw new QueryMalformedException("Failed to prepare statement", e); + } + } + + private QueryResultDto executeNonPersistent(Long containerId, Long databaseId, String statement, List<TableColumn> columns) + throws QueryMalformedException, DatabaseNotFoundException, TableMalformedException { + /* find */ + final Database database = databaseService.find(containerId, databaseId); + final User root = databaseMapper.containerToPrivilegedUser(database.getContainer()); + /* run query */ + final ComboPooledDataSource dataSource = getDataSource(database.getContainer().getImage(), + database.getContainer(), database, root); try { final Connection connection = dataSource.getConnection(); - final PreparedStatement preparedStatement = queryMapper.queryToRawTimestampedQuery(connection, query.getQuery(), - database, query.getCreated(), true, page, size); + final PreparedStatement preparedStatement = prepareStatement(connection, statement); final ResultSet resultSet = preparedStatement.executeQuery(); - dto = queryMapper.resultListToQueryResultDto(columns, resultSet); + return queryMapper.resultListToQueryResultDto(columns, resultSet); } catch (SQLException e) { log.error("Failed to execute and map time-versioned query: {}", e.getMessage()); - throw new TableMalformedException("Failed to execute and map time-versioned query", e); + throw new TableMalformedException("Failed to execute and map time-versioned query: " + e.getMessage(), e); } finally { dataSource.close(); } - dto.setId(query.getId()); - dto.setResultNumber(query.getResultNumber()); - return dto; } - @Override - @Transactional(readOnly = true) - public QueryResultDto findAll(Long containerId, Long databaseId, Long tableId, Instant timestamp, Long page, - Long size, Principal principal) throws TableNotFoundException, DatabaseNotFoundException, - ImageNotSupportedException, DatabaseConnectionException, TableMalformedException, PaginationException, - ContainerNotFoundException, QueryMalformedException, UserNotFoundException { + private Long executeCountNonPersistent(Long containerId, Long databaseId, String statement) + throws QueryMalformedException, TableMalformedException, DatabaseNotFoundException, QueryStoreException { /* find */ final Database database = databaseService.find(containerId, databaseId); - final Table table = tableService.find(containerId, databaseId, tableId); final User root = databaseMapper.containerToPrivilegedUser(database.getContainer()); /* run query */ final ComboPooledDataSource dataSource = getDataSource(database.getContainer().getImage(), database.getContainer(), database, root); - final QueryResultDto result; try { final Connection connection = dataSource.getConnection(); - final PreparedStatement preparedStatement = queryMapper.tableToRawFindAllQuery(connection, table, timestamp, size, page); + final PreparedStatement preparedStatement = prepareStatement(connection, statement); final ResultSet resultSet = preparedStatement.executeQuery(); - result = queryMapper.queryTableToQueryResultDto(resultSet, table); - } catch (DateTimeException e) { - log.error("Failed to parse date from the one stored in the metadata database: {}", e.getMessage()); - throw new TableMalformedException("Could not parse date from format", e); + return queryMapper.resultSetToNumber(resultSet); } catch (SQLException e) { log.error("Failed to map object: {}", e.getMessage()); - throw new TableMalformedException("Failed to map object", e); + throw new TableMalformedException("Failed to map object: " + e.getMessage(), e); } finally { dataSource.close(); } - return result; } @Override @Transactional(readOnly = true) - public ExportResource findAll(Long containerId, Long databaseId, Long tableId, Instant timestamp, Principal principal) - throws TableNotFoundException, DatabaseNotFoundException, ImageNotSupportedException, - DatabaseConnectionException, TableMalformedException, PaginationException, ContainerNotFoundException, - FileStorageException, QueryMalformedException, UserNotFoundException { + public QueryResultDto tableFindAll(Long containerId, Long databaseId, Long tableId, Instant timestamp, Long page, + Long size, Principal principal) throws TableNotFoundException, DatabaseNotFoundException, + ImageNotSupportedException, TableMalformedException, QueryMalformedException { + /* find */ + final Table table = tableService.find(containerId, databaseId, tableId); + /* run query */ + String statement = queryMapper.tableToRawFindAllQuery(table, timestamp, size, page); + return executeNonPersistent(containerId, databaseId, statement, table.getColumns()); + } + + @Override + @Transactional(readOnly = true) + public QueryResultDto viewFindAll(Long containerId, Long databaseId, View view, + Long page, Long size, Principal principal) throws DatabaseNotFoundException, + ImageNotSupportedException, QueryMalformedException, TableMalformedException { + /* find */ + /* run query */ + String statement = queryMapper.viewToRawFindAllQuery(view, size, page); + return executeNonPersistent(containerId, databaseId, statement, view.getColumns()); + } + + @Override + @Transactional + public Long tableCount(Long containerId, Long databaseId, Long tableId, Instant timestamp, Principal principal) + throws DatabaseNotFoundException, TableNotFoundException, ImageNotSupportedException, + QueryMalformedException, QueryStoreException, TableMalformedException { + /* find */ + final Table table = tableService.find(containerId, databaseId, tableId); + final String statement = queryMapper.tableToRawCountAllQuery(table, timestamp); + return executeCountNonPersistent(containerId, databaseId, statement); + } + + @Override + @Transactional + public Long viewCount(Long containerId, Long databaseId, View view, Principal principal) + throws DatabaseNotFoundException, ImageNotSupportedException, + QueryMalformedException, QueryStoreException, TableMalformedException { + /* find */ + final String statement = queryMapper.viewToRawCountAllQuery(view); + return executeCountNonPersistent(containerId, databaseId, statement); + } + + @Transactional(readOnly = true) + public QueryResultDto findAllView(Long containerId, Long databaseId, Long viewId, Instant timestamp, Long page, + Long size, Principal principal) throws TableNotFoundException, DatabaseNotFoundException, + ImageNotSupportedException, TableMalformedException, QueryMalformedException { + /* find */ + final Table table = tableService.find(containerId, databaseId, viewId); + /* run query */ + String statement = queryMapper.tableToRawFindAllQuery(table, timestamp, size, page); + return executeNonPersistent(containerId, databaseId, statement, table.getColumns()); + } + + @Override + @Transactional(readOnly = true) + public ExportResource tableFindAll(Long containerId, Long databaseId, Long tableId, Instant timestamp, Principal principal) + throws TableNotFoundException, DatabaseNotFoundException, FileStorageException, QueryMalformedException { final String filename = RandomStringUtils.randomAlphabetic(40) + ".csv"; /* find */ final Database database = databaseService.find(containerId, databaseId); @@ -178,7 +263,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService FileUtils.forceDelete(file); } catch (IOException | SQLException e) { log.error("Failed to execute query and/or export file: {}", e.getMessage()); - throw new FileStorageException("Failed to execute query and/or export file", e); + throw new FileStorageException("Failed to execute query and/or export file: " + e.getMessage(), e); } finally { dataSource.close(); } @@ -227,36 +312,11 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService .build(); } - @Override - @Transactional - public Long count(Long containerId, Long databaseId, Long tableId, Instant timestamp, Principal principal) - throws DatabaseNotFoundException, TableNotFoundException, ImageNotSupportedException, - QueryMalformedException, QueryStoreException, TableMalformedException { - /* find */ - final Database database = databaseService.find(containerId, databaseId); - final Table table = tableService.find(containerId, databaseId, tableId); - final User root = databaseMapper.containerToPrivilegedUser(database.getContainer()); - /* run query */ - final ComboPooledDataSource dataSource = getDataSource(database.getContainer().getImage(), - database.getContainer(), database, root); - try { - final Connection connection = dataSource.getConnection(); - final PreparedStatement preparedStatement = queryMapper.tableToRawCountAllQuery(connection, table, timestamp); - final ResultSet resultSet = preparedStatement.executeQuery(); - return queryMapper.resultSetToNumber(resultSet); - } catch (SQLException e) { - log.error("Failed to count raw tuples: {}", e.getMessage()); - throw new TableMalformedException("Failed to count raw tuples", e); - } finally { - dataSource.close(); - } - } - @Override @Transactional public void update(Long containerId, Long databaseId, Long tableId, TableCsvUpdateDto data, Principal principal) throws ImageNotSupportedException, TableMalformedException, DatabaseNotFoundException, - TableNotFoundException, QueryMalformedException, UserNotFoundException { + TableNotFoundException, QueryMalformedException { /* find */ final Database database = databaseService.find(containerId, databaseId); final Table table = tableService.find(containerId, databaseId, tableId); @@ -271,7 +331,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService preparedStatement.executeUpdate(); } catch (SQLException e) { log.error("Failed to update tuples: {}", e.getMessage()); - throw new TableMalformedException("Failed to update tuples", e); + throw new TableMalformedException("Failed to update tuples: " + e.getMessage(), e); } finally { dataSource.close(); } @@ -288,10 +348,6 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService final User root = databaseMapper.containerToPrivilegedUser(database.getContainer()); log.trace("parsed insert data {}", data); /* run query */ - if (data.getData().size() == 0) { - log.error("Failed to parse data, the provided map {} is empty", data.getData()); - throw new TableMalformedException("Failed to parse data"); - } final ComboPooledDataSource dataSource = getDataSource(database.getContainer().getImage(), database.getContainer(), database, root); /* prepare the statement */ @@ -301,14 +357,14 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService preparedStatement.executeUpdate(); } catch (DateTimeParseException e) { log.error("Failed to parse date: {}", e.getMessage()); - throw new TableMalformedException("Failed to parse date", e); + throw new TableMalformedException("Failed to parse date: " + e.getMessage(), e); } catch (NumberFormatException e) { log.error("Failed to parse number: {}", e.getMessage()); - throw new TableMalformedException("Failed to parse number", e); + throw new TableMalformedException("Failed to parse number: " + e.getMessage(), e); } catch (Exception e) { log.error("Database failed to accept tuple: {}", e.getMessage()); log.throwing(e); - throw new TableMalformedException("Database failed to accept tuple " + e.getMessage(), e); + throw new TableMalformedException("Database failed to accept tuple: " + e.getMessage(), e); } finally { dataSource.close(); } @@ -334,7 +390,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService preparedStatement.executeUpdate(); } catch (SQLException e) { log.error("Failed to delete tuples: {}", e.getMessage()); - throw new TableMalformedException("Failed to delete tuples", e); + throw new TableMalformedException("Failed to delete tuples: " + e.getMessage(), e); } finally { dataSource.close(); } @@ -399,7 +455,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService * @throws JSQLParserException The columns could not be extracted from the query. */ @Transactional(readOnly = true) - protected List<TableColumn> parseColumns(String query, Database database) throws JSQLParserException { + public List<TableColumn> parseColumns(String query, Database database) throws JSQLParserException { final List<TableColumn> columns = new ArrayList<>(); final CCJSqlParserManager parserRealSql = new CCJSqlParserManager(); final Statement statement = parserRealSql.parse(new StringReader(query)); diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java b/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java index 00f16ff96f763f335a24177d1c66cfd4185f3438..89bcb5425ec77f72cf971366b3bd70ec2e80c7dd 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/impl/RabbitMqServiceImpl.java @@ -1,19 +1,22 @@ package at.tuwien.service.impl; import at.tuwien.amqp.RabbitMqConsumer; +import at.tuwien.api.amqp.ConsumerDto; import at.tuwien.config.AmqpConfig; import at.tuwien.entities.database.table.Table; import at.tuwien.exception.*; +import at.tuwien.gateway.BrokerServiceGateway; import at.tuwien.service.MessageQueueService; import at.tuwien.service.QueryService; +import at.tuwien.service.TableService; import com.fasterxml.jackson.databind.ObjectMapper; import com.rabbitmq.client.*; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.io.IOException; +import java.util.List; @Log4j2 @Service @@ -23,14 +26,19 @@ public class RabbitMqServiceImpl implements MessageQueueService { private final AmqpConfig amqpConfig; private final ObjectMapper objectMapper; private final QueryService queryService; + private final TableService tableService; + private final BrokerServiceGateway brokerServiceGateway; @Autowired public RabbitMqServiceImpl(Channel channel, AmqpConfig amqpConfig, ObjectMapper objectMapper, - QueryService queryService) { + QueryService queryService, TableService tableService, + BrokerServiceGateway brokerServiceGateway) { this.channel = channel; this.amqpConfig = amqpConfig; this.objectMapper = objectMapper; this.queryService = queryService; + this.tableService = tableService; + this.brokerServiceGateway = brokerServiceGateway; } @Override @@ -54,4 +62,25 @@ public class RabbitMqServiceImpl implements MessageQueueService { } } + @Override + public void restore() throws AmqpException { + final List<Table> tables = tableService.findAll(); + final List<ConsumerDto> consumers = brokerServiceGateway.findAllConsumers(); + for (Table table : tables) { + final long consumerCount = consumers.stream().filter(c -> c.getQueue().getName().equals(table.getQueueName())).count(); + if (consumerCount >= amqpConfig.getAmqpConsumers()) { + log.trace("listener table with name {} already has {} consumers (max. {})", table.getName(), + consumerCount, amqpConfig.getAmqpConsumers()); + continue; + } + log.debug("table with id {} has {} consumers, but needs {} in total", table.getId(), consumerCount, + amqpConfig.getAmqpConsumers()); + for (long i = consumerCount; i < amqpConfig.getAmqpConsumers(); i++) { + createConsumer(table.getQueueName(), table.getDatabase().getContainer().getId(), + table.getDatabase().getId(), table.getId()); + log.trace("creating consumer #{}", i); + } + } + } + } diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/impl/StoreServiceImpl.java b/fda-query-service/services/src/main/java/at/tuwien/service/impl/StoreServiceImpl.java index 52dba2e3368ea79eaa13b6b4026bcfcf1904ab61..b03514297f2fab2544257505a51d1272d73dfabe 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/impl/StoreServiceImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/impl/StoreServiceImpl.java @@ -181,6 +181,34 @@ public class StoreServiceImpl extends HibernateConnector implements StoreService return out; } + @Override + public void deleteStaleQueries() throws ImageNotSupportedException, QueryStoreException { + /* find */ + final List<Database> databases = databaseService.findAll(); + for (Database database : databases) { + if (!database.getContainer().getImage().getRepository().equals("mariadb")) { + log.error("Currently only MariaDB is supported"); + throw new ImageNotSupportedException("Currently only MariaDB is supported"); + } + final User root = databaseMapper.containerToPrivilegedUser(database.getContainer()); + /* run query */ + final ComboPooledDataSource dataSource = getDataSource(database.getContainer().getImage(), + database.getContainer(), database, root); + /* delete stale queries older than 24hrs */ + try { + final Connection connection = dataSource.getConnection(); + final PreparedStatement preparedStatement = storeMapper.queryStoreRawDeleteStaleQueries(connection); + final int affected = preparedStatement.executeUpdate(); + log.debug("delete stale queries affected {} rows", affected); + } catch (SQLException e) { + log.error("Failed to delete stale queries in database with id {}, reason: {}", database.getId(), e.getMessage()); + throw new QueryStoreException("Failed to delete stale queries in database with id " + database.getId() + ": " + e.getMessage()); + } finally { + dataSource.close(); + } + } + } + protected List<Query> resultSetToQueryList(ResultSet resultSet) throws SQLException { final List<Query> queries = new LinkedList<>(); while (resultSet.next()) { diff --git a/fda-query-service/services/src/main/java/at/tuwien/service/impl/ViewServiceImpl.java b/fda-query-service/services/src/main/java/at/tuwien/service/impl/ViewServiceImpl.java index 830cc6d216c6ded0d7ba54cc1a2eec61203549e5..0ab98c4ebee9a2cf549e04be3e5782e3de4e39e7 100644 --- a/fda-query-service/services/src/main/java/at/tuwien/service/impl/ViewServiceImpl.java +++ b/fda-query-service/services/src/main/java/at/tuwien/service/impl/ViewServiceImpl.java @@ -3,6 +3,7 @@ package at.tuwien.service.impl; import at.tuwien.api.database.ViewCreateDto; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.View; +import at.tuwien.entities.database.table.columns.TableColumn; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.mapper.DatabaseMapper; @@ -10,10 +11,12 @@ import at.tuwien.mapper.ViewMapper; import at.tuwien.repository.elastic.ViewIdxRepository; import at.tuwien.repository.jpa.ViewRepository; import at.tuwien.service.DatabaseService; +import at.tuwien.service.QueryService; import at.tuwien.service.UserService; import at.tuwien.service.ViewService; import com.mchange.v2.c3p0.ComboPooledDataSource; import lombok.extern.log4j.Log4j2; +import net.sf.jsqlparser.JSQLParserException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,17 +38,19 @@ public class ViewServiceImpl extends HibernateConnector implements ViewService { private final ViewRepository viewRepository; private final DatabaseService databaseService; private final ViewIdxRepository viewIdxRepository; + private final QueryService queryService; @Autowired public ViewServiceImpl(ViewMapper viewMapper, UserService userService, DatabaseMapper databaseMapper, ViewRepository viewRepository, DatabaseService databaseService, - ViewIdxRepository viewIdxRepository) { + ViewIdxRepository viewIdxRepository, QueryService queryService) { this.viewMapper = viewMapper; this.userService = userService; this.databaseMapper = databaseMapper; this.viewRepository = viewRepository; this.databaseService = databaseService; this.viewIdxRepository = viewIdxRepository; + this.queryService = queryService; } @Override @@ -118,13 +123,20 @@ public class ViewServiceImpl extends HibernateConnector implements ViewService { /* create view */ final ComboPooledDataSource dataSource = getDataSource(database.getContainer().getImage(), database.getContainer(), database, root); + final List<TableColumn> columns; + try { + columns = queryService.parseColumns(data.getQuery(), database); + } catch (JSQLParserException e) { + log.error("Failed to map/parse columns: {}", e.getMessage()); + throw new QueryMalformedException(e.getMessage(), e); + } try { final Connection connection = dataSource.getConnection(); final PreparedStatement createViewStatement = viewMapper.viewCreateDtoToRawCreateViewQuery(connection, data); createViewStatement.executeUpdate(); } catch (SQLException e) { log.error("Failed to create view: {}", e.getMessage()); - throw new ViewMalformedException("Failed to create view", e); + throw new ViewMalformedException("Failed to create view: " + e.getMessage(), e); } finally { dataSource.close(); } @@ -139,6 +151,7 @@ public class ViewServiceImpl extends HibernateConnector implements ViewService { .query(data.getQuery()) .isInitialView(false) .isPublic(data.getIsPublic()) + .columns(columns) .build(); final View view = viewRepository.save(entity); log.info("Created view with id {}", view.getId()); diff --git a/fda-semantics-service/.coverage b/fda-semantics-service/.coverage deleted file mode 100644 index 71d9c3ff33f4130c7b3a4a880b8162ec161f96fb..0000000000000000000000000000000000000000 Binary files a/fda-semantics-service/.coverage and /dev/null differ diff --git a/fda-semantics-service/coverage.txt b/fda-semantics-service/coverage.txt deleted file mode 100644 index dbcdea6c85914de1708358021d8cf4e9b3be8b81..0000000000000000000000000000000000000000 --- a/fda-semantics-service/coverage.txt +++ /dev/null @@ -1,13 +0,0 @@ -Name Stmts Miss Cover -------------------------------------------- -app.py 132 71 46% -list.py 56 0 100% -onto_feat.py 41 26 37% -save.py 31 26 16% -test/__init__.py 0 0 100% -test/test_app.py 38 1 97% -test/test_list.py 40 1 98% -test/test_validate.py 18 1 94% -validate.py 21 0 100% -------------------------------------------- -TOTAL 377 126 67% diff --git a/fda-semantics-service/report.xml b/fda-semantics-service/report.xml deleted file mode 100644 index cf16f04fdeab656dd38e42ddf7d9435be3f81012..0000000000000000000000000000000000000000 --- a/fda-semantics-service/report.xml +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="18" time="85.067" timestamp="2023-01-26T09:41:09.876196" hostname="medusa"><testcase classname="test.test_validate.ValidatorUnitTest" name="test_validator_dividedunit" time="0.006" /><testcase classname="test.test_validate.ValidatorUnitTest" name="test_validator_misspelling" time="0.003" /><testcase classname="test.test_validate.ValidatorUnitTest" name="test_validator_no_SI_Unit" time="0.002" /><testcase classname="test.test_validate.ValidatorUnitTest" name="test_validator_prefixedunit" time="0.002" /><testcase classname="test.test_validate.ValidatorUnitTest" name="test_validator_true" time="0.002" /><testcase classname="test.test_list.ListUnitTest" name="test_get_concept_uri_hasSpaces_succeeds" time="22.945" /><testcase classname="test.test_list.ListUnitTest" name="test_get_concept_uri_succeeds" time="24.992" /><testcase classname="test.test_list.ListUnitTest" name="test_get_unit_uri_hasBraces_succeeds" time="0.239" /><testcase classname="test.test_list.ListUnitTest" name="test_get_unit_uri_succeeds" time="0.056" /><testcase classname="test.test_list.ListUnitTest" name="test_list_concepts_succeeds" time="19.513" /><testcase classname="test.test_list.ListUnitTest" name="test_list_units_fails" time="0.116" /><testcase classname="test.test_list.ListUnitTest" name="test_list_units_succeeds" time="0.066" /><testcase classname="test.test_app.AppUnitTest" name="test_save_concept_name_null_fails" time="0.004" /><testcase classname="test.test_app.AppUnitTest" name="test_save_concept_uri_and_name_null_fails" time="0.002" /><testcase classname="test.test_app.AppUnitTest" name="test_save_concept_uri_null_fails" time="0.002" /><testcase classname="test.test_app.AppUnitTest" name="test_save_unit_name_null_fails" time="0.002" /><testcase classname="test.test_app.AppUnitTest" name="test_save_unit_uri_and_name_null_fails" time="0.002" /><testcase classname="test.test_app.AppUnitTest" name="test_save_unit_uri_null_fails" time="0.002" /></testsuite></testsuites> \ No newline at end of file diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java b/fda-table-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java index aaab95c894008ac7bf65964450a2f8101ac643b6..08631811019b691a48e725754a824f989c060f05 100644 --- a/fda-table-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java +++ b/fda-table-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java @@ -85,9 +85,9 @@ public abstract class BaseUnitTest { public final static String DATABASE_NET = "fda-userdb"; - public final static String BROKER_NAME = "fda-broker-service"; - public final static String BROKER_IP = "172.29.0.2"; - public final static String BROKER_HOSTNAME = "broker-service"; + public final static String BROKER_NAME = "dbrepo-broker-service"; + public final static String BROKER_IP = "172.31.0.2"; + public final static String BROKER_HOSTNAME = "dbrepo-broker-service"; public final static Integer BROKER_MANAGEMENT_PORT = 15672; public final static String BROKER_IMAGE = "rabbitmq"; public final static String BROKER_TAG = "3-management-alpine"; @@ -148,24 +148,24 @@ public abstract class BaseUnitTest { public final static String CONTAINER_1_HASH = "deadbeef"; public final static ContainerImage CONTAINER_1_IMAGE = IMAGE_1; public final static String CONTAINER_1_NAME = "u01"; - public final static String CONTAINER_1_INTERNALNAME = "fda-userdb-u01"; - public final static String CONTAINER_1_IP = "172.28.0.5"; + public final static String CONTAINER_1_INTERNALNAME = "dbrepo-userdb-u01"; + public final static String CONTAINER_1_IP = "172.30.0.5"; public final static Instant CONTAINER_1_CREATED = Instant.now().minus(1, HOURS); public final static Long CONTAINER_2_ID = 2L; public final static String CONTAINER_2_HASH = "deadbeef"; public final static ContainerImage CONTAINER_2_IMAGE = IMAGE_1; public final static String CONTAINER_2_NAME = "u02"; - public final static String CONTAINER_2_INTERNALNAME = "fda-userdb-u02"; - public final static String CONTAINER_2_IP = "172.28.0.6"; + public final static String CONTAINER_2_INTERNALNAME = "dbrepo-userdb-u02"; + public final static String CONTAINER_2_IP = "172.30.0.6"; public final static Instant CONTAINER_2_CREATED = Instant.now().minus(1, HOURS); public final static Long CONTAINER_3_ID = 3L; public final static String CONTAINER_3_HASH = "deadbeef"; public final static ContainerImage CONTAINER_3_IMAGE = IMAGE_1; public final static String CONTAINER_3_NAME = "u03"; - public final static String CONTAINER_3_INTERNALNAME = "fda-userdb-u03"; - public final static String CONTAINER_3_IP = "172.28.0.7"; + public final static String CONTAINER_3_INTERNALNAME = "dbrepo-userdb-u03"; + public final static String CONTAINER_3_IP = "172.30.0.7"; public final static Instant CONTAINER_3_CREATED = Instant.now().minus(1, HOURS); public final static Long DATABASE_1_ID = 1L; @@ -761,7 +761,7 @@ public abstract class BaseUnitTest { public final static String CONTAINER_NGINX_NET = "fda-public"; public final static String CONTAINER_NGINX_NAME = "file-service"; public final static String CONTAINER_NGINX_INTERNALNAME = "fda-test-file-service"; - public final static String CONTAINER_NGINX_IP = "172.29.0.3"; + public final static String CONTAINER_NGINX_IP = "172.31.0.3"; public final static Instant CONTAINER_NGINX_CREATED = Instant.now().minus(3, HOURS); public final static Long CONCEPT_1_ID = 1L; diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/config/GatewayConfig.java b/fda-table-service/rest-service/src/test/java/at/tuwien/config/GatewayConfig.java index 6cf55041bae2691730fb6c5131673c568471cc03..26d1b50051997b362f19029252437e06aea44e63 100644 --- a/fda-table-service/rest-service/src/test/java/at/tuwien/config/GatewayConfig.java +++ b/fda-table-service/rest-service/src/test/java/at/tuwien/config/GatewayConfig.java @@ -21,7 +21,7 @@ public class GatewayConfig { @Bean("brokerRestTemplate") public RestTemplate brokerRestTemplate() { final RestTemplate restTemplate = new RestTemplate(); - restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("http://broker-service:15672")); + restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("http://dbrepo-broker-service:15672")); restTemplate.getInterceptors() .add(new BasicAuthenticationInterceptor(brokerUsername, brokerPassword)); return restTemplate; diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/service/MessageQueueServiceIntegrationTest.java b/fda-table-service/rest-service/src/test/java/at/tuwien/service/MessageQueueServiceIntegrationTest.java index 300e882c68bf1fd1dcbee8ab12312598030b83ba..1b5a0ecb183d79aa34a3aa66aafcd24aed43a49f 100644 --- a/fda-table-service/rest-service/src/test/java/at/tuwien/service/MessageQueueServiceIntegrationTest.java +++ b/fda-table-service/rest-service/src/test/java/at/tuwien/service/MessageQueueServiceIntegrationTest.java @@ -67,14 +67,14 @@ public class MessageQueueServiceIntegrationTest extends BaseUnitTest { .withName("fda-userdb") .withIpam(new Network.Ipam() .withConfig(new Network.Ipam.Config() - .withSubnet("172.28.0.0/16"))) + .withSubnet("172.30.0.0/16"))) .withEnableIpv6(false) .exec(); dockerClient.createNetworkCmd() .withName("fda-public") .withIpam(new Network.Ipam() .withConfig(new Network.Ipam.Config() - .withSubnet("172.29.0.0/16"))) + .withSubnet("172.31.0.0/16"))) .withEnableIpv6(false) .exec(); /* create amqp */ diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java b/fda-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java index 3ef1c854c2c2891e8313fa4d4fb07becc3a81b36..9d95e621ef0b22c053fc8b3c8309b77decda2b81 100644 --- a/fda-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java +++ b/fda-table-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java @@ -102,14 +102,14 @@ public class TableServiceIntegrationTest extends BaseUnitTest { .withName("fda-userdb") .withIpam(new Network.Ipam() .withConfig(new Network.Ipam.Config() - .withSubnet("172.28.0.0/16"))) + .withSubnet("172.30.0.0/16"))) .withEnableIpv6(false) .exec(); dockerClient.createNetworkCmd() .withName("fda-public") .withIpam(new Network.Ipam() .withConfig(new Network.Ipam.Config() - .withSubnet("172.29.0.0/16"))) + .withSubnet("172.31.0.0/16"))) .withEnableIpv6(false) .exec(); /* create container */ diff --git a/fda-table-service/rest-service/src/test/java/at/tuwien/utils/AmqpUtils.java b/fda-table-service/rest-service/src/test/java/at/tuwien/utils/AmqpUtils.java index 6caf0d930eb80c443a319292d5f749981002668d..dfc90d2ea25ae4920fd2edd74a26660209190554 100644 --- a/fda-table-service/rest-service/src/test/java/at/tuwien/utils/AmqpUtils.java +++ b/fda-table-service/rest-service/src/test/java/at/tuwien/utils/AmqpUtils.java @@ -34,7 +34,7 @@ public class AmqpUtils { public void createExchange(String exchange) { exchange = exchange.replace("/", "%2F"); - final URI uri = URI.create("http://broker-service:15672/api/exchanges/%2F/" + exchange); + final URI uri = URI.create("http://dbrepo-broker-service:15672/api/exchanges/%2F/" + exchange); final CreateExchangeDto payload = CreateExchangeDto.builder() .type("fanout") .autoDelete(false) @@ -67,7 +67,7 @@ public class AmqpUtils { } public boolean queueExists(String queue) { - final URI uri = URI.create("http://broker-service:15672/api/queues/%2F/"); + final URI uri = URI.create("http://dbrepo-broker-service:15672/api/queues/%2F/"); final ResponseEntity<QueueDto[]> response = restTemplate.exchange(uri, HttpMethod.GET, new HttpEntity<>(null), QueueDto[].class); if (!response.getStatusCode().equals(HttpStatus.OK)) { log.error("Failed to find queue, code is {}", response.getStatusCode()); diff --git a/fda-ui/.gitignore b/fda-ui/.gitignore index 0c83f47701873d10665a28e24aad623e89467776..a6a8dd571f3fc30997a2b59aab90d21abc4aff7c 100644 --- a/fda-ui/.gitignore +++ b/fda-ui/.gitignore @@ -21,6 +21,7 @@ lib-cov # Coverage directory used by tools like istanbul coverage/ +coverage.txt # nyc test coverage .nyc_output @@ -93,4 +94,5 @@ sw.* *.swp # CI records some videos for the e2e tests -videos +videos/ +screenshots/ diff --git a/fda-ui/.nycrc b/fda-ui/.nycrc index ccda5111b9ac9d41694692f5c4e0a12dadc6fbbb..a4841b7947c1e8e06b5ca816319f1eb0f20b952e 100644 --- a/fda-ui/.nycrc +++ b/fda-ui/.nycrc @@ -6,7 +6,7 @@ "branches": 35, "check-coverage": true, "include": [ "components/**/*.vue", "layouts/**/*.vue", "pages/**/*.vue", "server-middleware/**/*.js", "store/**/*.js"], - "exclude": ["node_modules"], + "exclude": ["node_modules", "server-middleware", "store"], "extension": [".js", ".vue"], "reporter": [ "lcov", diff --git a/fda-ui/.prod/default.conf b/fda-ui/.prod/default.conf deleted file mode 100644 index 43a88f37d85a14ad680cfe0e2c2d483b8ba3bafc..0000000000000000000000000000000000000000 --- a/fda-ui/.prod/default.conf +++ /dev/null @@ -1,30 +0,0 @@ -server { - listen 3000 default_server; - server_name dbrepo.local; - proxy_set_header Host dbrepo.local; - - root /usr/share/nginx/html; - index index.html index.htm; - - location /api { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_pass http://fda-gateway-service:9095; - } - - location / { - root /usr/share/nginx/html; - try_files $uri $uri/ /index.html; - index index.html index.htm; - } - - # allow post on static pages - error_page 405 =200 $uri; - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /usr/share/nginx/html; - } - -} diff --git a/fda-ui/.prod/secure.conf b/fda-ui/.prod/secure.conf deleted file mode 100644 index 402ce356e6f69ac6a322b36c7f3fe78672d6264f..0000000000000000000000000000000000000000 --- a/fda-ui/.prod/secure.conf +++ /dev/null @@ -1,38 +0,0 @@ -server { - listen 443 default_server; - server_name dbrepo.ossdip.at; - proxy_set_header Host dbrepo.ossdip.at; - - root /usr/share/nginx/html; - - index index.html; - location / { - root /usr/share/nginx/html; - try_files $uri $uri/ /index.html; - index index.html index.htm; - } - - location /api { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_pass http://fda-gateway-service:9095; - } - - location /server-middleware { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_pass https://fda-ui:443; - } - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /usr/share/nginx/html; - } - - ssl on; - ssl_certificate /etc/nginx/conf.d/cert.pem; - ssl_certificate_key /etc/nginx/conf.d/privkey.pem; - -} diff --git a/fda-ui/Dockerfile b/fda-ui/Dockerfile index b7d21fc55b84240c6a1fdd86f44cf6425c487e98..0df37b3162497a09a783844a2b2eef4f264f9a14 100644 --- a/fda-ui/Dockerfile +++ b/fda-ui/Dockerfile @@ -1,4 +1,3 @@ -# FROM node:lts as build FROM node:14-alpine as build MAINTAINER Martin Weise <martin.weise@tuwien.ac.at> @@ -35,6 +34,9 @@ COPY ./utils ./utils RUN yarn build > /dev/null +FROM node:14-alpine as runtime +MAINTAINER Martin Weise <martin.weise@tuwien.ac.at> + EXPOSE 3000 EXPOSE 9100 @@ -51,6 +53,13 @@ ENV VERSION="${TAG}" ENV TITLE="Database Repository" ENV ICON="/favicon.ico" +WORKDIR /app + +COPY --from=build /usr/local/bin/node_exporter/ /usr/local/bin/node_exporter/ +COPY --from=build /app /app + +RUN ls -la + COPY ./docker-entrypoint.sh /app/docker-entrypoint.sh RUN chmod +x /app/docker-entrypoint.sh diff --git a/fda-ui/components/DatabaseList.vue b/fda-ui/components/DatabaseList.vue new file mode 100644 index 0000000000000000000000000000000000000000..2f85f6a72ed63ff7c776abf354e839b5fc4198c9 --- /dev/null +++ b/fda-ui/components/DatabaseList.vue @@ -0,0 +1,193 @@ +<template> + <div> + <v-progress-linear v-if="loadingContainers || loadingDatabases" :color="loadingColor" :indeterminate="!error" /> + <v-card + v-for="(container, idx) in containers" + :key="idx" + :to="link(container)" + :disabled="!container.database" + flat + tile> + <v-divider class="mx-4" /> + <v-card-title v-if="!hasDatabase(container)" v-text="container.name" /> + <v-card-title v-if="hasDatabase(container)"> + <a :href="`/container/${container.id}/database/${container.database.id}`">{{ container.name }}</a> + </v-card-title> + <v-card-subtitle v-if="!hasIdentifier(container)" class="db-subtitle" v-text="formatCreator(container.creator)" /> + <v-card-subtitle v-if="hasIdentifier(container)" class="db-subtitle" v-text="formatCreatorz(container)" /> + <v-card-text class="db-description"> + <div class="db-tags"> + <v-chip v-if="hasDatabase(container) && container.database.is_public" small color="green" outlined>Public</v-chip> + <v-chip v-if="hasDatabase(container) && !container.database.is_public" small color="red" outlined>Private</v-chip> + <v-chip v-if="hasIdentifier(container)" small outlined>PID</v-chip> + <v-chip + v-if="identifierCreated(container)" + small + outlined + v-text="container.database.identifier.publisher" /> + </div> + <div v-text="identifierDescription(container)" /> + </v-card-text> + <v-card-text v-if="canInit(container)" class="db-buttons"> + <v-btn + small + secondary + :loading="container.database.loading" + @click.stop="initDatabase(container)"> + Start + </v-btn> + </v-card-text> + <v-divider v-if="idx - 1 === databases.length" class="mx-4" /> + </v-card> + <v-toolbar v-if="false" flat> + <v-toolbar-title> + <v-btn + small + color="secondary"> + More + </v-btn> + </v-toolbar-title> + </v-toolbar> + </div> +</template> + +<script> +import { formatCreators, formatUser, formatYearUTC, isResearcher } from '@/utils' + +export default { + data () { + return { + loadingContainers: false, + loadingCreate: false, + createDbDialog: false, + databases: [], + containers: [], + searchQuery: null, + limit: 100, + createDatabaseDto: { + name: null, + is_public: true + }, + items: [ + { text: 'Databases', to: '/container', activeClass: '' } + ], + loadingDatabases: false, + error: false + } + }, + computed: { + loadingColor () { + return this.error ? 'red lighten-2' : 'primary' + }, + token () { + return this.$store.state.token + }, + user () { + return this.$store.state.user + }, + isResearcher () { + return isResearcher(this.user) + }, + config () { + if (this.token === null) { + return {} + } + return { + headers: { Authorization: `Bearer ${this.token}` } + } + } + }, + mounted () { + this.loadContainers() + }, + methods: { + formatCreator (creator) { + return formatUser(creator) + }, + formatCreatorz (container) { + const creators = formatCreators(container) + return creators || this.formatCreator(container.creator) + }, + canInit (container) { + if (!this.token) { + return false + } + if (container.creator.username !== this.user.username) { + return false + } + return !container.database.id && !this.loadingDatabases + }, + hasDatabase (container) { + return container.database + }, + hasIdentifier (container) { + return container.database && container.database.identifier + }, + async initDatabase (container) { + await this.startContainer(container) + .then(() => this.createDatabase(container)) + }, + identifierCreated (container) { + if (!container.database.identifier) { + return null + } + return formatYearUTC(container.database.identifier.created) + }, + identifierDescription (container) { + if (!container.database.identifier) { + return null + } + return container.database.identifier.description + }, + async loadContainers () { + this.createDbDialog = false + try { + this.loadingContainers = true + const res = await this.$axios.get(`/api/container?limit=${this.limit}`) + this.containers = res.data + console.debug('containers', this.containers) + this.error = false + } catch (error) { + this.error = true + console.error('Failed to retrieve containers', error) + const { message } = error.response.data + this.$toast.error(`Failed to retrieve containers: ${message}`) + } + this.loadingContainers = false + }, + async startContainer (container) { + try { + container.database.loading = true + const res = await this.$axios.put(`/api/container/${container.id}`, { action: 'start' }, this.config) + console.debug('started container', res.data) + this.error = false + } catch (error) { + const { status } = error.response + if (status !== 409) { + this.error = true + this.$toast.error('Failed to start container') + } + } + container.database.loading = false + }, + link (container) { + if (!container.database || !container.database.id) { + return null + } + return `/container/${container.id}/database/${container.database.id}` + } + } +} +</script> + +<style> +.v-chip:not(:first-child) { + margin-left: 8px; +} +.db-subtitle { + padding-bottom: 8px; +} +.db-tags { + margin-bottom: 8px; +} +</style> diff --git a/fda-ui/components/QueryList.vue b/fda-ui/components/QueryList.vue index 96227d2816243e881c2a21d01b2a5702e5474041..5e5f6772b115f0755ff1c138b05a823ba1eed2ce 100644 --- a/fda-ui/components/QueryList.vue +++ b/fda-ui/components/QueryList.vue @@ -2,9 +2,6 @@ <div> <v-progress-linear v-if="loadingIdentifiers || loadingQueries || error" :color="loadingColor" :value="loadProgress" /> <v-tabs-items> - <v-card v-if="!loadingQueries && queries.length === 0 && !error" flat> - <v-card-text v-text="emptyMessage" /> - </v-card> <div v-if="!loadingQueries && !error"> <div v-for="(item,i) in queries" :key="i"> <v-divider v-if="i !== 0" class="mx-4" /> @@ -16,12 +13,15 @@ <pre>{{ item.query }}</pre> </v-list-item-subtitle> </v-list-item-content> + <v-list-item-action v-if="item.identifier"> + <v-icon color="primary">mdi-identifier</v-icon> + </v-list-item-action> </v-list-item> </v-list-item-group> </div> </div> <div v-if="!loadingIdentifiers && loadingQueries"> - <!-- show identifiers when error --> + <!-- show identifiers when loading subsets --> <div v-for="(item,i) in identifiers" :key="i"> <v-divider v-if="i !== 0" class="mx-4" /> <v-list-item-group> @@ -32,6 +32,28 @@ <pre>{{ item.query }}</pre> </v-list-item-subtitle> </v-list-item-content> + <v-list-item-action> + <v-icon color="primary">mdi-identifier</v-icon> + </v-list-item-action> + </v-list-item> + </v-list-item-group> + </div> + </div> + <div v-if="!loadingIdentifiers && !isPublicOrOwner"> + <!-- show identifiers when private --> + <div v-for="(item,i) in identifiers" :key="i"> + <v-divider v-if="i !== 0" class="mx-4" /> + <v-list-item-group> + <v-list-item two-line :class="clazz(item)" :to="link(item)" :href="navigate(item)"> + <v-list-item-content> + <v-list-item-title v-text="item.title" /> + <v-list-item-subtitle class="mt-2"> + <pre>{{ item.query }}</pre> + </v-list-item-subtitle> + </v-list-item-content> + <v-list-item-action> + <v-icon color="primary">mdi-identifier</v-icon> + </v-list-item-action> </v-list-item> </v-list-item-group> </div> @@ -81,11 +103,20 @@ export default { creator () { return this.queryDetails.creator }, - emptyMessage () { - if (this.isPublicOrOwner()) { - return '(no subsets)' + isPublicOrOwner () { + if (!this.database) { + return false + } + if (this.database.is_public) { + return true + } + if (this.token === null) { + return false } - return '(private database)' + if (!this.user) { + return false + } + return this.database.creator.username === this.user.username } }, mounted () { @@ -117,10 +148,14 @@ export default { const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/query?persisted=true`, this.config) this.queries = res.data console.debug('queries', this.queries) - } catch (err) { - this.error = true - console.error('Connection to query store failed', err.response.index) - this.$toast.error(err.response.index.message) + } catch (error) { + const { status, data } = error.response + const { message } = data + if (status !== 405) { + this.error = true + console.error('Connection to query store failed', error) + this.$toast.error(`Failed to connect to query store: ${message}`) + } } this.loadingQueries = false }, @@ -149,29 +184,11 @@ export default { return `/pid/${queryOrIdentifier.identifier.id}` }, clazz (queryOrIdentifier) { - if (!('identifier' in queryOrIdentifier) || queryOrIdentifier.identifier === null) { - return null - } if ('query_id' in queryOrIdentifier || queryOrIdentifier.identifier) { return 'primary--text' } return null }, - isPublicOrOwner () { - if (!this.database) { - return false - } - if (this.database.is_public) { - return true - } - if (this.token === null) { - return false - } - if (!this.user) { - return false - } - return this.database.creator.username === this.user.username - }, simulateProgress () { if (this.loadProgress !== 0) { return diff --git a/fda-ui/components/TableList.vue b/fda-ui/components/TableList.vue index c326f81505cee79b2c62bec9635bd4514f916a4b..456a8f6b087409e8a728ac0acc20830706db27a0 100644 --- a/fda-ui/components/TableList.vue +++ b/fda-ui/components/TableList.vue @@ -9,7 +9,9 @@ <div v-for="(item,i) in tables" :key="i"> <v-divider v-if="i !== 0" class="mx-4" /> <v-list-item-group> - <v-list-item two-line :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${item.id}`"> + <v-list-item + two-line + :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${item.id}`"> <v-list-item-content> <v-list-item-title v-text="item.name" /> <v-list-item-subtitle class="mt-2" v-text="item.description" /> @@ -113,29 +115,11 @@ export default { } return formatTimestampUTCLabel(this.tableDetails.created) }, - hasReadAccess () { - if (!this.database) { - return false - } - if (this.database.is_public) { - /* database is public */ - return true - } - if (!this.user) { + canRead () { + if (!this.user || !this.access) { return false } - if (this.database.creator.username === this.user.username) { - /* user is creator of database */ - return true - } - if (!this.access) { - return false - } - if (this.access.type === 'read' || this.access.type === 'write_own' || this.access.type === 'write_all') { - /* user has some level of access */ - return true - } - return false + return this.access.type === 'read' || this.access.type === 'write_own' || this.access.type === 'write_all' }, canModify () { if (!this.token || !this.user.username) { @@ -171,7 +155,7 @@ export default { /* use cache */ this.tableDetails = table /* load remaining info */ - if (this.hasReadAccess) { + if (this.canRead) { try { this.loadingDetails = true const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${table.id}`, this.config) diff --git a/fda-ui/components/TableToolbar.vue b/fda-ui/components/TableToolbar.vue index 350f5a1e50327094dbe0ac3865882cb8972d58e8..c138b2e7dcb4337733f04beb6ae07d9719edcdb3 100644 --- a/fda-ui/components/TableToolbar.vue +++ b/fda-ui/components/TableToolbar.vue @@ -35,7 +35,7 @@ <v-tab :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/info`"> Info </v-tab> - <v-tab :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/data`"> + <v-tab v-if="canRead" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/data`"> Data </v-tab> <v-tab :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/table/${$route.params.table_id}/schema`"> diff --git a/fda-ui/components/ViewList.vue b/fda-ui/components/ViewList.vue index ef511dbd6e10ca85f502d5adb00c45c1c71b0219..b2aff93d4e03c52af606c7792d50141af0dff87a 100644 --- a/fda-ui/components/ViewList.vue +++ b/fda-ui/components/ViewList.vue @@ -9,7 +9,7 @@ <div v-for="(item,i) in views" :key="i"> <v-divider v-if="i !== 0" class="mx-4" /> <v-list-item-group> - <v-list-item two-line :class="clazz(item)" :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/view/${item.id}`"> + <v-list-item two-line :to="`/container/${$route.params.container_id}/database/${$route.params.database_id}/view/${item.id}`"> <v-list-item-content> <v-list-item-title v-text="item.name" /> <v-list-item-subtitle class="mt-2"> @@ -127,12 +127,6 @@ export default { this.$toast.error('Failed to delete view') console.error('Failed to delete view') } - }, - clazz (view) { - if (view.is_public === false) { - return null - } - return 'primary--text' } } } diff --git a/fda-ui/components/dialogs/EditTuple.vue b/fda-ui/components/dialogs/EditTuple.vue index b5443874a9f33e66aeaf97b80831f8583d34fdbd..a3bf26d5f60d32e26954e764d98a33d233e92716 100644 --- a/fda-ui/components/dialogs/EditTuple.vue +++ b/fda-ui/components/dialogs/EditTuple.vue @@ -1,5 +1,5 @@ <template> - <div v-if="tuple"> + <div v-if="localTuple"> <v-form ref="form" v-model="valid" @submit.prevent="submit"> <v-card> <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" /> @@ -8,7 +8,7 @@ <div v-for="(attr,idx) in columns" :key="idx"> <v-text-field v-if="attr.column_type === 'number'" - v-model.number="tuple[attr.internal_name]" + v-model.number="localTuple[attr.internal_name]" :disabled="(!edit && attr.auto_generated)" class="mb-2" :hint="hint(attr)" @@ -19,7 +19,7 @@ type="number" /> <v-text-field v-if="attr.column_type === 'string' || attr.column_type === 'text' || attr.column_type === 'decimal'" - v-model="tuple[attr.internal_name]" + v-model="localTuple[attr.internal_name]" :disabled="(edit && attr.is_primary_key) || (!edit && attr.auto_generated)" class="mb-2" :rules="(attr.is_null_allowed || attr.auto_generated) ? [] : [ v => !!v || $t('Required') ]" @@ -28,7 +28,7 @@ type="text" /> <v-text-field v-if="attr.column_type === 'timestamp'" - v-model="tuple[attr.internal_name]" + v-model="localTuple[attr.internal_name]" suffix="UTC" hint="e.g. 2022-07-12 18:32:59" :rules="(attr.auto_generated) ? [] : (attr.is_null_allowed ? [ validateTimestamp ] : [validateTimestamp || $t('Required format yyyy-MM-dd HH:mm:ss'), v => !!v || $t('Required')])" @@ -46,7 +46,7 @@ min-width="auto"> <template v-slot:activator="{ on, attrs }"> <v-text-field - v-model="tuple[attr.internal_name]" + v-model="localTuple[attr.internal_name]" :label="label(attr)" suffix="UTC" readonly @@ -54,25 +54,27 @@ v-on="on" /> </template> <v-date-picker - v-model="tuple[attr.internal_name]" + v-model="localTuple[attr.internal_name]" color="primary" no-title scrollable /> </v-menu> <v-select v-if="attr.column_type === 'ENUM'" - v-model="tuple[attr.internal_name]" + v-model="localTuple[attr.internal_name]" class="mb-2" :rules="(attr.is_null_allowed || attr.auto_generated) ? [] : [ v => !!v || $t('Required') ]" :required="required(attr)" :items="attr.enum_values" :label="label(attr)" /> - <v-checkbox + <v-select v-if="attr.column_type === 'boolean'" - v-model="tuple[attr.internal_name]" - :rules="(attr.is_null_allowed || attr.auto_generated) ? [] : [ v => !!v || $t('Required') ]" - :required="required(attr)" + v-model="localTuple[attr.internal_name]" class="mb-2" + :rules="(attr.is_null_allowed || attr.auto_generated) ? [] : [ v => v !== null || $t('Required') ]" + :required="required(attr)" + :items="bools" + :clearable="attr.is_null_allowed" :label="label(attr)" /> </div> </v-card-text> @@ -127,7 +129,12 @@ export default { loading: false, error: false, menu: false, - columns: this.$parent.$parent.$parent.$parent.table.columns + columns: this.$parent.$parent.$parent.$parent.table.columns, + localTuple: null, + bools: [ + { text: 'true', value: true }, + { text: 'false', value: false } + ] } }, computed: { @@ -141,6 +148,14 @@ export default { return (this.edit ? 'Edit' : 'Add') + ' tuple' } }, + watch: { + tuple (val) { + this.localTuple = val + } + }, + mounted () { + this.localTuple = Object.assign({}, this.tuple) + }, methods: { submit () { this.$refs.form.validate() @@ -178,7 +193,7 @@ export default { constraints[c.internal_name] = this.tuple[c.internal_name] }) const data = { - data: this.tuple, + data: this.localTuple, keys: constraints } try { @@ -188,9 +203,10 @@ export default { console.info('update result') this.$toast.success('Successfully updated tuple!') this.$emit('close', { success: true }) - } catch (err) { - console.error('Failed to update tuple', err) - this.$toast.error('Failed to update tuple') + } catch (error) { + console.error('Failed to update tuple', error) + const { message } = error.response.data + this.$toast.error('Failed to update tuple: ' + message) } }, async addTuple () { @@ -198,7 +214,7 @@ export default { this.columns .filter(c => c.is_primary_key) .forEach((c) => { - constraints[c.internal_name] = this.tuple[c.internal_name] + constraints[c.internal_name] = this.localTuple[c.internal_name] }) try { const res = await this.$axios.post(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data`, { diff --git a/fda-ui/components/dialogs/Persist.vue b/fda-ui/components/dialogs/Persist.vue index f57c31ec2234cc99d62751787255285a492b1a16..a8595e18fd1151270c670c256f2509997c799b63 100644 --- a/fda-ui/components/dialogs/Persist.vue +++ b/fda-ui/components/dialogs/Persist.vue @@ -64,6 +64,19 @@ required /> </v-col> </v-row> + <v-row dense> + <v-col> + <v-select + id="visibility" + v-model="visibility" + name="visibility" + label="Visibility *" + :items="['Public']" + disabled + :rules="[v => !!v || $t('Required')]" + required /> + </v-col> + </v-row> <v-row v-for="(creator,i) in identifier.creators" :key="`c-${i}`" dense> <v-col cols="3"> <v-text-field @@ -200,6 +213,7 @@ export default { loading: false, error: false, // XXX: `error` is never changed licenses: [], + visibility: 'Public', identifier: { cid: parseInt(this.$route.params.container_id), dbid: parseInt(this.$route.params.database_id), diff --git a/fda-ui/components/query/Builder.vue b/fda-ui/components/query/Builder.vue index a624a3809aec061902fca8e895972bc2ce38dce6..311aac14f2dce772d544e29c11b558dab57e9e9e 100644 --- a/fda-ui/components/query/Builder.vue +++ b/fda-ui/components/query/Builder.vue @@ -209,10 +209,6 @@ export default { } }, computed: { - selectItems () { - const columns = this.tableDetails && this.tableDetails.columns - return columns || [] - }, columnNames () { return this.columns && this.columns.map(s => s.internal_name) }, @@ -334,7 +330,7 @@ export default { this.$toast.error(err.response.data.message) } this.loadingQuery = false - await this.$refs.queryResults.reExecute(this.resultId) + await Promise.all([this.$refs.queryResults.reExecute(this.resultId), this.$refs.queryResults.reExecuteCount(this.resultId)]) }, async buildQuery () { if (!this.table) { diff --git a/fda-ui/components/query/Results.vue b/fda-ui/components/query/Results.vue index 4d52923b58edcb2e02d63c055ee77dea183b5195..d04b59378614effed3b3a6595bbb912f13c164bc 100644 --- a/fda-ui/components/query/Results.vue +++ b/fda-ui/components/query/Results.vue @@ -3,7 +3,7 @@ flat :headers="result.headers" :items="result.rows" - :loading="loading" + :loading="loading > 0" :options.sync="options" :server-items-length="total" /> </template> @@ -18,7 +18,7 @@ export default { }, data () { return { - loading: false, + loading: 0, resultId: null, id: null, result: { @@ -29,7 +29,7 @@ export default { page: 1, itemsPerPage: 10 }, - total: 0 + total: -1 } }, computed: { @@ -60,7 +60,7 @@ export default { }, methods: { async executeFirstTime (parent, sql, timestamp) { - this.loading = true + this.loading++ try { const res = await this.$axios.post(this.executeUrl, { statement: sql, timestamp }, this.config) console.debug('query result', res.data) @@ -80,7 +80,7 @@ export default { } this.error = true } - this.loading = false + this.loading-- }, buildHeaders (firstLine) { return Object.keys(firstLine).map(k => ({ @@ -92,13 +92,16 @@ export default { reExecuteUrl (resultId) { const page = this.options.page - 1 const urlParams = `page=${page}&size=${this.options.itemsPerPage}` - return `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}` + (this.type === 'view' ? '/view' : '/query') + `/${resultId}/data?${urlParams}` + return `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/${this.type}/${resultId}/data?${urlParams}` + }, + reExecuteCountUrl (resultId) { + return `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/${this.type}/${resultId}/data/count` }, async reExecute (id) { if (id === null) { return } - this.loading = true + this.loading++ try { const res = await this.$axios.get(this.reExecuteUrl(id), this.config) this.mapResults(res.data) @@ -107,7 +110,21 @@ export default { console.error('failed to execute query', error) this.error = true } - this.loading = false + this.loading-- + }, + async reExecuteCount (id) { + if (id === null) { + return + } + this.loading++ + try { + const res = await this.$axios.get(this.reExecuteCountUrl(id), this.config) + this.total = res.data + } catch (error) { + console.error('failed to execute query count', error) + this.error = true + } + this.loading-- }, mapResults (data) { if (data.result.length) { @@ -115,7 +132,9 @@ export default { } console.debug('query result', data) this.result.rows = data.result - this.total = data.result_number + if (this.total < 0 && data.result_number != null) { + this.total = data.result_number + } } } } diff --git a/fda-ui/layouts/default.vue b/fda-ui/layouts/default.vue index 03887d4bed746416a37d0271c9f32ded815e797b..a2ced808cfa3cfd0e8fd39f7b19493682d1b2079 100644 --- a/fda-ui/layouts/default.vue +++ b/fda-ui/layouts/default.vue @@ -50,7 +50,7 @@ single-line hide-details placeholder="Search ..." /> - <v-btn icon class="ml-2" type="submit" @click="retrieve"> + <v-btn icon class="ml-2" type="submit" name="search-submit" @click="retrieve"> <v-icon>mdi-magnify</v-icon> </v-btn> <v-spacer /> @@ -227,8 +227,10 @@ export default { this.loadUser() this.setTheme() this.loadDatabase() - .then(() => this.loadIdentifier()) - this.loadTable() + .then(() => { + this.loadIdentifier() + this.loadTable() + }) this.loadAccess() if (this.$route.query && this.$route.query.q) { this.search = this.$route.query.q @@ -298,9 +300,16 @@ export default { const res = await this.$axios.get(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}`, this.config) this.$store.commit('SET_TABLE', res.data) console.debug('table', this.table) - } catch (err) { - console.error('Could not load table', err) - this.$toast.error('Could not load table') + } catch (error) { + const { status } = error.response + if (status === 405) { + const table = this.database.tables.filter(t => t.id === Number(this.$route.params.table_id))[0] + this.$store.commit('SET_TABLE', table) + } else { + const { message } = error.response.data + console.error('Failed to load table', error) + this.$toast.error(`Failed to load table: ${message}`) + } } this.loading = false }, @@ -327,7 +336,7 @@ export default { this.loading = false }, async loadIdentifier () { - if ('identifier' in this.database) { + if (!this.database || 'identifier' in this.database) { return } try { diff --git a/fda-ui/package.json b/fda-ui/package.json index de4d543b8929a002388db5d4e3a55e9cc2e96bfa..1649177b07b97389c2827d1c565f2db4f7e77ca9 100644 --- a/fda-ui/package.json +++ b/fda-ui/package.json @@ -8,15 +8,16 @@ "build": "nuxt build", "start": "nuxt start", "generate": "nuxt generate", + "clean": "rm -f ./videos/*", "ver": "nuxt --version", "lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .", "lint": "yarn lint:js", - "coverage": "nyc ava test/unit/**/* test/specs/**/*", + "coverage": "nyc ava test/unit/**/* test/specs/**/* > ./coverage.txt", "test:unit": "ava test/unit/**/* test/specs/**/*", "test:watch": "ava --watch test/unit/**/* test/specs/**/*", - "test:e2e": "cross-env PORT=3001 ava --timeout=2h --fail-fast test/e2e/**", + "test:e2e": "yarn clean && cross-env PORT=3001 ava --timeout=2h --fail-fast test/e2e/**", "test:e2e:docker": "cross-env PORT=3000 SLOWMO=500 ava --timeout=2h --fail-fast test/e2e/**", - "test": "yarn test:unit && yarn test:e2e:docker" + "test": "yarn clean && yarn test:unit && yarn test:e2e:docker && yarn coverage" }, "dependencies": { "@babel/plugin-transform-runtime": "^7.13.9", @@ -25,6 +26,7 @@ "@nuxtjs/eslint-module": "^2.0.0", "@nuxtjs/proxy": "^2.1.0", "@nuxtjs/vuetify": "^1.11.2", + "axios": "^1.3.0", "chart.js": "^3.8.0", "core-js": "^3.6.5", "date-fns": "^2.16.1", diff --git a/fda-ui/pages/container/_container_id/database/_database_id/info.vue b/fda-ui/pages/container/_container_id/database/_database_id/info.vue index 6b4849304911ec51016292a87a7034d335a04d43..b9224362e62047fb60aa8b81f72f49b67b949b53 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/info.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/info.vue @@ -114,33 +114,7 @@ </v-card-actions> </v-card-text> </v-card> - <v-divider v-if="hasIdentifier || (isCreator && isResearcher) || isDataSteward" /> - <v-card flat tile> - <v-card-title>Container</v-card-title> - <v-card-text> - <v-list dense> - <v-list-item> - <v-list-item-content> - <v-list-item-title class="mt-2"> - Container Name - </v-list-item-title> - <v-list-item-content> - <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" /> - <span v-if="!loading" v-text="container_name" /> - </v-list-item-content> - <v-list-item-title class="mt-2"> - Container Internal Name - </v-list-item-title> - <v-list-item-content> - <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" /> - <span v-if="!loading" v-text="container_internal_name" /> - </v-list-item-content> - </v-list-item-content> - </v-list-item> - </v-list> - </v-card-text> - </v-card> - <v-divider /> + <v-divider v-if="isDataSteward || hasIdentifier || (!hasIdentifier && isCreator && isResearcher)" /> <v-card flat tile> <v-card-title>Database</v-card-title> <v-card-text> @@ -205,6 +179,32 @@ </v-list> </v-card-text> </v-card> + <v-divider /> + <v-card flat tile> + <v-card-title>Container</v-card-title> + <v-card-text> + <v-list dense> + <v-list-item> + <v-list-item-content> + <v-list-item-title class="mt-2"> + Container Name + </v-list-item-title> + <v-list-item-content> + <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" /> + <span v-if="!loading" v-text="container_name" /> + </v-list-item-content> + <v-list-item-title class="mt-2"> + Container Internal Name + </v-list-item-title> + <v-list-item-content> + <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" /> + <span v-if="!loading" v-text="container_internal_name" /> + </v-list-item-content> + </v-list-item-content> + </v-list-item> + </v-list> + </v-card-text> + </v-card> </v-tab-item> </v-tabs-items> <v-dialog diff --git a/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue b/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue index 97cc6d16ceda3ed5d6f4c3742e4a27d175f9fc6d..4ca589d1527db7c2bd3eea1a845348c3f3d2d9c9 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue @@ -336,6 +336,9 @@ export default { if (this.query.creator.username === this.username) { return true } + if (!this.query.identifier) { + return false + } return this.query.identifier.visibility === 'everyone' }, canWrite () { @@ -367,6 +370,7 @@ export default { methods: { loadResult () { this.$refs.queryResults.reExecute(this.query.id) + this.$refs.queryResults.reExecuteCount(this.query.id) }, async download (mime) { if (mime === 'text/csv') { diff --git a/fda-ui/pages/container/_container_id/database/_database_id/settings.vue b/fda-ui/pages/container/_container_id/database/_database_id/settings.vue index 63f3d69a7d1a6a5b5c72c760e6cbd7c2add615f6..a22d9092ede9f2dca88c0e21c619011358dc403e 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/settings.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/settings.vue @@ -174,7 +174,7 @@ export default { async updateDatabaseVisibility () { try { this.loading = true - await this.$axios.put(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/transfer`, this.modifyVisibility, this.config) + await this.$axios.put(`/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/visibility`, this.modifyVisibility, this.config) this.$toast.success('Successfully updated the database') location.reload() } catch (err) { diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue index f041e3630467686d5afe0b2c834ca0fd319fa75b..17f58d3b1ed4fea81a5bb3f5aee217b26b2df852 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/data.vue @@ -1,5 +1,5 @@ <template> - <div> + <div v-if="canRead"> <TableToolbar :selection="selection" @modified="modified" /> <v-toolbar :color="versionColor" flat> <v-toolbar-title> @@ -23,7 +23,7 @@ </v-toolbar-title> </v-toolbar> <v-card tile> - <v-progress-linear v-if="loadingData || error" :value="loadProgress" :color="error ? 'error' : 'primary'" /> + <v-progress-linear v-if="loadingData > 0 || error" :value="loadProgress" :color="error ? 'error' : 'primary'" /> <v-data-table :headers="headers" :items="rows" @@ -41,7 +41,7 @@ <script> import TimeTravel from '@/components/dialogs/TimeTravel' import TableToolbar from '@/components/TableToolbar' -import { formatTimestampUTCLabel, formatDateUTC, formatTimestamp } from '@/utils' +import { formatTimestampUTC, formatDateUTC, formatTimestamp } from '@/utils' export default { components: { @@ -51,10 +51,10 @@ export default { data () { return { loading: true, - loadingData: true, + loadingData: 0, loadProgress: 0, editTupleDialog: false, - total: 0, + total: -1, footerProps: { 'items-per-page-options': [10, 20, 30, 40, 50] }, @@ -64,6 +64,7 @@ export default { selection: [], pickVersionDialog: null, version: null, + lastReload: new Date(), tab: null, error: false, // XXX: `error` is never changed options: { @@ -165,7 +166,7 @@ export default { watch: { version (newVersion, oldVersion) { console.info('selected new version', newVersion) - this.loadData() + this.reload() }, options () { this.loadData() @@ -177,7 +178,7 @@ export default { } }, mounted () { - this.loadData() + this.reload() this.simulateProgress() this.loadProperties() }, @@ -254,19 +255,22 @@ export default { this.selection = [] } if (success) { - this.loadData() + this.reload() } }, + reload () { + this.lastReload = new Date() + this.loadData() + this.loadCount() + }, async loadData () { try { - this.loadingData = true - let url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data?page=${this.options.page - 1}&size=${this.options.itemsPerPage}` + this.loadingData++ + const url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data?page=${this.options.page - 1}&size=${this.options.itemsPerPage}×tamp=${this.versionISO || this.lastReload.toISOString()}` if (this.version !== null) { console.info('versioning active', this.version) - url += `×tamp=${this.versionISO}` } const res = await this.$axios.get(url, this.config) - this.total = parseInt(res.headers['fda-count']) this.rows = res.data.result.map((row) => { for (const col in row) { const columnDefinition = this.dateColumns.filter(c => c.internal_name === col) @@ -274,7 +278,7 @@ export default { if (columnDefinition[0].column_type === 'date') { row[col] = formatDateUTC(row[col]) } else if (columnDefinition[0].column_type === 'timestamp') { - row[col] = formatTimestampUTCLabel(row[col]) + row[col] = formatTimestampUTC(row[col]) } } } @@ -294,8 +298,36 @@ export default { console.error('Failed to load data', code) this.$toast.error('Failed to load data: ' + message) } + } finally { + this.loadingData-- + } + }, + async loadCount () { + try { + this.loadingData++ + const url = `/api/container/${this.$route.params.container_id}/database/${this.$route.params.database_id}/table/${this.$route.params.table_id}/data/count?timestamp=${this.versionISO || this.lastReload.toISOString()}` + if (this.version !== null) { + console.info('versioning active', this.version) + } + const res = await this.$axios.get(url, this.config) + this.total = res.data + console.info('total', this.total) + } catch (error) { + console.error('Failed to load count', error) + this.error = true + this.loadProgress = 100 + const { status, data } = error.response + const { message, code } = data + if (status === 423) { + console.error('Database is offline', code) + this.$toast.error('Database is offline: ' + message) + } else { + console.error('Failed to load data', code) + this.$toast.error('Failed to load data: ' + message) + } + } finally { + this.loadingData-- } - this.loadingData = false }, simulateProgress () { if (this.loadProgress !== 0) { diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/info.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/info.vue index 50e51aaafa3be94accfb3769bc8b58875acd0186..9a27f6db5cac79fa2ea91b15ffd0c1f441aa98d0 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/info.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/info.vue @@ -31,16 +31,18 @@ <span v-if="table && table.creator">{{ formatCreator(table.creator) }} <span v-if="is_owner(table)" style="flex:none;"> (you)</span></span> <v-skeleton-loader v-if="!table" type="text" class="skeleton-medium" /> </v-list-item-content> - <v-list-item-title class="mt-2"> + <v-list-item-title v-if="table && table.created" class="mt-2"> Table Creation </v-list-item-title> - <v-list-item-content> - <span v-if="table && table.created">{{ createdUTC }}</span> - <v-skeleton-loader v-if="!table" type="text" class="skeleton-small" /> + <v-list-item-content v-if="table && table.created"> + <span>{{ createdUTC }}</span> + </v-list-item-content> + <v-list-item-content v-if="!table"> + <v-skeleton-loader type="text" class="skeleton-medium" /> </v-list-item-content> </v-list-item-content> </v-list-item> - <v-list-item> + <v-list-item v-if="canModify"> <v-list-item-icon> <v-icon>mdi-rabbit</v-icon> </v-list-item-icon> @@ -58,29 +60,35 @@ <pre v-if="database && database.exchange_name">{{ database.exchange_name }}</pre> <v-skeleton-loader v-if="!table" type="text" class="skeleton-medium" /> </v-list-item-content> - <v-list-item-title class="mt-2"> + <v-list-item-title v-if="table && table.queue_name" class="mt-2"> Queue Name </v-list-item-title> - <v-list-item-content> - <pre v-if="table && table.queue_name">{{ table.queue_name }}</pre> - <v-skeleton-loader v-if="!table" type="text" class="skeleton-medium" /> + <v-list-item-content v-if="table && table.queue_name"> + <pre>{{ table.queue_name }}</pre> </v-list-item-content> - <v-list-item-title class="mt-2"> + <v-list-item-content v-if="!table"> + <v-skeleton-loader type="text" class="skeleton-medium" /> + </v-list-item-content> + <v-list-item-title v-if="table && table.routing_key" class="mt-2"> Routing Key </v-list-item-title> - <v-list-item-content> - <pre v-if="table && table.routing_key">{{ table.routing_key }}</pre> - <v-skeleton-loader v-if="!table" type="text" class="skeleton-medium" /> + <v-list-item-content v-if="table && table.routing_key"> + <pre>{{ table.routing_key }}</pre> </v-list-item-content> - <v-list-item-title v-if="hasReadAccess" class="mt-2"> + <v-list-item-content v-if="!table"> + <v-skeleton-loader type="text" class="skeleton-medium" /> + </v-list-item-content> + <v-list-item-title v-if="canRead" class="mt-2"> Consumer Count </v-list-item-title> - <v-list-item-content v-if="hasReadAccess" class="amqp-consumer"> - <span v-text="`${consumersUp}/${consumersTotal}`" /> + <v-list-item-content v-if="canRead" class="amqp-consumer"> + <span v-if="attemptedLoadingConsumers" v-text="`${consumersUp}/${consumersTotal}`" /> <v-badge + v-if="attemptedLoadingConsumers" class="ml-1" :color="consumersState.color" :content="consumersState.text" /> + <v-skeleton-loader v-else type="text" class="skeleton-xsmall" /> </v-list-item-content> </v-list-item-content> </v-list-item> @@ -101,6 +109,7 @@ export default { data () { return { loadingConsumers: false, + attemptedLoadingConsumers: false, selection: [], consumers: [], items: [ @@ -137,29 +146,11 @@ export default { table () { return this.$store.state.table }, - hasReadAccess () { - if (!this.database) { - return false - } - if (this.database.is_public) { - /* database is public */ - return true - } - if (!this.user) { + canRead () { + if (!this.user || !this.access) { return false } - if (this.database.creator.username === this.user.username) { - /* user is creator of database */ - return true - } - if (!this.access) { - return false - } - if (this.access.type === 'read' || this.access.type === 'write_own' || this.access.type === 'write_all') { - /* user has some level of access */ - return true - } - return false + return this.access.type === 'read' || this.access.type === 'write_own' || this.access.type === 'write_all' }, createdUTC () { if (this.table.created === undefined || this.table.created === null) { @@ -192,7 +183,7 @@ export default { return this.consumers.filter(c => c.active).length }, canModify () { - if (!this.token || !this.user.username) { + if (!this.token || !this.user.username || !this.table || !('creator' in this.table) || !this.table.creator || !('username' in this.table.creator) || !this.table.creator.username) { /* not yet loaded */ return false } @@ -223,9 +214,11 @@ export default { } } }, - mounted () { - this.pollConsumerStatus(true) - setInterval(() => this.pollConsumerStatus(false), 5 * 1000) + watch: { + table (val) { + this.pollConsumerStatus(true) + setInterval(() => this.pollConsumerStatus(false), 5 * 1000) + } }, methods: { formatCreator (creator) { @@ -247,10 +240,13 @@ export default { const consumers = res.data.filter(c => c.queue.name === this.table.queue_name) console.debug('filtered', consumers) this.consumers = consumers - } catch (err) { - console.error('Could not find consumers', err) + } catch (error) { + const { message } = error + console.error('Failed to find consumers', error) + this.$toast.error(`Failed to find consumers: ${message}`) } this.loadingConsumers = false + this.attemptedLoadingConsumers = true } } } diff --git a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/schema.vue b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/schema.vue index 712dafbd367544bcb34a374d75527b8173dd3e26..72022b57c94af3625d59739cbba55c117b764262 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/schema.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/table/_table_id/schema.vue @@ -183,10 +183,10 @@ export default { return null }, hasUnit (item) { - return item.unit !== null + return item.unit && 'uri' in item.unit }, hasConcept (item) { - return item.concept !== null + return item.concept && 'uri' in item.concept }, pick (item) { this.column = item diff --git a/fda-ui/pages/container/_container_id/database/_database_id/view/_view_id/index.vue b/fda-ui/pages/container/_container_id/database/_database_id/view/_view_id/index.vue index 4d9df5a1e7658661fccfd4d2ffea501b54c0fdec..55930ae93e6856d48598213f02e4ded25c69ba24 100644 --- a/fda-ui/pages/container/_container_id/database/_database_id/view/_view_id/index.vue +++ b/fda-ui/pages/container/_container_id/database/_database_id/view/_view_id/index.vue @@ -191,6 +191,7 @@ export default { return } this.$refs.queryResults.reExecute(viewId) + this.$refs.queryResults.reExecuteCount(viewId) }, formatUTC (timestamp) { return formatTimestampUTCLabel(timestamp) diff --git a/fda-ui/pages/container/index.vue b/fda-ui/pages/container/index.vue index f2036ed2d7a95b6aa88f5c01e3189d6696130986..b439563e202f6e6eea3439da3efce5da89d885a9 100644 --- a/fda-ui/pages/container/index.vue +++ b/fda-ui/pages/container/index.vue @@ -2,67 +2,16 @@ <div> <v-toolbar flat> <v-toolbar-title> - <span>Databases</span> + Recent Databases </v-toolbar-title> <v-spacer /> <v-toolbar-title> - <v-btn v-if="isResearcher" color="primary" @click.stop="createDbDialog = true"> + <v-btn v-if="isResearcher" color="primary" name="create-database" @click.stop="createDbDialog = true"> <v-icon left>mdi-plus</v-icon> Database </v-btn> </v-toolbar-title> </v-toolbar> - <v-toolbar dense flat> - <v-toolbar-items> - <span class="mr-4">Filter:</span> - <v-checkbox - v-model="filterPrivate" - label="Private" /> - <v-checkbox - v-if="user" - v-model="filterMine" - class="ml-2" - label="Mine" /> - </v-toolbar-items> - </v-toolbar> - <v-progress-linear v-if="loadingContainers || loadingDatabases" :color="loadingColor" :indeterminate="!error" /> - <v-card - v-for="(container, idx) in filter(containers)" - :key="idx" - :to="link(container)" - :disabled="!container.database" - flat - tile> - <v-divider class="mx-4" /> - <v-card-title v-if="notInit(container)" v-text="container.name" /> - <v-card-title v-if="!notInit(container)"> - <a :href="`/container/${container.id}/database/${container.database.id}`">{{ container.name }}</a> - </v-card-title> - <v-card-subtitle v-if="!container.database.identifier" class="db-subtitle" v-text="formatCreator(container.creator)" /> - <v-card-subtitle v-if="container.database.identifier" class="db-subtitle" v-text="formatCreatorz(container)" /> - <v-card-text class="db-description"> - <div class="db-tags"> - <v-chip v-if="!notInit(container) && container.database.is_public" small color="green" outlined>Public</v-chip> - <v-chip v-if="!notInit(container) && !container.database.is_public" small color="red" outlined>Private</v-chip> - <v-chip v-if="identifierCreated(container)" small outlined>Database</v-chip> - <v-chip - v-if="identifierCreated(container)" - small - outlined - v-text="container.database.identifier.publisher" /> - <v-chip v-if="identifierCreated(container)" small outlined v-text="identifierCreated(container)" /> - </div> - <div v-text="identifierDescription(container)" /> - </v-card-text> - <v-card-text v-if="canInit(container)" class="db-buttons"> - <v-btn - small - secondary - :loading="container.database.loading" - @click.stop="initDatabase(container)"> - Start - </v-btn> - </v-card-text> - </v-card> + <DatabaseList ref="databases" /> <v-dialog v-model="createDbDialog" persistent @@ -75,22 +24,19 @@ <script> import { mdiDatabaseArrowRightOutline } from '@mdi/js' import CreateDB from '@/components/dialogs/CreateDB' -import { formatTimestampUTCLabel, formatCreators, formatYearUTC, formatUser, isResearcher } from '@/utils' +import DatabaseList from '@/components/DatabaseList' +import { isResearcher } from '@/utils' export default { components: { - CreateDB + CreateDB, + DatabaseList }, data () { return { loadingContainers: false, loadingCreate: false, createDbDialog: false, - databases: [], - containers: [], - filterPrivate: false, - filterMine: false, - searchQuery: null, createDatabaseDto: { name: null, is_public: true @@ -104,18 +50,12 @@ export default { } }, computed: { - loadingColor () { - return this.error ? 'red lighten-2' : 'primary' - }, token () { return this.$store.state.token }, user () { return this.$store.state.user }, - isResearcher () { - return isResearcher(this.user) - }, config () { if (this.token === null) { return {} @@ -123,119 +63,12 @@ export default { return { headers: { Authorization: `Bearer ${this.token}` } } + }, + isResearcher () { + return isResearcher(this.user) } }, - mounted () { - this.loadContainers() - .then(() => this.loadDatabases()) - }, methods: { - formatCreator (creator) { - return formatUser(creator) - }, - formatCreatorz (container) { - return formatCreators(container) - }, - canInit (container) { - if (!this.token) { - return false - } - if (container.creator.username !== this.user.username) { - return false - } - return !container.database.id && !this.loadingDatabases - }, - notInit (container) { - return !container.database.id - }, - async initDatabase (container) { - await this.startContainer(container) - .then(() => this.createDatabase(container)) - }, - filter (containers) { - if (this.loadingDatabases) { - return [] - } - let filtered = containers - if (this.filterPrivate) { - filtered = filtered.filter(c => !c.database.is_public) - } - if (this.user && this.filterMine) { - filtered = filtered.filter(c => c.creator.username === this.user.username) - } - return filtered - }, - identifierCreated (container) { - if (!container.database.identifier) { - return null - } - return formatYearUTC(container.database.identifier.created) - }, - identifierDescription (container) { - if (!container.database.identifier) { - return null - } - return container.database.identifier.description - }, - async loadContainers () { - this.createDbDialog = false - try { - this.loadingContainers = true - const res = await this.$axios.get('/api/container/') - this.containers = res.data.map((container) => { - container.database = { - id: null, - loading: false - } - return container - }) - console.debug('containers', this.containers) - this.error = false - } catch (err) { - console.error('containers', err) - this.error = true - } - this.loadingContainers = false - }, - async loadDatabases () { - if (this.containers.length === 0) { - return - } - this.loadingDatabases = true - for (const container of this.containers) { - try { - const res = await this.$axios.get(`/api/container/${container.id}/database`, this.config) - for (const info of res.data) { - info.container_id = container.id - info.visibility = info.is_public - info.created = formatTimestampUTCLabel(info.created) - const filtered = this.containers.filter(c => c.id === container.id)[0] - filtered.database = info - } - } catch (err) { - if (err.response === undefined || err.response.status === undefined || err.response.status !== 401) { - console.error('Failed to load databases for container', err) - } - } - } - this.loadingDatabases = false - console.debug('containers with databases', this.containers) - }, - async startContainer (container) { - try { - container.database.loading = true - const res = await this.$axios.put(`/api/container/${container.id}`, { action: 'start' }, this.config) - console.debug('started container', res.data) - this.error = false - } catch (error) { - const { status } = error.response - if (status !== 409) { - this.error = true - this.$toast.error('Failed to start container') - } - } - container.database.loading = false - }, async createDatabase (container) { try { container.database.loading = true @@ -255,30 +88,9 @@ export default { closed (event) { this.createDbDialog = false if (event.success) { - this.loadContainers() - .then(() => this.loadDatabases()) + this.$refs.databases.loadContainers() } - }, - link (container) { - if (!container.database || !container.database.id) { - return null - } - return `/container/${container.id}/database/${container.database.id}` } } } </script> -<style scoped> - tbody tr { - cursor: pointer; - } - .v-chip:not(:first-child) { - margin-left: 8px; - } - .db-subtitle { - padding-bottom: 8px; - } - .db-tags { - margin-bottom: 8px; - } -</style> diff --git a/fda-ui/pages/forgot.vue b/fda-ui/pages/forgot.vue index af20390b04503bc6484650995cb66b270b6b2f47..809139658b9b5bb04f1bb430a62b69ef4a03de14 100644 --- a/fda-ui/pages/forgot.vue +++ b/fda-ui/pages/forgot.vue @@ -18,6 +18,7 @@ v-model="data.username" autocomplete="off" autofocus + name="username" label="Username" /> </v-col> </v-row> @@ -27,6 +28,7 @@ v-model="data.email" autocomplete="off" type="email" + name="email" label="E-Mail Address" /> </v-col> </v-row> @@ -38,6 +40,7 @@ :disabled="!valid || !formValid" color="primary" type="submit" + name="submit" @click="forgot"> Reset password </v-btn> diff --git a/fda-ui/pages/login.vue b/fda-ui/pages/login.vue index c063503b9032528cfab050acd94800e930ed1a75..44ee3235cf09fd0e02f0e3e4eb09bd1f2a6a94fa 100644 --- a/fda-ui/pages/login.vue +++ b/fda-ui/pages/login.vue @@ -18,6 +18,7 @@ autocomplete="off" autofocus required + name="username" :rules="[v => !!v || $t('Required')]" label="Username *" /> </v-col> @@ -29,6 +30,7 @@ autocomplete="off" type="password" required + name="password" :rules="[v => !!v || $t('Required')]" label="Password *" /> </v-col> @@ -41,6 +43,7 @@ :disabled="!valid" color="primary" type="submit" + name="submit" :loading="loading" @click="login"> Login diff --git a/fda-ui/pages/search/index.vue b/fda-ui/pages/search/index.vue index a6d5489897dd90fa00ffa80b667cf9e2454b3dd5..a466a0f9229d395bfd03be5e671e250eec482079 100644 --- a/fda-ui/pages/search/index.vue +++ b/fda-ui/pages/search/index.vue @@ -46,6 +46,12 @@ export default { } return this.$route.query.q }, + type () { + if (!this.$route.query || !this.$route.query.t) { + return null + } + return this.$route.query.t + }, header () { if (this.results.length !== 1) { return `${this.results.length} results` @@ -64,7 +70,14 @@ export default { watch: { '$route.query.q': { handler (query) { - this.retrieve(query) + this.retrieve() + }, + deep: true, + immediate: true + }, + '$route.query.t': { + handler (type) { + this.retrieve() }, deep: true, immediate: true @@ -76,13 +89,13 @@ export default { } }, methods: { - async retrieve (v) { + async retrieve () { if (this.loading) { return } this.loading = true try { - const res = await this.$axios.get(`/retrieve/databaseindex,tableindex,columnindex,identifierindex,viewindex/_search?q=${v}*&terminate_after=50`, this.elasticConfig) + const res = await this.$axios.get(`/retrieve/_all/_search?q=${this.query}*&terminate_after=50`, this.elasticConfig) console.info('search results', res.data.hits.total.value) console.debug('search results for', this.$route.query.q, 'are', res.data.hits.hits) this.results = res.data.hits.hits.map(h => h._source) diff --git a/fda-ui/pages/signup.vue b/fda-ui/pages/signup.vue index d777c2b568f46725f0527491e390bc94ce65713e..2b88602a4e7b006558a5f5759771a9c2dcbde622 100644 --- a/fda-ui/pages/signup.vue +++ b/fda-ui/pages/signup.vue @@ -20,6 +20,7 @@ autocomplete="off" autofocus required + name="email" :rules="[v => !!v || $t('Required')]" hint="e.g. max.mustermann@work.com" label="Work E-Mail Address *" /> @@ -31,6 +32,7 @@ v-model="createAccount.username" autocomplete="off" required + name="username" :rules="[v => !!v || $t('Required'), v => /^[a-z0-9]{3,}$/.test(v) || $t('Only lowercase letters, min. 3 length')]" hint="e.g. mmustermann" @@ -43,6 +45,7 @@ v-model="createAccount.password" autocomplete="off" required + name="password" :rules="[v => !!v || $t('Required')]" type="password" label="Password *" /> @@ -54,6 +57,7 @@ v-model="password2" autocomplete="off" required + name="password-confirm" :rules="[v => !!v || $t('Required'), v => (!!v && v) === createAccount.password || $t('Not matching!')]" type="password" label="Repeat Password *" /> @@ -64,6 +68,7 @@ <v-checkbox v-model="consent" required + name="consent" :rules="[v => !!v || $t('Required')]" label="I understand the warning and do not use production data" /> </v-col> @@ -73,6 +78,7 @@ <v-checkbox v-model="privacy" required + name="privacy" :rules="[v => !!v || $t('Required')]" label="I have read and accept the privacy statement" /> </v-col> @@ -84,6 +90,7 @@ :disabled="!valid" color="primary" type="submit" + name="submit" :loading="loading" @click="register"> Submit diff --git a/fda-ui/test/e2e/database.js b/fda-ui/test/e2e/database.js index 1820f89fbf91fa90839baa5c082e46e1b2b41798..15a86e980b777905aa866d6162fd4976371905a2 100644 --- a/fda-ui/test/e2e/database.js +++ b/fda-ui/test/e2e/database.js @@ -1,63 +1,24 @@ const test = require('ava') -const axios = require('axios') +const { mutations } = require('store') const { pageMacro, before, after } = require('./_utils') test.before(before) test.after(after) -test('create database and see the tabs', pageMacro, async (t, page) => { - const database = 'Test Database ' + Math.random().toString(36).substring(7) - const description = 'Test Description' - - await page.go('/databases') - - // Click create new button - await page.click('button:has-text("Database")') - - // Fill database name - await page.fill('input[name="database"]', database) - - // Fill database description - await page.fill('textarea[name="description"]', description) - - // Press Tab - await page.press('textarea[name="description"]', 'Tab') - - // Select mariadb:10.5 - await page.press('#engine', 'ArrowDown') - - // Click submit button - await page.click('button:has-text("Create")') - - // See page load - let success = await page.waitForSelector('text=' + database) - t.true(!!success, `Database ${database} seems not to be created, notification not found`) - - const id = await axios.get('http://localhost:9092/api/database/').then(function (response) { - return response.filter(function (item) { - return item.name === database - }).id - }) - - // ------------------------------------------------------------------------------------------------------------------- - - await page.go('/databases/' + id + '/info') - - // find 'mariadb' anywhere on the page: - success = await page.waitForSelector('text=mariadb:10.5') - t.true(!!success, 'Could not find the mariadb image on the site') - - await page.go('/databases/' + id + '/tables') - - // find 'mariadb' anywhere on the page: - success = await page.waitForSelector('text=(no tables)') - t.true(!!success, 'Could not find the tables on the site') - - // ------------------------------------------------------------------------------------------------------------------- +test('databases_seeDatabases_succeeds', pageMacro, async (t, page) => { + await page.go('/container') + /* test */ + const success = await page.waitForSelector('main >> header >> text=Databases') + t.true(!!success, 'Failed to find \'Databases\' in page') +}) - await page.go('/databases/' + id + '/queries') +test('databases_createDatabase_succeeds', pageMacro, async (t, page) => { + const state = { token: null, user: null, database: null, table: null, access: null } - // find 'mariadb' anywhere on the page: - success = await page.waitForSelector('text=(no queries)') - t.true(!!success, 'Could not find the queries on the site') + await page.go('/container') + mutations.SET_TOKEN(state, 'ABC') + mutations.SET_USER(state, { username: 'ava' }) + await page.screenshot({ path: './screenshots/databases_createDatabase_succeeds.png' }) + /* test */ + t.true(true) }) diff --git a/fda-ui/test/e2e/forgot.js b/fda-ui/test/e2e/forgot.js new file mode 100644 index 0000000000000000000000000000000000000000..03c3415564e006031ec606d4be3c2b0948737b01 --- /dev/null +++ b/fda-ui/test/e2e/forgot.js @@ -0,0 +1,18 @@ +const test = require('ava') +const { pageMacro, before, after } = require('./_utils') + +test.before(before) +test.after(after) + +test('login_succeeds', pageMacro, async (t, page) => { + const email = 'ava@example.com' + const username = 'ava' + + await page.go('/forgot') + await page.fill('input[name="username"]', username) + await page.fill('input[name="email"]', email) + + /* test */ + const success = await page.waitForSelector('button[name="submit"]:not([disabled])') + t.true(!!success, 'Failed to reset user information') +}) diff --git a/fda-ui/test/e2e/index.js b/fda-ui/test/e2e/index.js index ee391d1d6d52aa4d5a364a6da3c4ac4675dbb2c4..399bd9485ec9a043ac1638517c48e133ba936aff 100644 --- a/fda-ui/test/e2e/index.js +++ b/fda-ui/test/e2e/index.js @@ -4,7 +4,50 @@ const { pageMacro, before, after } = require('./_utils') test.before(before) test.after(after) -test('visit homepage', pageMacro, async (t, page) => { +test('home_seeDatabaseRepository_succeeds', pageMacro, async (t, page) => { await page.go('/') - t.is(await page.title(), 'FAIR Data Austria - Database Repository (Sandbox)') + + // find 'Database Repository' anywhere on the page: + const success = await page.waitForSelector('text=Database Repository') + t.true(!!success, 'Failed to find \'Database Repository\' in page') +}) + +test('home_seeInformation_succeeds', pageMacro, async (t, page) => { + await page.go('/') + + // find 'Information' anywhere on the page: + const success = await page.waitForSelector('text=Information') + t.true(!!success, 'Failed to find \'Information\' in page') +}) + +test('home_seeDatabases_succeeds', pageMacro, async (t, page) => { + await page.go('/') + + // find 'Databases' anywhere on the page: + const success = await page.waitForSelector('text=Databases') + t.true(!!success, 'Failed to find \'Databases\' in page') +}) + +test('home_seeLogin_succeeds', pageMacro, async (t, page) => { + await page.go('/') + + // find 'Login' anywhere on the page: + const success = await page.waitForSelector('text=Login') + t.true(!!success, 'Failed to find \'Login\' in page') +}) + +test('home_seeSignup_succeeds', pageMacro, async (t, page) => { + await page.go('/') + + // find 'Signup' anywhere on the page: + const success = await page.waitForSelector('text=Signup') + t.true(!!success, 'Failed to find \'Signup\' in page') +}) + +test('home_seeSearch_succeeds', pageMacro, async (t, page) => { + await page.go('/') + + // find 'Search' anywhere on the page: + const success = await page.waitForSelector('[placeholder="Search ..."]') + t.true(!!success, 'Failed to find \'Search\' in page') }) diff --git a/fda-ui/test/e2e/login.js b/fda-ui/test/e2e/login.js new file mode 100644 index 0000000000000000000000000000000000000000..1ddae6a394c3d1bccc933b8e8a6e7d1fa0e50552 --- /dev/null +++ b/fda-ui/test/e2e/login.js @@ -0,0 +1,18 @@ +const test = require('ava') +const { pageMacro, before, after } = require('./_utils') + +test.before(before) +test.after(after) + +test('login_succeeds', pageMacro, async (t, page) => { + const username = 'ava' + const password = Math.random().toString(36).substring(7) + + await page.go('/login') + await page.fill('input[name="username"]', username) + await page.fill('input[name="password"]', password) + + /* test */ + const success = await page.waitForSelector('button[name="submit"]:not([disabled])') + t.true(!!success, 'Failed to login') +}) diff --git a/fda-ui/test/e2e/search.js b/fda-ui/test/e2e/search.js new file mode 100644 index 0000000000000000000000000000000000000000..b2fbc9868e30f8ec0caa4a6d0103c0ca271c3d05 --- /dev/null +++ b/fda-ui/test/e2e/search.js @@ -0,0 +1,28 @@ +const test = require('ava') +const { pageMacro, before, after } = require('./_utils') + +test.before(before) +test.after(after) + +test('search_succeeds', pageMacro, async (t, page) => { + const query = 'dummy' + + await page.go('/') + await page.fill('input[placeholder="Search ..."]', query) + + /* test */ + const success = await page.waitForSelector('button[name="search-submit"]') + t.true(!!success, 'Failed to search') +}) + +test('search_execute_succeeds', pageMacro, async (t, page) => { + const query = 'dummy' + + await page.go('/') + await page.fill('input[placeholder="Search ..."]', query) + await page.click('button[name="search-submit"]') + + /* test */ + const success = await page.waitForSelector('button[name="search-submit"]') + t.true(!!success, 'Failed to search') +}) diff --git a/fda-ui/test/e2e/signup.js b/fda-ui/test/e2e/signup.js new file mode 100644 index 0000000000000000000000000000000000000000..d25e27a274b212fc671dba8be5bd3f3990922ec4 --- /dev/null +++ b/fda-ui/test/e2e/signup.js @@ -0,0 +1,21 @@ +const test = require('ava') +const { pageMacro, before, after } = require('./_utils') + +test.before(before) +test.after(after) + +test('signup_succeeds', pageMacro, async (t, page) => { + const email = 'ava@example.com' + const username = 'ava' + const password = Math.random().toString(36).substring(7) + + await page.go('/signup') + await page.fill('input[name="email"]', email) + await page.fill('input[name="username"]', username) + await page.fill('input[name="password"]', password) + await page.fill('input[name="password-confirm"]', password) + + /* test */ + const success = await page.waitForSelector('button[name="submit"]:not([disabled])') + t.true(!!success, 'Failed to sign-up') +}) diff --git a/fda-ui/test/e2e/tables.js b/fda-ui/test/e2e/tables.js deleted file mode 100644 index 208280da7730b141049532b2e1dc73c8866ea02d..0000000000000000000000000000000000000000 --- a/fda-ui/test/e2e/tables.js +++ /dev/null @@ -1,62 +0,0 @@ -const test = require('ava') -const axios = require('axios') -const { pageMacro, before, after } = require('./_utils') - -test.before(before) -test.after(after) - -test('create table using form', pageMacro, async (t, page) => { - const database = 'Test Database ' + Math.random().toString(36).substring(7) - const table = 'Test Table ' + Math.random().toString(36).substring(7) - const description = 'Test Description' - - await page.go('/databases') - - // Click create new button - await page.click('button:has-text("Database")') - - // Fill database name - await page.fill('input[name="database"]', database) - - // Fill database description - await page.fill('textarea[name="description"]', description) - - // Press Tab - await page.press('textarea[name="description"]', 'Tab') - - // Select mariadb:10.5 - await page.press('#engine', 'ArrowDown') - - // Click submit button - await page.click('button:has-text("Create")') - - // See page load - let success = await page.waitForSelector('text=' + database) - t.true(!!success, `Database ${database} seems not to be created, notification not found`) - - const id = await axios.get('http://localhost:9092/api/database/').then(function (response) { - return response.filter(function (item) { - return item.name === database - }).id - }) - - // ------------------------------------------------------------------------------------------------------------------- - - await page.go('/databases/' + id + '/tables') - - // Click create new button - await page.click('button:has-text("Create Table")') - - // Fill table name - await page.fill('input[name="name"]', table) - - // Fill table description - await page.fill('input[name="description"]', description) - - // Click submit button - await page.click('button:has-text("Create Table")') - - // See page load - success = await page.waitForSelector('text=aaaaaaaa') - t.true(!!success, `Table ${database} seems not to be created, notification not found`) -}) diff --git a/fda-ui/test/unit/query.js b/fda-ui/test/unit/query.js index af97f551459143ea52318666258bae5387e0a0a0..1721491863978b8b2ee50141700991768a852504 100644 --- a/fda-ui/test/unit/query.js +++ b/fda-ui/test/unit/query.js @@ -1,14 +1,14 @@ const test = require('ava') const { buildQuery, castNum } = require('@/server-middleware/query') -test('simple select', (t) => { +test('buildQuery_succeeds', (t) => { const r = buildQuery({ table: 'Table' }) t.is(r.sql, 'select * from `Table`') }) -test('select some columns', (t) => { +test('buildQuery_columns_succeeds', (t) => { const r = buildQuery({ table: 'Table', select: ['database', 'bbb'] @@ -16,7 +16,7 @@ test('select some columns', (t) => { t.is(r.sql, 'select `database`, `bbb` from `Table`') }) -test('simple where clause', (t) => { +test('buildQuery_where_succeeds', (t) => { const r = buildQuery({ table: 'Table', clauses: [ @@ -26,7 +26,7 @@ test('simple where clause', (t) => { t.is(r.sql, 'select * from `Table` where `foo` = 42') }) -test('simple where clause with numeric string', (t) => { +test('buildQuery_whereNumeric_succeeds', (t) => { const r = buildQuery({ table: 'Table', clauses: [ @@ -36,7 +36,7 @@ test('simple where clause with numeric string', (t) => { t.is(r.sql, 'select * from `Table` where `foo` = 42') }) -test('simple where clause with non-numeric string', (t) => { +test('buildQuery_whereString_succeeds', (t) => { const r = buildQuery({ table: 'Table', clauses: [ @@ -46,7 +46,7 @@ test('simple where clause with non-numeric string', (t) => { t.is(r.sql, 'select * from `Table` where `foo` = \'bla\'') }) -test('using unallowed operator', (t) => { +test('buildQuery_illegalOperator_fails', (t) => { const r = buildQuery({ table: 'Table', clauses: [ @@ -57,7 +57,7 @@ test('using unallowed operator', (t) => { t.is(r.error, 'The operator "UNKNOWN" is not permitted') }) -test('where clause with explicit `and`', (t) => { +test('buildQuery_whereAndExplicit_succeeds', (t) => { const r = buildQuery({ table: 'Table', clauses: [ @@ -69,7 +69,7 @@ test('where clause with explicit `and`', (t) => { t.is(r.sql, 'select * from `Table` where `foo` = 42 and `bar` = 42') }) -test('where clause with implicit `and`', (t) => { +test('buildQuery_whereAndImplicit_succeeds', (t) => { const r = buildQuery({ table: 'Table', clauses: [ @@ -81,7 +81,7 @@ test('where clause with implicit `and`', (t) => { t.is(r.sql, 'select * from `Table` where `foo` = 42 and `bar` = 42') }) -test('where clause with `or`', (t) => { +test('buildQuery_whereOr', (t) => { const r = buildQuery({ table: 'Table', clauses: [ @@ -93,7 +93,7 @@ test('where clause with `or`', (t) => { t.is(r.sql, 'select * from `Table` where `foo` = 42 or `bar` = 42') }) -test('cast numeric strings to numbers', (t) => { +test('castNum_succeeds', (t) => { t.is(castNum(''), '') t.is(castNum(' '), ' ') t.is(castNum('0'), 0) diff --git a/fda-ui/test/unit/utils.js b/fda-ui/test/unit/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..4b6a0f0be00d091b169c13c70a1b5daf55d708be --- /dev/null +++ b/fda-ui/test/unit/utils.js @@ -0,0 +1,279 @@ +const test = require('ava') +const { + isNonNegativeInteger, + isDeveloper, + isResearcher, + isDataSteward, + formatUser, + formatDateUTC, + formatYearUTC, + formatMonthUTC, + formatDayUTC, + formatTimestamp, + formatCreators, + formatTimestampUTCLabel, + formatTimestampUTC +} = require('@/utils') + +test('isNonNegativeInteger_succeeds', (t) => { + /* test */ + const response = isNonNegativeInteger('1') + t.is(response, true) +}) + +test('isNonNegativeInteger_zero_succeeds', (t) => { + /* test */ + const response = isNonNegativeInteger('0') + t.is(response, true) +}) + +test('isNonNegativeInteger_fails', (t) => { + /* test */ + const response = isNonNegativeInteger('-1') + t.is(response, false) +}) + +test('isDeveloper_succeeds', (t) => { + const user = { roles: ['ROLE_DEVELOPER'] } + /* test */ + const response = isDeveloper(user) + t.is(response, true) +}) + +test('isDeveloper_fails', (t) => { + const user = { roles: [] } + /* test */ + const response = isDeveloper(user) + t.is(response, false) +}) + +test('isDeveloper_otherRole_fails', (t) => { + const user = { roles: ['ROLE_RESEARCHER'] } + /* test */ + const response = isDeveloper(user) + t.is(response, false) +}) + +test('isResearcher_succeeds', (t) => { + const user = { roles: ['ROLE_RESEARCHER'] } + /* test */ + const response = isResearcher(user) + t.is(response, true) +}) + +test('isResearcher_fails', (t) => { + const user = { roles: [] } + /* test */ + const response = isResearcher(user) + t.is(response, false) +}) + +test('isResearcher_otherRole_fails', (t) => { + const user = { roles: ['ROLE_DEVELOPER'] } + /* test */ + const response = isResearcher(user) + t.is(response, false) +}) + +test('isDataSteward_succeeds', (t) => { + const user = { roles: ['ROLE_DATA_STEWARD'] } + /* test */ + const response = isDataSteward(user) + t.is(response, true) +}) + +test('isDataSteward_fails', (t) => { + const user = { roles: [] } + /* test */ + const response = isDataSteward(user) + t.is(response, false) +}) + +test('isDataSteward_otherRole_fails', (t) => { + const user = { roles: ['ROLE_DEVELOPER'] } + /* test */ + const response = isDataSteward(user) + t.is(response, false) +}) + +test('formatUser_fails', (t) => { + const user = null + /* test */ + const response = formatUser(user) + t.is(response, null) +}) + +test('formatUser_usernameMissing_fails', (t) => { + const user = { lastname: null, firstname: null } + /* test */ + const response = formatUser(user) + t.is(response, null) +}) + +test('formatUser_succeeds', (t) => { + const user = { lastname: null, firstname: null, username: 'mweise' } + /* test */ + const response = formatUser(user) + t.is(response, 'mweise') +}) + +test('formatUser_firstnameLastname_succeeds', (t) => { + const user = { lastname: 'Martin', firstname: 'Weise', username: 'mweise' } + /* test */ + const response = formatUser(user) + t.is(response, 'Weise Martin') +}) + +test('formatUser_titles_succeeds', (t) => { + const user = { lastname: 'Martin', firstname: 'Weise', username: 'mweise', titles_before: 'Dipl.-Ing.', titles_after: 'BSc' } + /* test */ + const response = formatUser(user) + t.is(response, 'Dipl.-Ing. Weise Martin BSc') +}) + +test('formatDateUTC_succeeds', (t) => { + /* test */ + const response = formatDateUTC('2023-02-15 10:32:21') + t.is(response, '2023-02-15') +}) + +test('formatDateUTC_fails', (t) => { + /* test */ + const response = formatDateUTC(null) + t.is(response, null) +}) + +test('formatYearUTC_fails', (t) => { + /* test */ + const response = formatYearUTC(null) + t.is(response, null) +}) + +test('formatYearUTC_succeeds', (t) => { + /* test */ + const response = formatYearUTC('2023-02-15 10:32:21') + t.is(response, '2023') +}) + +test('formatMonthUTC_fails', (t) => { + /* test */ + const response = formatMonthUTC(null) + t.is(response, null) +}) + +test('formatMonthUTC_succeeds', (t) => { + /* test */ + const response = formatMonthUTC('2023-02-15 10:32:21') + t.is(response, '02') +}) + +test('formatDayUTC_fails', (t) => { + /* test */ + const response = formatDayUTC(null) + t.is(response, null) +}) + +test('formatDayUTC_succeeds', (t) => { + /* test */ + const response = formatDayUTC('2023-02-15 10:32:21') + t.is(response, '15') +}) + +test('formatTimestamp_fails', (t) => { + /* test */ + const response = formatTimestamp(null) + t.is(response, null) +}) + +test('formatTimestamp_succeeds', (t) => { + /* test */ + const response = formatTimestamp('2023-02-15 10:32:21') + t.is(response, '2023-02-15 10:32:21') +}) + +test('formatCreators_containerMissing_fails', (t) => { + const container = null + /* test */ + const response = formatCreators(container) + t.is(response, null) +}) + +test('formatCreators_databaseMissing_fails', (t) => { + const container = { } + /* test */ + const response = formatCreators(container) + t.is(response, null) +}) + +test('formatCreators_identifierMissing_fails', (t) => { + const container = { database: { } } + /* test */ + const response = formatCreators(container) + t.is(response, null) +}) + +test('formatCreators_creatorsMissing_fails', (t) => { + const container = { database: { identifier: { } } } + /* test */ + const response = formatCreators(container) + t.is(response, null) +}) + +test('formatCreators_identifierNull_fails', (t) => { + const container = { database: { identifier: null } } + /* test */ + const response = formatCreators(container) + t.is(response, null) +}) + +test('formatCreators_creatorsEmpty_fails', (t) => { + const container = { database: { identifier: { creators: [] } } } + /* test */ + const response = formatCreators(container) + t.is(response, null) +}) + +test('formatCreators_single_succeeds', (t) => { + const container = { database: { identifier: { creators: [{ firstname: 'Martin', lastname: 'Weise' }] } } } + /* test */ + const response = formatCreators(container) + t.is(response, 'M., Weise') +}) + +test('formatCreators_double_succeeds', (t) => { + const container = { database: { identifier: { creators: [{ firstname: 'Martin', lastname: 'Weise' }, { firstname: 'Tobias', lastname: 'Grantner' }] } } } + /* test */ + const response = formatCreators(container) + t.is(response, 'M., Weise, & T., Grantner') +}) + +test('formatCreators_multiple_succeeds', (t) => { + const container = { database: { identifier: { creators: [{ firstname: 'Martin', lastname: 'Weise' }, { firstname: 'Tobias', lastname: 'Grantner' }, { firstname: 'Josef', lastname: 'Taha' }] } } } + /* test */ + const response = formatCreators(container) + t.is(response, 'M., Weise, T., Grantner, & J., Taha') +}) + +test('formatTimestampUTCLabel_succeeds', (t) => { + /* test */ + const response = formatTimestampUTCLabel('2023-02-15 10:32:21') + t.is(response, '2023-02-15 09:32:21 (UTC)') +}) + +test('formatTimestampUTCLabel_fails', (t) => { + /* test */ + const response = formatTimestampUTCLabel(null) + t.is(response, null) +}) + +test('formatTimestampUTC_fails', (t) => { + /* test */ + const response = formatTimestampUTC(null) + t.is(response, null) +}) + +test('formatTimestampUTC_succeeds', (t) => { + /* test */ + const response = formatTimestampUTC('2023-02-15 10:32:21') + t.is(response, '2023-02-15 09:32:21') +}) diff --git a/fda-ui/utils/index.js b/fda-ui/utils/index.js index 5e8dde78716edfb27d7341feb5ec45592a921bc9..2cb3310639179d677ae038562ba7be92a03504c8 100644 --- a/fda-ui/utils/index.js +++ b/fda-ui/utils/index.js @@ -53,10 +53,10 @@ function formatUser (user) { if (!user) { return null } - if (!('firstname' in user) || !('lastname' in user)) { - return user.username - } - if (user.firstname === null || user.lastname === null) { + if (!('firstname' in user) || !('lastname' in user) || user.firstname === null || user.lastname === null) { + if (!('username' in user)) { + return null + } return user.username } let name = '' @@ -70,13 +70,6 @@ function formatUser (user) { return name } -function padLeft (str, padString, length) { - while (str.length < length) { - str = padString + str - } - return str -} - function formatDateUTC (str) { if (str === null) { return null @@ -117,8 +110,8 @@ function formatTimestamp (str) { } function formatCreators (container) { - if (!container.database.identifier || !container.database.identifier.creators) { - return '' + if (!container || !('database' in container) || !('identifier' in container.database) || !container.database.identifier || !('creators' in container.database.identifier) || !container.database.identifier.creators) { + return null } const creators = container.database.identifier.creators if (creators.length === 0) { @@ -127,8 +120,8 @@ function formatCreators (container) { let str = '' for (let i = 0; i < creators.length; i++) { /* separator */ - if (i > 0 && creators.length === 2) { - str += ' & ' + if (creators.length > 1 && i === creators.length - 1) { + str += ', & ' } else if (i > 0 && creators.length !== 2) { str += ', ' } @@ -170,7 +163,6 @@ module.exports = { formatYearUTC, formatMonthUTC, formatDayUTC, - padLeft, formatCreators, isDeveloper, isResearcher, diff --git a/fda-ui/yarn.lock b/fda-ui/yarn.lock index e302f9ae08538f8432dbf7fe79485fca4dc56503..421217b621020514a9f20925a4ba668b5d2735c6 100644 --- a/fda-ui/yarn.lock +++ b/fda-ui/yarn.lock @@ -2767,6 +2767,15 @@ axios@^0.21.1: dependencies: follow-redirects "^1.14.0" +axios@^1.3.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.3.tgz#e7011384ba839b885007c9c9fae1ff23dceb295b" + integrity sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + babel-eslint@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" @@ -5639,6 +5648,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -5661,6 +5675,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -10091,6 +10114,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"