From 763e78e34d2d3ff16033a8ec9f5cc387adec3668 Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Thu, 30 Mar 2023 20:08:42 +0200
Subject: [PATCH] Updated env vars and added user service

---
 docker-compose.yml                            |  20 ++
 .../java/at/tuwien/config/GatewayConfig.java  |   5 +
 .../java/at/tuwien/entities/user/User.java    |   3 +
 fda-ui/.env.example                           |   3 +-
 fda-ui/components/dialogs/Persist.vue         |   9 +-
 fda-ui/config.js                              |   1 +
 fda-ui/nuxt.config.js                         |   5 +-
 .../database/_database_id/info.vue            | 101 +++---
 .../database/_database_id/settings.vue        |  61 +++-
 fda-user-service/.gitignore                   |  44 +++
 .../.mvn/wrapper/MavenWrapperDownloader.java  | 118 +++++++
 .../.mvn/wrapper/maven-wrapper.jar            | Bin 0 -> 50710 bytes
 .../.mvn/wrapper/maven-wrapper.properties     |   2 +
 fda-user-service/Dockerfile                   |  42 +++
 fda-user-service/mvnw                         | 310 ++++++++++++++++++
 fda-user-service/mvnw.cmd                     | 182 ++++++++++
 fda-user-service/pom.xml                      | 288 ++++++++++++++++
 fda-user-service/report/pom.xml               |  56 ++++
 fda-user-service/rest-service/pom.xml         |  46 +++
 .../at/tuwien/FdaUserServiceApplication.java  |  25 ++
 .../java/at/tuwien/config/SwaggerConfig.java  |  46 +++
 .../java/at/tuwien/endpoint/UserEndpoint.java |  58 ++++
 .../tuwien/handlers/ApiExceptionHandler.java  | 249 ++++++++++++++
 .../src/main/resources/application-docker.yml |  40 +++
 .../src/main/resources/application-local.yml  |  40 +++
 .../src/main/resources/application.yml        |  40 +++
 .../src/main/resources/config.properties      |   0
 .../main/resources/mariadb_hibernate.cfg.xml  |  20 ++
 .../src/test/resources/application.properties |  32 ++
 .../src/test/resources/schema.sql             |  25 ++
 .../test/resources/sensor/1_querystore.sql    |  75 +++++
 .../src/test/resources/sensor/2_traffic.sql   |   8 +
 .../test/resources/traffic/1_querystore.sql   |  75 +++++
 .../src/test/resources/traffic/2_traffic.sql  |  43 +++
 .../test/resources/weather/1_querystore.sql   |  75 +++++
 .../src/test/resources/weather/2_weather.sql  |  55 ++++
 .../src/test/resources/weather/location.csv   |   2 +
 .../src/test/resources/zoo/1_querystore.sql   |  75 +++++
 .../src/test/resources/zoo/2_zoo.sql          | 191 +++++++++++
 fda-user-service/service_ready                |   6 +
 fda-user-service/services/pom.xml             |  16 +
 .../java/at/tuwien/auth/AuthTokenFilter.java  | 100 ++++++
 .../java/at/tuwien/config/JacksonConfig.java  |  31 ++
 .../java/at/tuwien/config/ReadyConfig.java    |  25 ++
 .../at/tuwien/config/WebSecurityConfig.java   |  87 +++++
 .../at/tuwien/exception/AmqpException.java    |  21 ++
 .../exception/ColumnParseException.java       |  21 ++
 .../exception/ContainerNotFoundException.java |  21 ++
 .../DatabaseConnectionException.java          |  23 ++
 .../exception/DatabaseNotFoundException.java  |  23 ++
 .../exception/FileStorageException.java       |  20 ++
 .../IdentifierNotFoundException.java          |  21 ++
 .../exception/ImageNotSupportedException.java |  23 ++
 .../tuwien/exception/NotAllowedException.java |  21 ++
 .../tuwien/exception/PaginationException.java |  21 ++
 .../QueryAlreadyPersistedException.java       |  19 ++
 .../exception/QueryMalformedException.java    |  23 ++
 .../exception/QueryNotFoundException.java     |  21 ++
 .../tuwien/exception/QueryStoreException.java |  19 ++
 .../at/tuwien/exception/SortException.java    |  21 ++
 .../exception/TableMalformedException.java    |  21 ++
 .../exception/TableNotFoundException.java     |  21 ++
 .../exception/TupleDeleteException.java       |  19 ++
 .../exception/UserNotFoundException.java      |  21 ++
 .../exception/ViewMalformedException.java     |  21 ++
 .../exception/ViewNotFoundException.java      |  21 ++
 .../java/at/tuwien/mapper/UserMapper.java     |  31 ++
 .../tuwien/repository/jpa/UserRepository.java |  14 +
 .../java/at/tuwien/service/UserService.java   |  39 +++
 .../tuwien/service/impl/UserServiceImpl.java  |  66 ++++
 .../src/test/resources/application.properties |  14 +
 71 files changed, 3276 insertions(+), 65 deletions(-)
 create mode 100644 fda-user-service/.gitignore
 create mode 100644 fda-user-service/.mvn/wrapper/MavenWrapperDownloader.java
 create mode 100644 fda-user-service/.mvn/wrapper/maven-wrapper.jar
 create mode 100644 fda-user-service/.mvn/wrapper/maven-wrapper.properties
 create mode 100644 fda-user-service/Dockerfile
 create mode 100755 fda-user-service/mvnw
 create mode 100644 fda-user-service/mvnw.cmd
 create mode 100644 fda-user-service/pom.xml
 create mode 100644 fda-user-service/report/pom.xml
 create mode 100644 fda-user-service/rest-service/pom.xml
 create mode 100644 fda-user-service/rest-service/src/main/java/at/tuwien/FdaUserServiceApplication.java
 create mode 100644 fda-user-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java
 create mode 100644 fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java
 create mode 100644 fda-user-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java
 create mode 100644 fda-user-service/rest-service/src/main/resources/application-docker.yml
 create mode 100644 fda-user-service/rest-service/src/main/resources/application-local.yml
 create mode 100644 fda-user-service/rest-service/src/main/resources/application.yml
 create mode 100644 fda-user-service/rest-service/src/main/resources/config.properties
 create mode 100644 fda-user-service/rest-service/src/main/resources/mariadb_hibernate.cfg.xml
 create mode 100644 fda-user-service/rest-service/src/test/resources/application.properties
 create mode 100644 fda-user-service/rest-service/src/test/resources/schema.sql
 create mode 100644 fda-user-service/rest-service/src/test/resources/sensor/1_querystore.sql
 create mode 100644 fda-user-service/rest-service/src/test/resources/sensor/2_traffic.sql
 create mode 100644 fda-user-service/rest-service/src/test/resources/traffic/1_querystore.sql
 create mode 100644 fda-user-service/rest-service/src/test/resources/traffic/2_traffic.sql
 create mode 100644 fda-user-service/rest-service/src/test/resources/weather/1_querystore.sql
 create mode 100644 fda-user-service/rest-service/src/test/resources/weather/2_weather.sql
 create mode 100644 fda-user-service/rest-service/src/test/resources/weather/location.csv
 create mode 100644 fda-user-service/rest-service/src/test/resources/zoo/1_querystore.sql
 create mode 100644 fda-user-service/rest-service/src/test/resources/zoo/2_zoo.sql
 create mode 100644 fda-user-service/service_ready
 create mode 100644 fda-user-service/services/pom.xml
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/config/JacksonConfig.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/config/ReadyConfig.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/AmqpException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/ColumnParseException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/ContainerNotFoundException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/DatabaseConnectionException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/DatabaseNotFoundException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/FileStorageException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/IdentifierNotFoundException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/ImageNotSupportedException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/NotAllowedException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/PaginationException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/QueryAlreadyPersistedException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/QueryMalformedException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/QueryNotFoundException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/QueryStoreException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/SortException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/TableMalformedException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/TableNotFoundException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/TupleDeleteException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/UserNotFoundException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/ViewMalformedException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/exception/ViewNotFoundException.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/repository/jpa/UserRepository.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/service/UserService.java
 create mode 100644 fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java
 create mode 100644 fda-user-service/services/src/test/resources/application.properties

diff --git a/docker-compose.yml b/docker-compose.yml
index 2ec72e2df9..07f8df8fa9 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -262,6 +262,26 @@ services:
     logging:
       driver: json-file
 
+  fda-user-service:
+    restart: on-failure
+    container_name: dbrepo-user-service
+    hostname: user-service
+    build: ./fda-user-service
+    image: fda-user-service
+    networks:
+      core:
+    ports:
+      - "9098:9098"
+    env_file:
+      - .env
+    depends_on:
+      fda-metadata-db:
+        condition: service_healthy
+      fda-authentication-service:
+        condition: service_healthy
+    logging:
+      driver: json-file
+
   fda-semantics-service:
     restart: on-failure
     container_name: dbrepo-semantics-service
diff --git a/fda-gateway-service/rest-service/src/main/java/at/tuwien/config/GatewayConfig.java b/fda-gateway-service/rest-service/src/main/java/at/tuwien/config/GatewayConfig.java
index 937479e041..bb1450fde7 100644
--- a/fda-gateway-service/rest-service/src/main/java/at/tuwien/config/GatewayConfig.java
+++ b/fda-gateway-service/rest-service/src/main/java/at/tuwien/config/GatewayConfig.java
@@ -16,6 +16,11 @@ public class GatewayConfig {
                         .method("POST", "GET", "PUT", "DELETE")
                         .filters(f -> f.rewritePath("/api/auth/(?<segment>.*)", "/${segment}"))
                         .uri("lb://authentication-service"))
+                .route("user-service", r -> r.path("/api/user/**")
+                        .and()
+                        .method("POST", "GET", "PUT", "DELETE")
+                        .and()
+                        .uri("lb://user-service"))
                 .route("broker-service", r -> r.path("/api/broker/**")
                         .and()
                         .method("POST", "GET", "PUT", "DELETE")
diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java
index f394a01803..abd946bdb9 100644
--- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java
+++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java
@@ -36,6 +36,9 @@ public class User {
     @Column(name = "last_name")
     private String lastname;
 
+    @Column(name = "realm id")
+    private String realmId;
+
     @Column(unique = true, nullable = false)
     private String email;
 
diff --git a/fda-ui/.env.example b/fda-ui/.env.example
index 5a83ca9863..366635afbd 100644
--- a/fda-ui/.env.example
+++ b/fda-ui/.env.example
@@ -7,4 +7,5 @@ BROKER_USERNAME=fda
 BROKER_PASSWORD=fda
 SANDBOX=false
 SHARED_FILESYSTEM=/tmp
-DBREPO_CLIENT_SECRET=
+DBREPO_CLIENT_SECRET=MUwRc7yfXSJwX8AdRMWaQC3Nep1VjwgG
+DEFAULT_PID_PUBLISHER=
diff --git a/fda-ui/components/dialogs/Persist.vue b/fda-ui/components/dialogs/Persist.vue
index a8595e18fd..7ad914ef20 100644
--- a/fda-ui/components/dialogs/Persist.vue
+++ b/fda-ui/components/dialogs/Persist.vue
@@ -77,7 +77,7 @@
                 required />
             </v-col>
           </v-row>
-          <v-row v-for="(creator,i) in identifier.creators" :key="`c-${i}`" dense>
+          <v-row v-for="(creator, i) in identifier.creators" :key="`c-${i}`" dense>
             <v-col cols="3">
               <v-text-field
                 v-model="creator.firstname"
@@ -108,7 +108,7 @@
                 name="orcid"
                 label="ORCID" />
             </v-col>
-            <v-col cols="1" class="mt-5">
+            <v-col v-if="i > 0" cols="1" class="mt-5">
               <v-btn icon x-small @click="deleteCreator(i)">
                 <v-icon>mdi-close</v-icon>
               </v-btn>
@@ -334,6 +334,8 @@ export default {
   },
   mounted () {
     this.loadLicenses()
+    this.addCreator()
+    this.identifier.publisher = this.$config.defaultPublisher
   },
   methods: {
     cancel () {
@@ -356,6 +358,9 @@ export default {
       })
     },
     deleteCreator (index) {
+      if (index === 0) {
+        return
+      }
       this.identifier.creators.splice(index, 1)
     },
     deleteRelatedIdentifier (index) {
diff --git a/fda-ui/config.js b/fda-ui/config.js
index a11e09b5c4..b2ea4ef41e 100644
--- a/fda-ui/config.js
+++ b/fda-ui/config.js
@@ -15,5 +15,6 @@ config.tokenMax = process.env.TOKEN_MAX || 5
 config.elasticPassword = process.env.ELASTIC_PASSWORD || 'elastic'
 config.elasticPassword = process.env.ELASTIC_PASSWORD || 'elastic'
 config.clientSecret = process.env.DBREPO_CLIENT_SECRET
+config.defaultPublisher = process.env.DEFAULT_PID_PUBLISHER
 
 module.exports = config
diff --git a/fda-ui/nuxt.config.js b/fda-ui/nuxt.config.js
index 840e02341c..9530a9de94 100644
--- a/fda-ui/nuxt.config.js
+++ b/fda-ui/nuxt.config.js
@@ -1,6 +1,6 @@
 import path from 'path'
 import colors from 'vuetify/es5/util/colors'
-import { sandbox, title, icon, brokerUsername, brokerPassword, sharedFilesystem, version, logo, mailVerify, tokenMax, elasticPassword, clientSecret, api, search } from './config'
+import { sandbox, title, icon, brokerUsername, brokerPassword, sharedFilesystem, version, logo, mailVerify, tokenMax, elasticPassword, clientSecret, api, search, defaultPublisher } from './config'
 
 if (sandbox) {
   console.info('[FDA] Running in sandbox environment')
@@ -77,7 +77,8 @@ export default {
     mailVerify,
     tokenMax,
     elasticPassword,
-    clientSecret
+    clientSecret,
+    defaultPublisher
   },
 
   proxy: {
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 b9224362e6..0eebace712 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
@@ -4,24 +4,38 @@
     <v-progress-linear v-if="loading" />
     <v-tabs-items v-model="tab">
       <v-tab-item>
-        <v-card v-if="isDataSteward || hasIdentifier || (!hasIdentifier && isCreator && isResearcher)" flat tile>
+        <v-card v-if="showIdentifierCard" flat tile>
           <v-card-title>Identifier</v-card-title>
           <v-card-text v-if="hasIdentifier">
             <v-list dense>
               <v-list-item>
                 <v-list-item-content>
-                  <v-list-item-title v-if="publisher" class="mt-2">
+                  <v-list-item-title class="mt-2">
                     Persistent Identifier
                   </v-list-item-title>
-                  <v-list-item-content v-if="publisher">
+                  <v-list-item-content>
                     <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" />
                     <a v-if="!loading" :href="pid">{{ pid }}</a>
                   </v-list-item-content>
-                  <v-list-item-title v-if="publisher" class="mt-2">
+                  <v-list-item-title class="mt-2">
+                    Database Title
+                  </v-list-item-title>
+                  <v-list-item-content>
+                    <v-skeleton-loader v-if="loading" type="paragraph" width="50%" />
+                    <span v-if="!loading">{{ identifier.title }}</span>
+                  </v-list-item-content>
+                  <v-list-item-title class="mt-2">
+                    Database Description
+                  </v-list-item-title>
+                  <v-list-item-content>
+                    <v-skeleton-loader v-if="loading" type="paragraph" width="50%" />
+                    <span v-if="!loading">{{ identifier.description }}</span>
+                  </v-list-item-content>
+                  <v-list-item-title class="mt-2">
                     Database Publisher
                   </v-list-item-title>
-                  <v-list-item-content v-if="publisher">
-                    {{ publisher }}
+                  <v-list-item-content>
+                    {{ database.identifier.publisher }}
                   </v-list-item-content>
                   <v-list-item-title v-if="identifier.creators.length > 0" class="mt-2">
                     Creators
@@ -93,18 +107,17 @@
               </v-list-item>
             </v-list>
           </v-card-text>
-          <v-card-text>
+          <v-card-text v-if="canCreateIdentifier || canDeleteIdentifier">
             <v-card-actions>
               <v-btn
-                v-if="!hasIdentifier && (isDataSteward || (!hasIdentifier && isCreator && isResearcher))"
+                v-if="canCreateIdentifier"
                 small
                 color="primary"
                 @click="editDbDialog = true">
                 Get Database PID
               </v-btn>
-              <!--                v-if="isDataSteward && hasIdentifier"-->
               <v-btn
-                v-if="false"
+                v-if="canDeleteIdentifier"
                 small
                 :loading="loadingDelete"
                 color="error"
@@ -114,7 +127,7 @@
             </v-card-actions>
           </v-card-text>
         </v-card>
-        <v-divider v-if="isDataSteward || hasIdentifier || (!hasIdentifier && isCreator && isResearcher)" />
+        <v-divider v-if="showIdentifierCard" />
         <v-card flat tile>
           <v-card-title>Database</v-card-title>
           <v-card-text>
@@ -135,13 +148,6 @@
                     <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" />
                     <span v-if="!loading">{{ internal_name }}</span>
                   </v-list-item-content>
-                  <v-list-item-title v-if="description" class="mt-2">
-                    Database Description
-                  </v-list-item-title>
-                  <v-list-item-content v-if="description">
-                    <v-skeleton-loader v-if="loading" type="paragraph" width="50%" />
-                    <span v-if="!loading">{{ description }}</span>
-                  </v-list-item-content>
                   <v-list-item-title class="mt-2">
                     Database Creator
                   </v-list-item-title>
@@ -222,7 +228,7 @@ import DBToolbar from '@/components/DBToolbar'
 import Persist from '@/components/dialogs/Persist'
 import OrcidIcon from '@/components/icons/OrcidIcon'
 import Citation from '@/components/identifier/Citation'
-import { formatTimestampUTCLabel, formatUser, isDataSteward, isResearcher } from '@/utils'
+import { formatTimestampUTCLabel, formatUser } from '@/utils'
 
 export default {
   components: {
@@ -254,18 +260,6 @@ export default {
     baseUrl () {
       return location.protocol + '//' + location.host
     },
-    description () {
-      if (!this.hasIdentifier) {
-        return ''
-      }
-      return this.database.identifier.description
-    },
-    publisher () {
-      if (!this.hasIdentifier) {
-        return ''
-      }
-      return this.database.identifier.publisher
-    },
     token () {
       return this.$store.state.token
     },
@@ -273,10 +267,10 @@ export default {
       return this.$store.state.user
     },
     identifier () {
-      if (this.database) {
-        return this.$store.state.database.identifier
+      if (!this.database) {
+        return null
       }
-      return null
+      return this.$store.state?.database.identifier
     },
     access () {
       return this.$store.state.access
@@ -294,27 +288,12 @@ export default {
         headers: { Authorization: `Bearer ${this.token}`, Accept: 'application/json' }
       }
     },
-    isResearcher () {
-      return isResearcher(this.user)
-    },
-    isDataSteward () {
-      return isDataSteward(this.user)
-    },
     pid () {
       return `${this.baseUrl}/pid/${this.database.identifier.id}`
     },
     createdUTC () {
       return formatTimestampUTCLabel(this.database.created)
     },
-    isCreator () {
-      if (!this.database) {
-        return false
-      }
-      if (!this.database.creator.username || !this.user || !this.user.username) {
-        return false
-      }
-      return this.database.creator.username === this.user.username
-    },
     language () {
       return this.database.identifier.language
     },
@@ -327,6 +306,30 @@ export default {
     container_internal_name () {
       return this.database.container.internal_name
     },
+    showIdentifierCard () {
+      if (this.hasIdentifier) {
+        return true
+      }
+      if (!this.user) {
+        return false
+      }
+      return this.canCreateIdentifier || this.canDeleteIdentifier || this.user.roles.includes('modify-identifier-metadata')
+    },
+    canCreateIdentifier () {
+      if (!this.user) {
+        return false
+      }
+      if (this.hasIdentifier) {
+        return false
+      }
+      return this.user.roles.includes('create-identifier')
+    },
+    canDeleteIdentifier () {
+      if (!this.user) {
+        return false
+      }
+      return this.user.roles.includes('delete-identifier')
+    },
     contact () {
       if (this.database.contact === null || this.database.contact === undefined) {
         return null
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 85631642ea..888ba4f681 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
@@ -5,8 +5,7 @@
     <v-tabs-items v-model="tab">
       <v-tab-item>
         <v-card v-if="isOwner" flat tile>
-          <v-card-title>Modify database access</v-card-title>
-          <v-card-subtitle>This is a dangerous operation</v-card-subtitle>
+          <v-card-title>Access</v-card-title>
           <v-data-table
             :headers="headers"
             :items="database.accesses"
@@ -34,15 +33,8 @@
         </v-card>
         <v-divider />
         <v-card v-if="canModifyVisibility" flat tile>
-          <v-card-title>Modify database visibility</v-card-title>
-          <v-card-subtitle>This is a dangerous operation</v-card-subtitle>
+          <v-card-title>Visibility</v-card-title>
           <v-card-text>
-            <v-alert
-              v-if="database.is_public !== modifyVisibility.is_public"
-              border="left"
-              color="warning">
-              <strong>Dangerous operation:</strong> you are about to change the visibility of the database. This affects all (sensitive) data held in the database.
-            </v-alert>
             <v-row dense>
               <v-col sm="6">
                 <v-select
@@ -55,7 +47,29 @@
             </v-row>
             <v-btn
               small
-              :disabled="database.is_public === modifyVisibility.is_public"
+              color="warning"
+              class="black--text"
+              @click="updateDatabaseVisibility">
+              Modify Visibility
+            </v-btn>
+          </v-card-text>
+        </v-card>
+        <v-divider />
+        <v-card v-if="canModifyOwnership" flat tile>
+          <v-card-title>Ownership</v-card-title>
+          <v-card-text>
+            <v-row dense>
+              <v-col sm="6">
+                <v-select
+                  id="owner"
+                  v-model="modifyOwner.username"
+                  :items="users"
+                  label="Owner"
+                  name="owner" />
+              </v-col>
+            </v-row>
+            <v-btn
+              small
               color="warning"
               class="black--text"
               @click="updateDatabaseVisibility">
@@ -89,12 +103,17 @@ export default {
       dialogDelete: false,
       confirm: null,
       username: null,
+      users: [],
       loading: false,
+      loadingUsers: false,
       editAccessDialog: false,
       editVisibilityDialog: false,
       modifyVisibility: {
         is_public: null
       },
+      modifyOwner: {
+        username: null
+      },
       visibility: [
         { text: 'Public', value: true },
         { text: 'Private', value: false }
@@ -153,6 +172,12 @@ export default {
         return false
       }
       return this.user.roles.includes('modify-database-visibility')
+    },
+    canModifyOwnership () {
+      if (!this.isOwner) {
+        return false
+      }
+      return this.user.roles.includes('modify-database-owner')
     }
   },
   watch: {
@@ -164,6 +189,7 @@ export default {
     }
   },
   mounted () {
+    this.loadUsers()
     if (!this.database) {
       return
     }
@@ -197,6 +223,19 @@ export default {
       this.username = item.user.username
       this.editAccessDialog = true
     },
+    async loadUsers () {
+      this.loadingUsers = true
+      try {
+        const res = await this.$axios.get('/api/user', this.config)
+        this.users = res.data
+        console.debug('users', this.users)
+      } catch (error) {
+        console.error('Failed to load users', error)
+        const { message } = error.response.data
+        this.$toast.error(`Failed to load users: ${message}`)
+      }
+      this.loadingUsers = false
+    },
     async loadDatabase () {
       if (!this.$route.params.container_id || !this.$route.params.database_id) {
         return
diff --git a/fda-user-service/.gitignore b/fda-user-service/.gitignore
new file mode 100644
index 0000000000..d68acdb421
--- /dev/null
+++ b/fda-user-service/.gitignore
@@ -0,0 +1,44 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+# Documentation
+docs/*.html
+docs/css/
+docs/images/
+
+# Docker
+ready
+
+## JUnit
+.attach_pid*
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/fda-user-service/.mvn/wrapper/MavenWrapperDownloader.java b/fda-user-service/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000000..a45eb6ba26
--- /dev/null
+++ b/fda-user-service/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+    private static final String WRAPPER_VERSION = "0.5.6";
+    /**
+     * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+     */
+    private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+            + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+    /**
+     * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+     * use instead of the default one.
+     */
+    private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+            ".mvn/wrapper/maven-wrapper.properties";
+
+    /**
+     * Path where the maven-wrapper.jar will be saved to.
+     */
+    private static final String MAVEN_WRAPPER_JAR_PATH =
+            ".mvn/wrapper/maven-wrapper.jar";
+
+    /**
+     * Name of the property which should be used to override the default download url for the wrapper.
+     */
+    private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+    public static void main(String args[]) {
+        System.out.println("- Downloader started");
+        File baseDirectory = new File(args[0]);
+        System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+        // If the maven-wrapper.properties exists, read it and check if it contains a custom
+        // wrapperUrl parameter.
+        File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+        String url = DEFAULT_DOWNLOAD_URL;
+        if (mavenWrapperPropertyFile.exists()) {
+            FileInputStream mavenWrapperPropertyFileInputStream = null;
+            try {
+                mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+                Properties mavenWrapperProperties = new Properties();
+                mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+                url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+            } catch (IOException e) {
+                System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+            } finally {
+                try {
+                    if (mavenWrapperPropertyFileInputStream != null) {
+                        mavenWrapperPropertyFileInputStream.close();
+                    }
+                } catch (IOException e) {
+                    // Ignore ...
+                }
+            }
+        }
+        System.out.println("- Downloading from: " + url);
+
+        File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+        if (!outputFile.getParentFile().exists()) {
+            if (!outputFile.getParentFile().mkdirs()) {
+                System.out.println(
+                        "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+            }
+        }
+        System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+        try {
+            downloadFileFromURL(url, outputFile);
+            System.out.println("Done");
+            System.exit(0);
+        } catch (Throwable e) {
+            System.out.println("- Error downloading");
+            e.printStackTrace();
+            System.exit(1);
+        }
+    }
+
+    private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+        if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+            String username = System.getenv("MVNW_USERNAME");
+            char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+            Authenticator.setDefault(new Authenticator() {
+                @Override
+                protected PasswordAuthentication getPasswordAuthentication() {
+                    return new PasswordAuthentication(username, password);
+                }
+            });
+        }
+        URL website = new URL(urlString);
+        ReadableByteChannel rbc;
+        rbc = Channels.newChannel(website.openStream());
+        FileOutputStream fos = new FileOutputStream(destination);
+        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+        fos.close();
+        rbc.close();
+    }
+
+}
diff --git a/fda-user-service/.mvn/wrapper/maven-wrapper.jar b/fda-user-service/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000000000000000000000000000000000000..2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054
GIT binary patch
literal 50710
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

literal 0
HcmV?d00001

diff --git a/fda-user-service/.mvn/wrapper/maven-wrapper.properties b/fda-user-service/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000..642d572ce9
--- /dev/null
+++ b/fda-user-service/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/fda-user-service/Dockerfile b/fda-user-service/Dockerfile
new file mode 100644
index 0000000000..dd9fe0250e
--- /dev/null
+++ b/fda-user-service/Dockerfile
@@ -0,0 +1,42 @@
+###### FIRST STAGE ######
+FROM fda-metadata-db:latest as dependency
+MAINTAINER Martin Weise <martin.weise@tuwien.ac.at>
+
+###### SECOND STAGE ######
+FROM maven:slim as build
+
+COPY ./pom.xml ./
+
+RUN mvn -fn -B dependency:go-offline > /dev/null
+
+COPY --from=dependency /root/.m2/repository/at/tuwien /root/.m2/repository/at/tuwien
+
+COPY ./rest-service ./rest-service
+COPY ./services ./services
+COPY ./report ./report
+
+# Make sure it compiles
+RUN mvn -q clean package -DskipTests > /dev/null
+
+###### THIRD STAGE ######
+FROM openjdk:11-jre-slim as runtime
+
+ENV METADATA_DB=fda
+ENV METADATA_USERNAME=root
+ENV METADATA_PASSWORD=dbrepo
+ENV LOG_LEVEL=debug
+ENV DBREPO_CLIENT_SECRET=client-secret
+ENV CLIENT_ID=dbrepo-client
+ENV JWT_ISSUER=http://localhost:8080/realms/dbrepo
+ENV JWT_PUBKEY=public-key
+
+COPY ./service_ready /usr/bin
+RUN chmod +x /usr/bin/service_ready
+
+HEALTHCHECK --interval=10s --timeout=5s --retries=12 CMD service_ready
+
+COPY --from=build ./rest-service/target/rest-service-*.jar ./user-service.jar
+
+EXPOSE 9093
+
+ENTRYPOINT ["java", "-Dlog4j2.formatMsgNoLookups=true", "-jar", "./user-service.jar"]
diff --git a/fda-user-service/mvnw b/fda-user-service/mvnw
new file mode 100755
index 0000000000..a16b5431b4
--- /dev/null
+++ b/fda-user-service/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`which java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    if [ -n "$MVNW_REPOURL" ]; then
+      jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+    else
+      jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+    fi
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+    if $cygwin; then
+      wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+    fi
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget "$jarUrl" -O "$wrapperJarPath"
+        else
+            wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+        fi
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl -o "$wrapperJarPath" "$jarUrl" -f
+        else
+            curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+        fi
+
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaClass=`cygpath --path --windows "$javaClass"`
+        fi
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/fda-user-service/mvnw.cmd b/fda-user-service/mvnw.cmd
new file mode 100644
index 0000000000..c8d43372c9
--- /dev/null
+++ b/fda-user-service/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %DOWNLOAD_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/fda-user-service/pom.xml b/fda-user-service/pom.xml
new file mode 100644
index 0000000000..f06580d159
--- /dev/null
+++ b/fda-user-service/pom.xml
@@ -0,0 +1,288 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.3.10.RELEASE</version>
+    </parent>
+
+    <groupId>at.tuwien</groupId>
+    <artifactId>fda-user-service</artifactId>
+    <version>1.1.0-alpha</version>
+    <name>fda-user-service</name>
+    <description>
+        The query service provides an interface to insert data into the tables created by the table service. It
+        also allows for view-only (possibly paginated and versioned) query execution to the raw data and consumes
+        messages in the message queue from the Broker Service.
+    </description>
+    <url>https://dbrepo-docs.ossdip.at</url>
+    <developers>
+        <developer>
+            <name>Martin Weise</name>
+            <email>martin.weise@tuwien.ac.at</email>
+            <organization>TU Wien</organization>
+        </developer>
+        <developer>
+            <name>Moritz Staudinger</name>
+            <email>moritz.staudinger@tuwien.ac.at</email>
+            <organization>TU Wien</organization>
+        </developer>
+    </developers>
+
+    <packaging>pom</packaging>
+    <modules>
+        <module>rest-service</module>
+        <module>services</module>
+        <module>report</module>
+    </modules>
+
+    <properties>
+        <java.version>11</java.version>
+        <spring-cloud.version>3.0.1</spring-cloud.version>
+        <mapstruct.version>1.4.2.Final</mapstruct.version>
+        <docker.version>3.2.7</docker.version>
+        <testcontainers.version>1.15.2</testcontainers.version>
+        <swagger.version>2.1.7</swagger.version>
+        <springfox.version>3.0.0</springfox.version>
+        <jacoco.version>0.8.7</jacoco.version>
+        <opencsv.version>5.4</opencsv.version>
+        <hibernate-c3po.version>5.6.3.Final</hibernate-c3po.version>
+        <maven-report.version>3.0.0</maven-report.version>
+        <jwt.version>4.3.0</jwt.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-amqp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+        </dependency>
+        <dependency><!-- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#validation-starter-no-longer-included-in-web-starters -->
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-bootstrap</artifactId>
+            <version>${spring-cloud.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <!-- Authentication -->
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>${jwt.version}</version>
+        </dependency>
+        <!-- Monitoring -->
+        <dependency>
+            <groupId>io.micrometer</groupId>
+            <artifactId>micrometer-registry-prometheus</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <!-- Data Source -->
+        <dependency>
+            <groupId>org.mariadb.jdbc</groupId>
+            <artifactId>mariadb-java-client</artifactId>
+            <version>${mariadb.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.data</groupId>
+            <artifactId>spring-data-elasticsearch</artifactId>
+        </dependency>
+        <!-- AMPQ -->
+        <dependency>
+            <groupId>com.rabbitmq</groupId>
+            <artifactId>amqp-client</artifactId>
+            <version>${rabbit-amqp-client.version}</version>
+        </dependency>
+        <!-- Docker -->
+        <dependency>
+            <groupId>com.github.docker-java</groupId>
+            <artifactId>docker-java</artifactId>
+            <version>${docker.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.ws.rs</groupId>
+                    <artifactId>jsr311-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.github.docker-java</groupId>
+            <artifactId>docker-java-transport-httpclient5</artifactId>
+            <version>${docker.version}</version>
+        </dependency>
+        <!-- IDE -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <!-- Entity, API, QueryStore -->
+        <dependency>
+            <groupId>at.tuwien</groupId>
+            <artifactId>fda-metadata-db-api</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>at.tuwien</groupId>
+            <artifactId>fda-metadata-db-entites</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
+        <!-- Testing -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.jupiter</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jacoco</groupId>
+            <artifactId>jacoco-maven-plugin</artifactId>
+            <version>${jacoco.version}</version>
+        </dependency>
+        <!-- Mapping -->
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-processor</artifactId>
+            <version>${mapstruct.version}</version>
+            <optional>true</optional>
+        <!-- IntelliJ -->
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+            <version>${mapstruct.version}</version>
+        </dependency>
+        <!-- Gateway -->
+        <dependency>
+            <groupId>org.projectreactor</groupId>
+            <artifactId>reactor-spring</artifactId>
+            <version>1.0.1.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <version>2.1.1</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${basedir}/src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/application*.yml</include>
+                    <include>**/templates/*.xml</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>${jacoco.version}</version>
+                <configuration>
+                    <excludes>
+                        <exclude>at/tuwien/utils/**/*</exclude>
+                        <exclude>at/tuwien/mapper/**/*</exclude>
+                        <exclude>at/tuwien/entities/**/*</exclude>
+                        <exclude>at/tuwien/seeder/**/*</exclude>
+                        <exclude>at/tuwien/handlers/**/*</exclude>
+                        <exclude>at/tuwien/exception/**/*</exclude>
+                        <exclude>at/tuwien/config/**/*</exclude>
+                        <exclude>**/RabbitMqServiceImpl.class</exclude>
+                        <exclude>**/ServiceSeeder.class</exclude>
+                        <exclude>**/FdaQueryServiceApplication.class</exclude>
+                    </excludes>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>default-prepare-agent</id>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>report</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-site-plugin</artifactId>
+                <version>3.7.1</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-project-info-reports-plugin</artifactId>
+                <version>3.0.0</version>
+<!--                <configuration>-->
+<!--                    <outputDirectory>docs</outputDirectory>-->
+<!--                </configuration>-->
+            </plugin>
+            <plugin>
+                <groupId>com.soebes.maven.plugins</groupId>
+                <artifactId>doxygen-maven-plugin</artifactId>
+                <version>1.1.0</version>
+                <configuration>
+                    <haveDot>false</haveDot>
+                    <quiet>false</quiet>
+                    <projectName>This is a Test Project (basicReportTest)</projectName>
+                    <outputDirectory>docs</outputDirectory>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/fda-user-service/report/pom.xml b/fda-user-service/report/pom.xml
new file mode 100644
index 0000000000..b93720ffeb
--- /dev/null
+++ b/fda-user-service/report/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>fda-user-service</artifactId>
+        <groupId>at.tuwien</groupId>
+        <version>1.1.0-alpha</version>
+    </parent>
+
+    <artifactId>report</artifactId>
+    <version>1.1.0-alpha</version>
+    <name>fda-user-service-report</name>
+    <description>
+        This module is only intended for the pipeline coverage report. See the detailed report in the
+        respective modules
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>at.tuwien</groupId>
+            <artifactId>rest-service</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>at.tuwien</groupId>
+            <artifactId>services</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <properties>
+        <jacoco.version>0.8.7</jacoco.version>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>${jacoco.version}</version>
+                <executions>
+                    <execution>
+                        <id>report-aggregate</id>
+                        <phase>verify</phase>
+                        <goals>
+                            <goal>report-aggregate</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/fda-user-service/rest-service/pom.xml b/fda-user-service/rest-service/pom.xml
new file mode 100644
index 0000000000..6a53b116b3
--- /dev/null
+++ b/fda-user-service/rest-service/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>fda-user-service</artifactId>
+        <groupId>at.tuwien</groupId>
+        <version>1.1.0-alpha</version>
+    </parent>
+
+    <artifactId>rest-service</artifactId>
+    <version>1.1.0-alpha</version>
+    <name>fda-user-service-rest</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>at.tuwien</groupId>
+            <artifactId>services</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal><!-- to make it exuteable with $ java -jar ./app.jar -->
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/main/java/at/tuwien/FdaUserServiceApplication.java b/fda-user-service/rest-service/src/main/java/at/tuwien/FdaUserServiceApplication.java
new file mode 100644
index 0000000000..4a6c3dff08
--- /dev/null
+++ b/fda-user-service/rest-service/src/main/java/at/tuwien/FdaUserServiceApplication.java
@@ -0,0 +1,25 @@
+package at.tuwien;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+@EnableJpaAuditing
+@SpringBootApplication
+@EnableTransactionManagement
+@EnableScheduling
+@EntityScan(basePackages = "at.tuwien.entities")
+@EnableElasticsearchRepositories(basePackages = {"at.tuwien.repository.elastic"})
+@EnableJpaRepositories(basePackages = {"at.tuwien.repository.jpa"})
+public class FdaUserServiceApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(FdaUserServiceApplication.class, args);
+    }
+
+}
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java b/fda-user-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java
new file mode 100644
index 0000000000..eb08f946aa
--- /dev/null
+++ b/fda-user-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java
@@ -0,0 +1,46 @@
+package at.tuwien.config;
+
+import io.swagger.v3.oas.models.ExternalDocumentation;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import org.springdoc.core.GroupedOpenApi;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class SwaggerConfig {
+
+    @Value("${app.version:unknown}")
+    private String version;
+
+    @Bean
+    public OpenAPI springShopOpenAPI() {
+        return new OpenAPI()
+                .info(new Info()
+                        .title("Database Repository User Service API")
+                        .contact(new Contact()
+                                .name("Prof. Andreas Rauber")
+                                .email("andreas.rauber@tuwien.ac.at"))
+                        .description("Service that manages the users")
+                        .version(version)
+                        .license(new License()
+                                .name("Apache 2.0")
+                                .url("https://www.apache.org/licenses/LICENSE-2.0")))
+                .externalDocs(new ExternalDocumentation()
+                        .description("Wiki Documentation")
+                        .url("https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/wikis"));
+    }
+
+    @Bean
+    public GroupedOpenApi publicApi() {
+        return GroupedOpenApi.builder()
+                .group("user-service")
+                .pathsToMatch("/api/**")
+                .build();
+    }
+
+}
+
diff --git a/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java b/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java
new file mode 100644
index 0000000000..7bb8dc3149
--- /dev/null
+++ b/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java
@@ -0,0 +1,58 @@
+package at.tuwien.endpoint;
+
+import at.tuwien.api.auth.SignupRequestDto;
+import at.tuwien.api.user.UserBriefDto;
+import at.tuwien.mapper.UserMapper;
+import at.tuwien.service.UserService;
+import io.micrometer.core.annotation.Timed;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Log4j2
+@CrossOrigin(origins = "*")
+@RestController
+@RequestMapping("/api/user")
+public class UserEndpoint {
+
+    private final UserMapper userMapper;
+    private final UserService userService;
+
+    @Autowired
+    public UserEndpoint(UserMapper userMapper, UserService userService) {
+        this.userMapper = userMapper;
+        this.userService = userService;
+    }
+
+    @GetMapping
+    @Transactional(readOnly = true)
+    @Timed(value = "user.list", description = "Time needed to list all users in the metadata database")
+    @Operation(summary = "Find all users")
+    public ResponseEntity<List<UserBriefDto>> findAll() {
+        log.debug("endpoint find all users");
+        final List<UserBriefDto> users = userService.findAll()
+                .stream()
+                .map(userMapper::userToUserBriefDto)
+                .collect(Collectors.toList());
+        log.trace("find all users resulted in users {}", users);
+        return ResponseEntity.ok(users);
+    }
+
+    @PostMapping
+    @Transactional
+    @Timed(value = "user.create", description = "Time needed to create a user in the metadata database")
+    @Operation(summary = "Create a user")
+    public ResponseEntity<UserBriefDto> create(SignupRequestDto data) {
+        log.debug("endpoint create a user, data={}", data);
+        final UserBriefDto dto = userMapper.userToUserBriefDto(userService.create(data));
+        log.trace("create user resulted in dto {}", dto);
+        return ResponseEntity.ok(dto);
+    }
+
+}
diff --git a/fda-user-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java b/fda-user-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java
new file mode 100644
index 0000000000..b5d0c1658b
--- /dev/null
+++ b/fda-user-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java
@@ -0,0 +1,249 @@
+package at.tuwien.handlers;
+
+import at.tuwien.api.error.ApiErrorDto;
+import at.tuwien.exception.*;
+import net.sf.jsqlparser.JSQLParserException;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+@ControllerAdvice
+public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
+
+    @ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
+    @ExceptionHandler(AmqpException.class)
+    public ResponseEntity<ApiErrorDto> handle(AmqpException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.NOT_ACCEPTABLE)
+                .message(e.getLocalizedMessage())
+                .code("error.query.amqp")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.EXPECTATION_FAILED)
+    @ExceptionHandler(ColumnParseException.class)
+    public ResponseEntity<ApiErrorDto> handle(ColumnParseException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.EXPECTATION_FAILED)
+                .message(e.getLocalizedMessage())
+                .code("error.query.columnparse")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.NOT_FOUND)
+    @ExceptionHandler(ContainerNotFoundException.class)
+    public ResponseEntity<ApiErrorDto> handle(ContainerNotFoundException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.NOT_FOUND)
+                .message(e.getLocalizedMessage())
+                .code("error.query.containernotfound")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
+    @ExceptionHandler(DatabaseConnectionException.class)
+    public ResponseEntity<ApiErrorDto> handle(DatabaseConnectionException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.METHOD_NOT_ALLOWED)
+                .message(e.getLocalizedMessage())
+                .code("error.query.databaseconnection")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.NOT_FOUND)
+    @ExceptionHandler(DatabaseNotFoundException.class)
+    public ResponseEntity<ApiErrorDto> handle(DatabaseNotFoundException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.NOT_FOUND)
+                .message(e.getLocalizedMessage())
+                .code("error.query.databasenotfound")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ExceptionHandler(FileStorageException.class)
+    public ResponseEntity<ApiErrorDto> handle(FileStorageException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.BAD_REQUEST)
+                .message(e.getLocalizedMessage())
+                .code("error.query.filestore")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.NOT_FOUND)
+    @ExceptionHandler(IdentifierNotFoundException.class)
+    public ResponseEntity<ApiErrorDto> handle(IdentifierNotFoundException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.NOT_FOUND)
+                .message(e.getLocalizedMessage())
+                .code("error.query.identifiernotfound")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.CONFLICT)
+    @ExceptionHandler(ImageNotSupportedException.class)
+    public ResponseEntity<ApiErrorDto> handle(ImageNotSupportedException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.CONFLICT)
+                .message(e.getLocalizedMessage())
+                .code("error.query.imagenotsupported")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
+    @ExceptionHandler(NotAllowedException.class)
+    public ResponseEntity<ApiErrorDto> handle(NotAllowedException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.METHOD_NOT_ALLOWED)
+                .message(e.getLocalizedMessage())
+                .code("error.query.permission")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ExceptionHandler(PaginationException.class)
+    public ResponseEntity<ApiErrorDto> handle(PaginationException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.BAD_REQUEST)
+                .message(e.getLocalizedMessage())
+                .code("error.query.pagination")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.CONFLICT)
+    @ExceptionHandler(QueryAlreadyPersistedException.class)
+    public ResponseEntity<ApiErrorDto> handle(QueryAlreadyPersistedException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.CONFLICT)
+                .message(e.getLocalizedMessage())
+                .code("error.query.alreadypersisted")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ExceptionHandler(QueryMalformedException.class)
+    public ResponseEntity<ApiErrorDto> handle(QueryMalformedException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.BAD_REQUEST)
+                .message(e.getLocalizedMessage())
+                .code("error.query.malformed")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.NOT_FOUND)
+    @ExceptionHandler(QueryNotFoundException.class)
+    public ResponseEntity<ApiErrorDto> handle(QueryNotFoundException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.NOT_FOUND)
+                .message(e.getLocalizedMessage())
+                .code("error.query.notfound")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.GATEWAY_TIMEOUT)
+    @ExceptionHandler(QueryStoreException.class)
+    public ResponseEntity<ApiErrorDto> handle(QueryStoreException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.GATEWAY_TIMEOUT)
+                .message(e.getLocalizedMessage())
+                .code("error.query.store")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    @ExceptionHandler(SortException.class)
+    public ResponseEntity<ApiErrorDto> handle(SortException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.BAD_REQUEST)
+                .message(e.getLocalizedMessage())
+                .code("error.query.sort")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.LOCKED)
+    @ExceptionHandler(TableMalformedException.class)
+    public ResponseEntity<ApiErrorDto> handle(TableMalformedException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.LOCKED)
+                .message(e.getLocalizedMessage())
+                .code("error.query.tablemalformed")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.NOT_FOUND)
+    @ExceptionHandler(TableNotFoundException.class)
+    public ResponseEntity<ApiErrorDto> handle(TableNotFoundException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.NOT_FOUND)
+                .message(e.getLocalizedMessage())
+                .code("error.query.tablenotfound")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.CONFLICT)
+    @ExceptionHandler(TupleDeleteException.class)
+    public ResponseEntity<ApiErrorDto> handle(TupleDeleteException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.CONFLICT)
+                .message(e.getLocalizedMessage())
+                .code("error.query.tupledelete")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.NOT_FOUND)
+    @ExceptionHandler(UserNotFoundException.class)
+    public ResponseEntity<ApiErrorDto> handle(UserNotFoundException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.NOT_FOUND)
+                .message(e.getLocalizedMessage())
+                .code("error.query.usernotfound")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.LOCKED)
+    @ExceptionHandler(ViewMalformedException.class)
+    public ResponseEntity<ApiErrorDto> handle(ViewMalformedException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.LOCKED)
+                .message(e.getLocalizedMessage())
+                .code("error.query.viewmalformed")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+    @ResponseStatus(HttpStatus.NOT_FOUND)
+    @ExceptionHandler(ViewNotFoundException.class)
+    public ResponseEntity<ApiErrorDto> handle(ViewNotFoundException e, WebRequest request) {
+        final ApiErrorDto response = ApiErrorDto.builder()
+                .status(HttpStatus.NOT_FOUND)
+                .message(e.getLocalizedMessage())
+                .code("error.query.viewnotfound")
+                .build();
+        return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus());
+    }
+
+}
diff --git a/fda-user-service/rest-service/src/main/resources/application-docker.yml b/fda-user-service/rest-service/src/main/resources/application-docker.yml
new file mode 100644
index 0000000000..084cc82986
--- /dev/null
+++ b/fda-user-service/rest-service/src/main/resources/application-docker.yml
@@ -0,0 +1,40 @@
+app.version: '@project.version@'
+spring:
+  main.banner-mode: off
+  datasource:
+    url: jdbc:mariadb://metadata-db:3306/fda
+    driver-class-name: org.mariadb.jdbc.Driver
+    username: "${METADATA_USERNAME}"
+    password: "${METADATA_PASSWORD}"
+  jpa:
+    show-sql: false
+    database-platform: org.hibernate.dialect.MariaDBDialect
+    hibernate:
+      ddl-auto: validate
+      use-new-id-generator-mappings: false
+    open-in-view: false
+    properties:
+      hibernate:
+        default_schema: fda
+        jdbc:
+          time_zone: UTC
+  application:
+    name: user-service
+  cloud:
+    loadbalancer.ribbon.enabled: false
+management.endpoints.web.exposure.include: health,info,prometheus
+server.port: 9098
+logging:
+  pattern.console: "%d %highlight(%-5level) %msg%n"
+  level:
+    root: warn
+    at.tuwien.: "${LOG_LEVEL}"
+    org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug
+eureka:
+  instance.hostname: user-service
+  client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/
+fda:
+  ready.path: /ready
+  jwt:
+    issuer: "${JWT_ISSUER}"
+    public_key: "${JWT_PUBKEY}"
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/main/resources/application-local.yml b/fda-user-service/rest-service/src/main/resources/application-local.yml
new file mode 100644
index 0000000000..4b86aa03a8
--- /dev/null
+++ b/fda-user-service/rest-service/src/main/resources/application-local.yml
@@ -0,0 +1,40 @@
+app.version: '@project.version@'
+spring:
+  main.banner-mode: off
+  datasource:
+    url: jdbc:mariadb://localhost:3306/fda
+    driver-class-name: org.mariadb.jdbc.Driver
+    username: root
+    password: dbrepo
+  jpa:
+    show-sql: false
+    database-platform: org.hibernate.dialect.MariaDBDialect
+    hibernate:
+      ddl-auto: validate
+      use-new-id-generator-mappings: false
+    open-in-view: false
+    properties:
+      hibernate:
+        default_schema: fda
+        jdbc:
+          time_zone: UTC
+  application:
+    name: user-service
+  cloud:
+    loadbalancer.ribbon.enabled: false
+management.endpoints.web.exposure.include: health,info,prometheus
+server.port: 9098
+logging:
+  pattern.console: "%d %highlight(%-5level) %msg%n"
+  level:
+    root: warn
+    at.tuwien.: trace
+    org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug
+eureka:
+  instance.hostname: user-service
+  client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/
+fda:
+  ready.path: /ready
+  jwt:
+    issuer: http://localhost:8080/realms/dbrepo
+    public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqnHQ2BWWW9vDNLRCcxD++xZg/16oqMo/c1l+lcFEjjAIJjJp/HqrPYU/U9GvquGE6PbVFtTzW1KcKawOW+FJNOA3CGo8Q1TFEfz43B8rZpKsFbJKvQGVv1Z4HaKPvLUm7iMm8Hv91cLduuoWx6Q3DPe2vg13GKKEZe7UFghF+0T9u8EKzA/XqQ0OiICmsmYPbwvf9N3bCKsB/Y10EYmZRb8IhCoV9mmO5TxgWgiuNeCTtNCv2ePYqL/U0WvyGFW0reasIK8eg3KrAUj8DpyOgPOVBn3lBGf+3KFSYi+0bwZbJZWqbC/Xlk20Go1YfeJPRIt7ImxD27R/lNjgDO/MwIDAQAB
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/main/resources/application.yml b/fda-user-service/rest-service/src/main/resources/application.yml
new file mode 100644
index 0000000000..9ba4e1547a
--- /dev/null
+++ b/fda-user-service/rest-service/src/main/resources/application.yml
@@ -0,0 +1,40 @@
+app.version: '@project.version@'
+spring:
+  main.banner-mode: off
+  datasource:
+    url: "jdbc:mariadb://metadata-db:3306/${METADATA_DB}"
+    driver-class-name: org.mariadb.jdbc.Driver
+    username: "${METADATA_USERNAME}"
+    password: "${METADATA_PASSWORD}"
+  jpa:
+    show-sql: false
+    database-platform: org.hibernate.dialect.MariaDBDialect
+    hibernate:
+      ddl-auto: validate
+      use-new-id-generator-mappings: false
+    open-in-view: false
+    properties:
+      hibernate:
+        default_schema: "${METADATA_DB}"
+        jdbc:
+          time_zone: UTC
+  application:
+    name: user-service
+  cloud:
+    loadbalancer.ribbon.enabled: false
+management.endpoints.web.exposure.include: health,info,prometheus
+server.port: 9098
+logging:
+  pattern.console: "%d %highlight(%-5level) %msg%n"
+  level:
+    root: warn
+    at.tuwien.: "${LOG_LEVEL}"
+    org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug
+eureka:
+  instance.hostname: user-service
+  client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/
+fda:
+  ready.path: /ready
+  jwt:
+    issuer: "${JWT_ISSUER}"
+    public_key: "${JWT_PUBKEY}"
diff --git a/fda-user-service/rest-service/src/main/resources/config.properties b/fda-user-service/rest-service/src/main/resources/config.properties
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/fda-user-service/rest-service/src/main/resources/mariadb_hibernate.cfg.xml b/fda-user-service/rest-service/src/main/resources/mariadb_hibernate.cfg.xml
new file mode 100644
index 0000000000..01f90448ca
--- /dev/null
+++ b/fda-user-service/rest-service/src/main/resources/mariadb_hibernate.cfg.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE hibernate-configuration PUBLIC
+        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
+<hibernate-configuration>
+    <session-factory>
+        <property name="current_session_context_class">thread</property>
+        <property name="transaction.coordinator_class">jdbc</property>
+        <property name="c3p0.min_size">1</property>
+        <property name="c3p0.max_size">30</property>
+        <property name="c3p0.acquire_increment">1</property>
+        <property name="c3p0.timeout">1800</property>
+        <property name="show_sql">true</property>
+        <property name="format_sql">true</property>
+        <property name="hbm2ddl.auto">update</property>
+        <mapping class="at.tuwien.querystore.Column" />
+        <mapping class="at.tuwien.querystore.Query" />
+        <mapping class="at.tuwien.querystore.Table" />
+    </session-factory>
+</hibernate-configuration>
diff --git a/fda-user-service/rest-service/src/test/resources/application.properties b/fda-user-service/rest-service/src/test/resources/application.properties
new file mode 100644
index 0000000000..4eea490a91
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/application.properties
@@ -0,0 +1,32 @@
+# enable local spring profile
+spring.profiles.active=local
+
+# disable discovery
+spring.cloud.discovery.enabled=false
+
+# disable cloud config and config discovery
+spring.cloud.config.discovery.enabled=false
+spring.cloud.config.enabled=false
+
+# internal datasource
+spring.datasource.url=jdbc:h2:mem:testdb;DATABASE_TO_UPPER=false;DB_CLOSE_ON_EXIT=FALSE;INIT=RUNSCRIPT FROM './src/test/resources/schema.sql'
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=password
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.jpa.hibernate.ddl-auto=create-drop
+spring.jpa.show-sql=false
+
+# additional logging
+logging.level.root=error
+logging.level.at.tuwien.=trace
+
+# broker service
+spring.rabbitmq.host=dbrepo-broker-service
+spring.rabbitmq.username=guest
+spring.rabbitmq.password=guest
+
+# search service
+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-user-service/rest-service/src/test/resources/schema.sql b/fda-user-service/rest-service/src/test/resources/schema.sql
new file mode 100644
index 0000000000..906d8df808
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/schema.sql
@@ -0,0 +1,25 @@
+CREATE SCHEMA IF NOT EXISTS `fda`;
+SET SCHEMA `fda`;
+DROP TABLE IF EXISTS fda.mdb_concepts;
+CREATE TABLE IF NOT EXISTS fda.mdb_concepts
+(
+    uri        VARCHAR(500) not null,
+    name       VARCHAR(255),
+    created    timestamp    NOT NULL DEFAULT NOW(),
+    created_by bigint,
+    PRIMARY KEY (uri)
+);
+DROP TABLE IF EXISTS fda.mdb_units;
+CREATE TABLE IF NOT EXISTS fda.mdb_units
+(
+    uri        VARCHAR(500) not null,
+    name       VARCHAR(255),
+    created    timestamp    NOT NULL DEFAULT NOW(),
+    created_by bigint,
+    PRIMARY KEY (uri)
+);
+-- Modified for H2
+-- Assume id=1 is invalid
+-- Assume id=2 is still valid token
+-- CREATE VIEW IF NOT EXISTS fda.mdb_invalid_tokens AS
+-- (SELECT `id`, `token_hash`, `creator`, `created`, `expires`, `last_used` FROM fda.`mdb_tokens` WHERE `id` = 1);
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/test/resources/sensor/1_querystore.sql b/fda-user-service/rest-service/src/test/resources/sensor/1_querystore.sql
new file mode 100644
index 0000000000..2762d130a0
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/sensor/1_querystore.sql
@@ -0,0 +1,75 @@
+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
+);
+DELIMITER $$
+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 $$
+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;
+    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 $$
+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;
+    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 $$
+DELIMITER ;
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/test/resources/sensor/2_traffic.sql b/fda-user-service/rest-service/src/test/resources/sensor/2_traffic.sql
new file mode 100644
index 0000000000..93d293a6fe
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/sensor/2_traffic.sql
@@ -0,0 +1,8 @@
+CREATE SEQUENCE seq_sensor
+    START 1;
+
+CREATE TABLE sensor
+(
+    `timestamp` TIMESTAMP NULL,
+    primary key (`timestamp`)
+) with system versioning;
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/test/resources/traffic/1_querystore.sql b/fda-user-service/rest-service/src/test/resources/traffic/1_querystore.sql
new file mode 100644
index 0000000000..2762d130a0
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/traffic/1_querystore.sql
@@ -0,0 +1,75 @@
+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
+);
+DELIMITER $$
+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 $$
+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;
+    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 $$
+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;
+    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 $$
+DELIMITER ;
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/test/resources/traffic/2_traffic.sql b/fda-user-service/rest-service/src/test/resources/traffic/2_traffic.sql
new file mode 100644
index 0000000000..3038fe5a7a
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/traffic/2_traffic.sql
@@ -0,0 +1,43 @@
+/* https://www.kaggle.com/laa283/zurich-public-transport/version/2 */
+CREATE SEQUENCE seq_traffic
+    START 1;
+
+CREATE TABLE traffic_zurich
+(
+    linie                bigint                                null,
+    richtung             bigint                                null,
+    betriebsdatum        date                                  null,
+    fahrzeug             bigint                                null,
+    kurs                 bigint                                null,
+    seq_von              bigint                                null,
+    halt_diva_von        bigint                                null,
+    halt_punkt_diva_von  bigint                                null,
+    halt_kurz_von1       varchar(255)                          null,
+    datum_von            date                                  null,
+    soll_an_von          bigint                                null,
+    ist_an_von           bigint                                null,
+    soll_ab_von          bigint                                null,
+    ist_ab_von           bigint                                null,
+    seq_nach             bigint                                null,
+    halt_diva_nach       bigint                                null,
+    halt_punkt_diva_nach bigint                                null,
+    halt_kurz_nach1      varchar(255)                          null,
+    datum_nach           DATE                                  null,
+    soll_an_nach         bigint                                null,
+    ist_an_nach1         bigint                                null,
+    soll_ab_nach         bigint                                null,
+    ist_ab_nach          bigint                                null,
+    fahrt_id             bigint                                null,
+    fahrweg_id           bigint                                null,
+    fw_no                bigint                                null,
+    fw_typ               bigint                                null,
+    fw_kurz              bigint                                null,
+    fw_lang              varchar(255)                          null,
+    umlauf_von           varchar(255)                          null,
+    halt_id_von          bigint                                null,
+    halt_id_nach         bigint                                null,
+    halt_punkt_id_von    bigint                                null,
+    halt_punkt_id_nach   bigint                                null,
+    id                   bigint default nextval(`seq_traffic`) not null,
+    primary key (id)
+) with system versioning;
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/test/resources/weather/1_querystore.sql b/fda-user-service/rest-service/src/test/resources/weather/1_querystore.sql
new file mode 100644
index 0000000000..2762d130a0
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/weather/1_querystore.sql
@@ -0,0 +1,75 @@
+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
+);
+DELIMITER $$
+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 $$
+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;
+    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 $$
+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;
+    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 $$
+DELIMITER ;
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/test/resources/weather/2_weather.sql b/fda-user-service/rest-service/src/test/resources/weather/2_weather.sql
new file mode 100644
index 0000000000..29967f2977
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/weather/2_weather.sql
@@ -0,0 +1,55 @@
+/* https://www.kaggle.com/jsphyg/weather-dataset-rattle-package */
+CREATE TABLE weather_location
+(
+    location VARCHAR(255) PRIMARY KEY,
+    lat      DOUBLE PRECISION NULL,
+    lng      DOUBLE PRECISION NULL
+) WITH SYSTEM VERSIONING;
+
+CREATE TABLE weather_aus
+(
+    id       BIGINT           NOT NULL PRIMARY KEY,
+    `date`   DATE             NOT NULL,
+    location VARCHAR(255)     NULL,
+    mintemp  DOUBLE PRECISION NULL,
+    rainfall DOUBLE PRECISION NULL,
+    FOREIGN KEY (location) REFERENCES weather_location (location),
+    UNIQUE (`date`),
+    CHECK (`mintemp` > 0)
+) WITH SYSTEM VERSIONING;
+
+CREATE TABLE sensor
+(
+    `timestamp` TIMESTAMP NOT NULL,
+    PRIMARY KEY (`timestamp`),
+    UNIQUE (`timestamp`)
+) WITH SYSTEM VERSIONING;
+
+INSERT INTO weather_location (location, lat, lng)
+VALUES ('Albury', -36.0653583, 146.9112214),
+       ('Melbourne', null, null),
+       ('Sydney', -33.847927, 150.6517942);
+
+INSERT INTO weather_aus (id, `date`, location, mintemp, rainfall)
+VALUES (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);
+
+########################################################################################################################
+## TEST CASE PRE-REQUISITE                                                                                            ##
+########################################################################################################################
+
+CREATE VIEW junit2 AS
+(
+SELECT `location`, `lat`, `lng`
+FROM `weather_location`
+WHERE `location` = 'Albury');
+
+CREATE VIEW `hs_weather_aus` AS
+SELECT *
+FROM (SELECT `id`, ROW_START AS inserted_at, IF(ROW_END > NOW(), NULL, ROW_END) AS deleted_at, COUNT(*) as total
+      FROM `weather_aus` FOR SYSTEM_TIME ALL
+      GROUP BY inserted_at, deleted_at
+      ORDER BY deleted_at DESC
+      LIMIT 50) AS v
+ORDER BY v.inserted_at, v.deleted_at ASC;
diff --git a/fda-user-service/rest-service/src/test/resources/weather/location.csv b/fda-user-service/rest-service/src/test/resources/weather/location.csv
new file mode 100644
index 0000000000..b9410c65c9
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/weather/location.csv
@@ -0,0 +1,2 @@
+Albury,-36.0653583,146.9112214
+Sydney,-33.847927,150.6517942
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/test/resources/zoo/1_querystore.sql b/fda-user-service/rest-service/src/test/resources/zoo/1_querystore.sql
new file mode 100644
index 0000000000..2762d130a0
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/zoo/1_querystore.sql
@@ -0,0 +1,75 @@
+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
+);
+DELIMITER $$
+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 $$
+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;
+    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 $$
+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;
+    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 $$
+DELIMITER ;
\ No newline at end of file
diff --git a/fda-user-service/rest-service/src/test/resources/zoo/2_zoo.sql b/fda-user-service/rest-service/src/test/resources/zoo/2_zoo.sql
new file mode 100644
index 0000000000..50f02ee2c5
--- /dev/null
+++ b/fda-user-service/rest-service/src/test/resources/zoo/2_zoo.sql
@@ -0,0 +1,191 @@
+create sequence seq_zoo_id;
+create sequence seq_names_id;
+create table zoo
+(
+    id          bigint       not null default nextval(`seq_zoo_id`),
+    animal_name varchar(255) null,
+    hair        tinyint(1)   null,
+    feathers    tinyint(1)   null,
+    eggs        tinyint(1)   null,
+    milk        tinyint(1)   null,
+    airborne    tinyint(1)   null,
+    aquatic     tinyint(1)   null,
+    predator    tinyint(1)   null,
+    toothed     tinyint(1)   null,
+    backbone    tinyint(1)   null,
+    breathes    tinyint(1)   null,
+    venomous    tinyint(1)   null,
+    fins        tinyint(1)   null,
+    legs        bigint       null,
+    tail        tinyint(1)   null,
+    domestic    tinyint(1)   null,
+    catsize     tinyint(1)   null,
+    class_type  bigint       null,
+    primary key (id)
+) with system versioning;
+
+create table names
+(
+    id        bigint not null default nextval(`seq_names_id`),
+    firstname varchar(255),
+    lastname  varchar(255),
+    primary key (id),
+    unique key (firstname, lastname)
+) with system versioning;
+
+create table likes
+(
+    name_id bigint not null,
+    zoo_id  bigint not null,
+    primary key (name_id, zoo_id),
+    foreign key (name_id) references names (id),
+    foreign key (zoo_id) references zoo (id)
+) with system versioning;
+
+INSERT INTO zoo (id, animal_name, hair, feathers, eggs, milk, airborne, aquatic, predator, toothed, backbone, breathes,
+                 venomous, fins, legs, tail, domestic, catsize, class_type)
+VALUES (1, 'aardvark', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 0, 0, 1, 1),
+       (2, 'antelope', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (3, 'bass', 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 4),
+       (4, 'bear', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 0, 0, 1, 1),
+       (5, 'boar', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (6, 'buffalo', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (7, 'calf', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 1, 1, 1),
+       (8, 'carp', 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 4),
+       (9, 'catfish', 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 4),
+       (10, 'cavy', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 0, 1, 0, 1),
+       (11, 'cheetah', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (12, 'chicken', 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1, 1, 0, 2),
+       (13, 'chub', 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 4),
+       (14, 'clam', 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7),
+       (15, 'crab', 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 7),
+       (16, 'crayfish', 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7),
+       (17, 'crow', 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2),
+       (18, 'deer', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (19, 'dogfish', 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 4),
+       (20, 'dolphin', 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1),
+       (21, 'dove', 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1, 1, 0, 2),
+       (22, 'duck', 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2),
+       (23, 'elephant', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (24, 'flamingo', 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1, 0, 1, 2),
+       (25, 'flea', 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 6, 0, 0, 0, 6),
+       (26, 'frog', 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 4, 0, 0, 0, 5),
+       (27, 'frog', 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 4, 0, 0, 0, 5),
+       (28, 'fruitbat', 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 2, 1, 0, 0, 1),
+       (29, 'giraffe', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (30, 'girl', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 2, 0, 1, 1, 1),
+       (31, 'gnat', 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 6, 0, 0, 0, 6),
+       (32, 'goat', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 1, 1, 1),
+       (33, 'gorilla', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 2, 0, 0, 1, 1),
+       (34, 'gull', 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2),
+       (35, 'haddock', 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 4),
+       (36, 'hamster', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 1, 0, 1),
+       (37, 'hare', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 0, 0, 1),
+       (38, 'hawk', 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2),
+       (39, 'herring', 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 4),
+       (40, 'honeybee', 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 6, 0, 1, 0, 6),
+       (41, 'housefly', 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 6, 0, 0, 0, 6),
+       (42, 'kiwi', 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2),
+       (43, 'ladybird', 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 6, 0, 0, 0, 6),
+       (44, 'lark', 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2),
+       (45, 'leopard', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (46, 'lion', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (47, 'lobster', 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7),
+       (48, 'lynx', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (49, 'mink', 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (50, 'mole', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 0, 1),
+       (51, 'mongoose', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (52, 'moth', 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 6, 0, 0, 0, 6),
+       (53, 'newt', 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 4, 1, 0, 0, 5),
+       (54, 'octopus', 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 8, 0, 0, 1, 7),
+       (55, 'opossum', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 0, 1),
+       (56, 'oryx', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (57, 'ostrich', 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 1, 0, 1, 2),
+       (58, 'parakeet', 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1, 1, 0, 2),
+       (59, 'penguin', 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 2, 1, 0, 1, 2),
+       (60, 'pheasant', 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2),
+       (61, 'pike', 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 4),
+       (62, 'piranha', 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 4),
+       (63, 'pitviper', 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 3),
+       (64, 'platypus', 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (65, 'polecat', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (66, 'pony', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 1, 1, 1),
+       (67, 'porpoise', 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1),
+       (68, 'puma', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (69, 'pussycat', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 1, 1, 1),
+       (70, 'raccoon', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (71, 'reindeer', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 1, 1, 1),
+       (72, 'rhea', 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 2, 1, 0, 1, 2),
+       (73, 'scorpion', 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 8, 1, 0, 0, 7),
+       (74, 'seahorse', 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 4),
+       (75, 'seal', 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1),
+       (76, 'sealion', 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 2, 1, 0, 1, 1),
+       (77, 'seasnake', 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 3),
+       (78, 'seawasp', 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 7),
+       (79, 'skimmer', 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2),
+       (80, 'skua', 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2),
+       (81, 'slowworm', 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 3),
+       (82, 'slug', 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 7),
+       (83, 'sole', 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 4),
+       (84, 'sparrow', 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2),
+       (85, 'squirrel', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 2, 1, 0, 0, 1),
+       (86, 'starfish', 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 0, 0, 0, 7),
+       (87, 'stingray', 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 4),
+       (88, 'swan', 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 1, 0, 1, 2),
+       (89, 'termite', 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 6, 0, 0, 0, 6),
+       (90, 'toad', 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 4, 0, 0, 0, 5),
+       (91, 'tortoise', 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 4, 1, 0, 1, 3),
+       (92, 'tuatara', 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 0, 3),
+       (93, 'tuna', 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 4),
+       (94, 'vampire', 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 2, 1, 0, 0, 1),
+       (95, 'vole', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 4, 1, 0, 0, 1),
+       (96, 'vulture', 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 2, 1, 0, 1, 2),
+       (97, 'wallaby', 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 2, 1, 0, 1, 1),
+       (98, 'wasp', 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 6, 0, 0, 0, 6),
+       (99, 'wolf', 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 4, 1, 0, 1, 1),
+       (100, 'worm', 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 7),
+       (101, 'wren', 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1, 0, 0, 2);
+
+INSERT INTO names (firstname, lastname)
+VALUES ('Moritz', 'Staudinger'),
+       ('Martin', 'Weise'),
+       ('Eva', 'Gergely'),
+       ('Cornelia', 'Michlits'),
+       ('Kirill', 'Stytsenko');
+
+INSERT INTO likes (name_id, zoo_id)
+VALUES (1, 5),
+       (1, 10),
+       (2, 3),
+       (2, 80),
+       (3, 4),
+       (4, 4),
+       (5, 100);
+
+########################################################################################################################
+## TEST CASE PRE-REQUISITE                                                                                            ##
+########################################################################################################################
+
+CREATE VIEW mock_view AS
+(
+SELECT `id`,
+       `animal_name`,
+       `hair`,
+       `feathers`,
+       `eggs`,
+       `milk`,
+       `airborne`,
+       `aquatic`,
+       `predator`,
+       `toothed`,
+       `backbone`,
+       `breathes`,
+       `venomous`,
+       `fins`,
+       `legs`,
+       `tail`,
+       `domestic`,
+       `catsize`,
+       `class_type`
+FROM `zoo`
+WHERE `class_type` = 1);
diff --git a/fda-user-service/service_ready b/fda-user-service/service_ready
new file mode 100644
index 0000000000..b2e4f9df68
--- /dev/null
+++ b/fda-user-service/service_ready
@@ -0,0 +1,6 @@
+#!/bin/bash
+if [ -f /ready ]; then
+  echo "service is ready and accepting connections"
+  exit 0
+fi
+exit 1
\ No newline at end of file
diff --git a/fda-user-service/services/pom.xml b/fda-user-service/services/pom.xml
new file mode 100644
index 0000000000..a42439c741
--- /dev/null
+++ b/fda-user-service/services/pom.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>fda-user-service</artifactId>
+        <groupId>at.tuwien</groupId>
+        <version>1.1.0-alpha</version>
+    </parent>
+
+    <artifactId>services</artifactId>
+    <version>1.1.0-alpha</version>
+    <name>fda-user-service-services</name>
+
+</project>
diff --git a/fda-user-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java b/fda-user-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java
new file mode 100644
index 0000000000..b2b01c42ee
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java
@@ -0,0 +1,100 @@
+package at.tuwien.auth;
+
+import at.tuwien.api.auth.RealmAccessDto;
+import at.tuwien.api.user.UserDetailsDto;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.stream.Collectors;
+
+@Slf4j
+public class AuthTokenFilter extends OncePerRequestFilter {
+
+    @Value("${fda.jwt.issuer}")
+    private String issuer;
+
+    @Value("${fda.jwt.public_key}")
+    private String publicKey;
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+            throws ServletException, IOException {
+        final String jwt = parseJwt(request);
+        if (jwt != null) {
+            final UserDetails userDetails = verifyJwt(jwt);
+            log.debug("authenticated user {}", userDetails);
+            final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
+                    userDetails, null, userDetails.getAuthorities());
+            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+
+            SecurityContextHolder.getContext().setAuthentication(authentication);
+        }
+        filterChain.doFilter(request, response);
+    }
+
+    public UserDetails verifyJwt(String token) throws ServletException {
+        final KeyFactory kf;
+        try {
+            kf = KeyFactory.getInstance("RSA");
+        } catch (NoSuchAlgorithmException e) {
+            log.error("Failed to find RSA algorithm");
+            throw new ServletException("Failed to find RSA algorithm", e);
+        }
+        final X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
+        final RSAPublicKey pubKey;
+        try {
+            pubKey = (RSAPublicKey) kf.generatePublic(keySpecX509);
+        } catch (InvalidKeySpecException e) {
+            log.error("Provided public key is invalid");
+            throw new ServletException("Provided public key is invalid", e);
+        }
+        final Algorithm algorithm = Algorithm.RSA256(pubKey, null);
+        JWTVerifier verifier = JWT.require(algorithm)
+                .withIssuer(issuer)
+                .withAudience("spring")
+                .build();
+        final DecodedJWT jwt = verifier.verify(token);
+        final RealmAccessDto realmAccess = jwt.getClaim("realm_access").as(RealmAccessDto.class);
+        return UserDetailsDto.builder()
+                .username(jwt.getClaim("client_id").asString())
+                .authorities(Arrays.stream(realmAccess.getRoles()).map(SimpleGrantedAuthority::new).collect(Collectors.toList()))
+                .build();
+    }
+
+    /**
+     * Parses the token from the HTTP header of the request
+     *
+     * @param request The request.
+     * @return The token.
+     */
+    public String parseJwt(HttpServletRequest request) {
+        String headerAuth = request.getHeader("Authorization");
+        if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
+            return headerAuth.substring(7, headerAuth.length());
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/fda-user-service/services/src/main/java/at/tuwien/config/JacksonConfig.java b/fda-user-service/services/src/main/java/at/tuwien/config/JacksonConfig.java
new file mode 100644
index 0000000000..fba7f99cf2
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/config/JacksonConfig.java
@@ -0,0 +1,31 @@
+package at.tuwien.config;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+@Slf4j
+@Configuration
+public class JacksonConfig {
+
+    @Bean
+    public ObjectMapper objectMapper() throws JsonProcessingException {
+        final ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.findAndRegisterModules();
+        objectMapper.registerModule(new Jdk8Module());
+        objectMapper.registerModule(new JavaTimeModule());
+        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        objectMapper.setTimeZone(TimeZone.getTimeZone("UTC"));
+        log.debug("current time is {}", objectMapper.writeValueAsString(new Date()));
+        return objectMapper;
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/config/ReadyConfig.java b/fda-user-service/services/src/main/java/at/tuwien/config/ReadyConfig.java
new file mode 100644
index 0000000000..2250fa5088
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/config/ReadyConfig.java
@@ -0,0 +1,25 @@
+package at.tuwien.config;
+
+import com.google.common.io.Files;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.event.EventListener;
+
+import java.io.File;
+import java.io.IOException;
+
+@Log4j2
+@Configuration
+public class ReadyConfig {
+
+    @Value("${fda.ready.path}")
+    private String readyPath;
+
+    @EventListener(ApplicationReadyEvent.class)
+    public void init() throws IOException {
+        Files.touch(new File(readyPath));
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java b/fda-user-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java
new file mode 100644
index 0000000000..0a2af8f294
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java
@@ -0,0 +1,87 @@
+package at.tuwien.config;
+
+import at.tuwien.auth.AuthTokenFilter;
+import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
+import io.swagger.v3.oas.annotations.security.SecurityScheme;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import javax.servlet.http.HttpServletResponse;
+
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+@SecurityScheme(
+        name = "bearerAuth",
+        type = SecuritySchemeType.HTTP,
+        bearerFormat = "JWT",
+        scheme = "bearer"
+)
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Bean
+    public AuthTokenFilter authTokenFilter() {
+        return new AuthTokenFilter();
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        /* enable CORS and disable CSRF */
+        http = http.cors().and().csrf().disable();
+        /* set session management to stateless */
+        http = http
+                .sessionManagement()
+                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+                .and();
+        /* set unauthorized requests exception handler */
+        http = http
+                .exceptionHandling()
+                .authenticationEntryPoint(
+                        (request, response, ex) -> {
+                            response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
+                                    ex.getMessage()
+                            );
+                        }
+                ).and();
+        /* set permissions on endpoints */
+        http.authorizeRequests()
+                /* our internal endpoints */
+                .antMatchers(HttpMethod.GET, "/actuator/prometheus/**").permitAll()
+                /* our public endpoints */
+                .antMatchers(HttpMethod.GET, "/api/user/**").permitAll()
+                .antMatchers(HttpMethod.POST, "/api/user/**").permitAll()
+                .antMatchers("/v3/api-docs.yaml",
+                        "/v3/api-docs/**",
+                        "/swagger-ui/**",
+                        "/swagger-ui.html").permitAll()
+                /* our private endpoints */
+                .anyRequest().authenticated();
+        /* add JWT token filter */
+        http.addFilterBefore(authTokenFilter(),
+                UsernamePasswordAuthenticationFilter.class
+        );
+    }
+
+    @Bean
+    public CorsFilter corsFilter() {
+        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        final CorsConfiguration config = new CorsConfiguration();
+        config.setAllowCredentials(true);
+        config.addAllowedOrigin("*");
+        config.addAllowedHeader("*");
+        config.addAllowedMethod("*");
+        source.registerCorsConfiguration("/**", config);
+        return new CorsFilter(source);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/AmqpException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/AmqpException.java
new file mode 100644
index 0000000000..6af0750d6f
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/AmqpException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.NOT_ACCEPTABLE)
+public class AmqpException extends Exception {
+
+    public AmqpException(String msg) {
+        super(msg);
+    }
+
+    public AmqpException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public AmqpException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/ColumnParseException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/ColumnParseException.java
new file mode 100644
index 0000000000..c0c1e109de
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/ColumnParseException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.EXPECTATION_FAILED)
+public class ColumnParseException extends Exception {
+
+    public ColumnParseException(String msg) {
+        super(msg);
+    }
+
+    public ColumnParseException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public ColumnParseException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/ContainerNotFoundException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/ContainerNotFoundException.java
new file mode 100644
index 0000000000..85d49d4cb3
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/ContainerNotFoundException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.NOT_FOUND)
+public class ContainerNotFoundException extends Exception {
+
+    public ContainerNotFoundException(String msg) {
+        super(msg);
+    }
+
+    public ContainerNotFoundException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public ContainerNotFoundException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/DatabaseConnectionException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/DatabaseConnectionException.java
new file mode 100644
index 0000000000..3c1f797647
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/DatabaseConnectionException.java
@@ -0,0 +1,23 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import java.io.IOException;
+
+@ResponseStatus(code = HttpStatus.METHOD_NOT_ALLOWED)
+public class DatabaseConnectionException extends Exception {
+
+    public DatabaseConnectionException(String msg) {
+        super(msg);
+    }
+
+    public DatabaseConnectionException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public DatabaseConnectionException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/DatabaseNotFoundException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/DatabaseNotFoundException.java
new file mode 100644
index 0000000000..b9ca79c783
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/DatabaseNotFoundException.java
@@ -0,0 +1,23 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import java.io.IOException;
+
+@ResponseStatus(code = HttpStatus.NOT_FOUND)
+public class DatabaseNotFoundException extends Exception {
+
+    public DatabaseNotFoundException(String msg) {
+        super(msg);
+    }
+
+    public DatabaseNotFoundException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public DatabaseNotFoundException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/FileStorageException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/FileStorageException.java
new file mode 100644
index 0000000000..ab068e4245
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/FileStorageException.java
@@ -0,0 +1,20 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.BAD_REQUEST)
+public class FileStorageException extends Exception {
+
+    public FileStorageException(String msg) {
+        super(msg);
+    }
+
+    public FileStorageException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public FileStorageException(Throwable thr) {
+        super(thr);
+    }
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/IdentifierNotFoundException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/IdentifierNotFoundException.java
new file mode 100644
index 0000000000..f0bb71f364
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/IdentifierNotFoundException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.NOT_FOUND)
+public class IdentifierNotFoundException extends Exception {
+
+    public IdentifierNotFoundException(String msg) {
+        super(msg);
+    }
+
+    public IdentifierNotFoundException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public IdentifierNotFoundException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/ImageNotSupportedException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/ImageNotSupportedException.java
new file mode 100644
index 0000000000..70963128f8
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/ImageNotSupportedException.java
@@ -0,0 +1,23 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import java.io.IOException;
+
+@ResponseStatus(code = HttpStatus.CONFLICT)
+public class ImageNotSupportedException extends Exception {
+
+    public ImageNotSupportedException(String msg) {
+        super(msg);
+    }
+
+    public ImageNotSupportedException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public ImageNotSupportedException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/NotAllowedException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/NotAllowedException.java
new file mode 100644
index 0000000000..44c3d430f9
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/NotAllowedException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.METHOD_NOT_ALLOWED)
+public class NotAllowedException extends Exception {
+
+    public NotAllowedException(String msg) {
+        super(msg);
+    }
+
+    public NotAllowedException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public NotAllowedException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/PaginationException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/PaginationException.java
new file mode 100644
index 0000000000..9d56aec9c2
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/PaginationException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.BAD_REQUEST)
+public class PaginationException extends Exception {
+
+    public PaginationException(String msg) {
+        super(msg);
+    }
+
+    public PaginationException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public PaginationException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/QueryAlreadyPersistedException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/QueryAlreadyPersistedException.java
new file mode 100644
index 0000000000..48d3bb0ad9
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/QueryAlreadyPersistedException.java
@@ -0,0 +1,19 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.CONFLICT)
+public class QueryAlreadyPersistedException extends Exception {
+
+    public QueryAlreadyPersistedException(String msg) {
+        super(msg);
+    }
+
+    public QueryAlreadyPersistedException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public QueryAlreadyPersistedException(Throwable thr) { super(thr);
+    }
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/QueryMalformedException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/QueryMalformedException.java
new file mode 100644
index 0000000000..3d81b6ba4e
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/QueryMalformedException.java
@@ -0,0 +1,23 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import java.io.IOException;
+
+@ResponseStatus(code = HttpStatus.BAD_REQUEST)
+public class QueryMalformedException extends Exception {
+
+    public QueryMalformedException(String msg) {
+        super(msg);
+    }
+
+    public QueryMalformedException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public QueryMalformedException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/QueryNotFoundException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/QueryNotFoundException.java
new file mode 100644
index 0000000000..a5e9075489
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/QueryNotFoundException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.NOT_FOUND)
+public class QueryNotFoundException extends Exception {
+
+    public QueryNotFoundException(String msg) {
+        super(msg);
+    }
+
+    public QueryNotFoundException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public QueryNotFoundException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/QueryStoreException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/QueryStoreException.java
new file mode 100644
index 0000000000..b1f472f2a1
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/QueryStoreException.java
@@ -0,0 +1,19 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.GATEWAY_TIMEOUT)
+public class QueryStoreException  extends Exception {
+
+    public QueryStoreException(String msg) {
+        super(msg);
+    }
+
+    public QueryStoreException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public QueryStoreException(Throwable thr) { super(thr);
+    }
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/SortException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/SortException.java
new file mode 100644
index 0000000000..7415590ad6
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/SortException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.BAD_REQUEST)
+public class SortException extends Exception {
+
+    public SortException(String msg) {
+        super(msg);
+    }
+
+    public SortException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public SortException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/TableMalformedException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/TableMalformedException.java
new file mode 100644
index 0000000000..542c789ad5
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/TableMalformedException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.LOCKED)
+public class TableMalformedException extends Exception {
+
+    public TableMalformedException(String msg) {
+        super(msg);
+    }
+
+    public TableMalformedException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public TableMalformedException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/TableNotFoundException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/TableNotFoundException.java
new file mode 100644
index 0000000000..89fa3ed467
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/TableNotFoundException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.NOT_FOUND)
+public class TableNotFoundException extends Exception {
+
+    public TableNotFoundException(String msg) {
+        super(msg);
+    }
+
+    public TableNotFoundException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public TableNotFoundException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/TupleDeleteException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/TupleDeleteException.java
new file mode 100644
index 0000000000..87e5e4a483
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/TupleDeleteException.java
@@ -0,0 +1,19 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.CONFLICT)
+public class TupleDeleteException extends Exception {
+
+    public TupleDeleteException(String msg) {
+        super(msg);
+    }
+
+    public TupleDeleteException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public TupleDeleteException(Throwable thr) { super(thr);
+    }
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/UserNotFoundException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/UserNotFoundException.java
new file mode 100644
index 0000000000..0abb87f609
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/UserNotFoundException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "User not found")
+public class UserNotFoundException extends Exception {
+
+    public UserNotFoundException(String message) {
+        super(message);
+    }
+
+    public UserNotFoundException(String message, Throwable thr) {
+        super(message, thr);
+    }
+
+    public UserNotFoundException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/ViewMalformedException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/ViewMalformedException.java
new file mode 100644
index 0000000000..5dfdaf170e
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/ViewMalformedException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.LOCKED)
+public class ViewMalformedException extends Exception {
+
+    public ViewMalformedException(String msg) {
+        super(msg);
+    }
+
+    public ViewMalformedException(String msg, Throwable thr) {
+        super(msg, thr);
+    }
+
+    public ViewMalformedException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/ViewNotFoundException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/ViewNotFoundException.java
new file mode 100644
index 0000000000..2f260975ff
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/exception/ViewNotFoundException.java
@@ -0,0 +1,21 @@
+package at.tuwien.exception;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "View not found")
+public class ViewNotFoundException extends Exception {
+
+    public ViewNotFoundException(String message) {
+        super(message);
+    }
+
+    public ViewNotFoundException(String message, Throwable thr) {
+        super(message, thr);
+    }
+
+    public ViewNotFoundException(Throwable thr) {
+        super(thr);
+    }
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java b/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java
new file mode 100644
index 0000000000..cba134ae89
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java
@@ -0,0 +1,31 @@
+package at.tuwien.mapper;
+
+import at.tuwien.api.auth.SignupRequestDto;
+import at.tuwien.api.user.GrantedAuthorityDto;
+import at.tuwien.api.user.UserBriefDto;
+import at.tuwien.api.user.UserDetailsDto;
+import at.tuwien.api.user.UserDto;
+import at.tuwien.entities.user.User;
+import org.mapstruct.Mapper;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+
+@Mapper(componentModel = "spring")
+public interface UserMapper {
+
+    org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(UserMapper.class);
+
+    UserDetailsDto userDtoToUserDetailsDto(UserDto data);
+
+    UserDto userToUserDto(User data);
+
+    UserBriefDto userToUserBriefDto(User data);
+
+    User signupRequestDtoToUser(SignupRequestDto data);
+
+    default GrantedAuthority grantedAuthorityDtoToGrantedAuthority(GrantedAuthorityDto data) {
+        final GrantedAuthority authority = new SimpleGrantedAuthority(data.getAuthority());
+        log.trace("mapped granted authority {} to granted authority {}", data, authority);
+        return authority;
+    }
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/UserRepository.java b/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/UserRepository.java
new file mode 100644
index 0000000000..e77de8f6a8
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/UserRepository.java
@@ -0,0 +1,14 @@
+package at.tuwien.repository.jpa;
+
+import at.tuwien.entities.user.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+public interface UserRepository extends JpaRepository<User, String> {
+
+    Optional<User> findByUsername(String username);
+
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java b/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java
new file mode 100644
index 0000000000..d9371a4695
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java
@@ -0,0 +1,39 @@
+package at.tuwien.service;
+
+import at.tuwien.api.auth.SignupRequestDto;
+import at.tuwien.entities.container.Container;
+import at.tuwien.entities.user.User;
+import at.tuwien.exception.UserNotFoundException;
+
+import java.security.Principal;
+import java.util.List;
+
+public interface UserService {
+
+    /**
+     * Finds all users
+     *
+     * @return The list of users.
+     */
+    List<User> findAll();
+
+    /**
+     * Finds a user by username.
+     *
+     * @param username The username.
+     * @return The user.
+     * @throws UserNotFoundException The user was not found in the metadata database.
+     */
+    User findByUsername(String username) throws UserNotFoundException;
+
+    User create(SignupRequestDto data);
+
+    /**
+     * Finds a user by id.
+     *
+     * @param id The id.
+     * @return The user.
+     * @throws UserNotFoundException The user was not found in the metadata database.
+     */
+    User find(String id) throws UserNotFoundException;
+}
diff --git a/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java b/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java
new file mode 100644
index 0000000000..5578d9186b
--- /dev/null
+++ b/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java
@@ -0,0 +1,66 @@
+package at.tuwien.service.impl;
+
+import at.tuwien.api.auth.SignupRequestDto;
+import at.tuwien.entities.user.User;
+import at.tuwien.exception.UserNotFoundException;
+import at.tuwien.mapper.UserMapper;
+import at.tuwien.repository.jpa.UserRepository;
+import at.tuwien.service.UserService;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+@Log4j2
+@Service
+public class UserServiceImpl implements UserService {
+
+    private final UserMapper userMapper;
+    private final UserRepository userRepository;
+
+    @Autowired
+    public UserServiceImpl(UserMapper userMapper, UserRepository userRepository) {
+        this.userMapper = userMapper;
+        this.userRepository = userRepository;
+    }
+
+    @Override
+    public List<User> findAll() {
+        return userRepository.findAll();
+    }
+
+    @Override
+    public User findByUsername(String username) throws UserNotFoundException {
+        final Optional<User> optional = userRepository.findByUsername(username);
+        if (optional.isEmpty()) {
+            log.error("Failed to retrieve user with username {}", username);
+            throw new UserNotFoundException("Failed to retrieve user");
+        }
+        return optional.get();
+    }
+
+    @Override
+    public User create(SignupRequestDto data) {
+        final User user = userMapper.signupRequestDtoToUser(data);
+        user.setRealmId("82c39861-d877-4667-a0f3-4daa2ee230e0");
+        user.setEmailVerified(false);
+        user.setId(UUID.randomUUID().toString());
+        final User entity = userRepository.save(user);
+        log.info("Created user with id {}", entity.getId());
+        return entity;
+    }
+
+    @Override
+    public User find(String id) throws UserNotFoundException {
+        final Optional<User> optional = userRepository.findById(id);
+        if (optional.isEmpty()) {
+            log.error("Failed to retrieve user with id {}", id);
+            throw new UserNotFoundException("Failed to retrieve user");
+        }
+        return optional.get();
+    }
+
+}
diff --git a/fda-user-service/services/src/test/resources/application.properties b/fda-user-service/services/src/test/resources/application.properties
new file mode 100644
index 0000000000..69df4a3123
--- /dev/null
+++ b/fda-user-service/services/src/test/resources/application.properties
@@ -0,0 +1,14 @@
+# disable discovery
+spring.cloud.discovery.enabled = false
+
+# disable cloud config and config discovery
+spring.cloud.config.discovery.enabled = false
+spring.cloud.config.enabled = false
+
+# disable datasource
+spring.datasource.url=jdbc:h2:mem:testdb
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=password
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+spring.jpa.hibernate.ddl-auto=update
\ No newline at end of file
-- 
GitLab