commit 8671ef71b6bdd7dea5210afe4144bcac1b8635c3 Author: Guillaume Dugas Date: Tue Sep 9 10:00:01 2025 +0200 Project init diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..94810d0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/* \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..cc8a653 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,25 @@ +name: Maven build +run-name: Build application with maven +on: [ push ] + +jobs: + build: + runs-on: ubuntu-latest + env: + MAVEN_ACCESS_TOKEN: ${{ secrets.CI_TOKEN }} + MAVEN_REPO_OWNER: ${{ gitea.actor }} + steps: + - name: Check out latest repository code + uses: actions/checkout@v4 + + - uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Build project + run: ./mvnw -s .mvn/settings.xml clean install -DskipTests + + - name: Deploy project + run: ./mvnw -s .mvn/settings.xml deploy -DskipTests + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..91a800a --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties +.flattened-pom.xml + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env + +# Plugin directory +/.quarkus/cli/plugins/ +# TLS Certificates +.certs/ diff --git a/.mvn/settings.xml b/.mvn/settings.xml new file mode 100644 index 0000000..984d060 --- /dev/null +++ b/.mvn/settings.xml @@ -0,0 +1,19 @@ + + + + gitea + + + + Authorization + token ${env.MAVEN_ACCESS_TOKEN} + + + + + + + diff --git a/.mvn/wrapper/.gitignore b/.mvn/wrapper/.gitignore new file mode 100644 index 0000000..e72f5e8 --- /dev/null +++ b/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..0636a7b --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,93 @@ +/* + * 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 + * + * http://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.io.IOException; +import java.io.InputStream; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.concurrent.ThreadLocalRandom; + +public final class MavenWrapperDownloader { + private static final String WRAPPER_VERSION = "3.3.2"; + + private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); + + public static void main(String[] args) { + log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION); + + if (args.length != 2) { + System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing"); + System.exit(1); + } + + try { + log(" - Downloader started"); + final URL wrapperUrl = URI.create(args[0]).toURL(); + final String jarPath = args[1].replace("..", ""); // Sanitize path + final Path wrapperJarPath = Paths.get(jarPath).toAbsolutePath().normalize(); + downloadFileFromURL(wrapperUrl, wrapperJarPath); + log("Done"); + } catch (IOException e) { + System.err.println("- Error downloading: " + e.getMessage()); + if (VERBOSE) { + e.printStackTrace(); + } + System.exit(1); + } + } + + private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath) + throws IOException { + log(" - Downloading to: " + wrapperJarPath); + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + final String username = System.getenv("MVNW_USERNAME"); + final char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + Path temp = wrapperJarPath + .getParent() + .resolve(wrapperJarPath.getFileName() + "." + + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp"); + try (InputStream inStream = wrapperUrl.openStream()) { + Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING); + Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING); + } finally { + Files.deleteIfExists(temp); + } + log(" - Downloader complete"); + } + + private static void log(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + +} diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..1a580be --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,20 @@ +# 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 +# +# http://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. +wrapperVersion=3.3.2 +distributionType=source +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..eabb165 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# semrack + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: . + +## Quickstart + +### Dev mode local execution + +```shell script +./mvnw quarkus:dev +``` + +### Build project + +```shell script +./mvnw clean install +``` + +## Configuration + +### Storage Postgres + +The storage-postgres module use the quarkus jdbc-postgresql extension. + +In dev mode, the database devservices is used. To configure database in production, you need to set the quarkus jdbc parameters : + + quarkus.datasource.jdbc.url=jdbc:postgresql://${semrack.db.host}:${semrack.db.port}/${semrack.db.name} + quarkus.datasource.reactive.url=postgresql://${semrack.db.host}:${semrack.db.port}/${semrack.db.name} + quarkus.datasource.username=${semrack.db.user} + quarkus.datasource.password=${semrack.db.password} + + diff --git a/docs/semrack-core.md b/docs/semrack-core.md new file mode 100644 index 0000000..16ec4e6 --- /dev/null +++ b/docs/semrack-core.md @@ -0,0 +1,27 @@ +# Semrack Core + +Fourni les entités / fonctionalités suivantes : + +- Semdoc : l'entité document, la base de travail +- Storage : backend de récupération / stockage des documents +- Repository : s'interface en amont du storage. Active les fonctionnalité d'interceptions de lecture, d'écriture et recherche +- ReadInterceptor : interception du document en lecture +- WriteInterceptor : interception du document en écriture +- SearchInterceptor : interception des filtres de recherche + +## Semdoc + +- uid : identifiant unique dans le rack +- annotations: écrites uniquement par les intercepteurs +- métadonnées: écrite par l'utilisateur, peuvent être altérées par les intercepteurs (validation, securité, etc.) +- directives: lors d'un push, renseigne les intercepteurs sur les actions à mener pour l'écriture du document (versioning, annotations, etc.) + +## Directives + +### Snapshot +La directive snapshot permet de créer une capture lors du push du document précédent + + directives: + snapshot: true | false + publish: true | false + diff --git a/integration/semrack-integration/docker-compose.yml b/integration/semrack-integration/docker-compose.yml new file mode 100644 index 0000000..7865b39 --- /dev/null +++ b/integration/semrack-integration/docker-compose.yml @@ -0,0 +1,24 @@ +services: + postgres: + image: postgres + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: quarkus + ports: + - "35532:5432" + + semrack: + build: + context: . + dockerfile: src/main/docker/Dockerfile.native-micro + environment: + QUARKUS_FLYWAY_ACTIVE: true + ports: + - "9090:8080" + links: + - postgres + volumes: + - ./.env:/work/.env + - ./src/main/resources/application.properties:/work/config/application.properties + + diff --git a/integration/semrack-integration/pom.xml b/integration/semrack-integration/pom.xml new file mode 100644 index 0000000..0c8cf51 --- /dev/null +++ b/integration/semrack-integration/pom.xml @@ -0,0 +1,136 @@ + + + 4.0.0 + + fr.codeanddata.semrack + 1.0-SNAPSHOT + semrack-parent + ../../pom.xml + + semrack-integration + + + + fr.codeanddata.semrack + semrack-core + + + fr.codeanddata.semrack + semrack-storage-postgres + + + fr.codeanddata.semrack + semrack-api-rest + + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + commons-codec + commons-codec + + + org.projectlombok + lombok + + + org.mapstruct + mapstruct + + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + native-image-agent + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + true + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + native + + + + false + true + + + + diff --git a/integration/semrack-integration/src/main/docker/Dockerfile.jvm b/integration/semrack-integration/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..642a2f2 --- /dev/null +++ b/integration/semrack-integration/src/main/docker/Dockerfile.jvm @@ -0,0 +1,98 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/semrack-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/semrack-jvm +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/semrack-jvm +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override +# the default JVM options, use `JAVA_OPTS_APPEND` to append options +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi9/openjdk-21:1.21 + +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] + diff --git a/integration/semrack-integration/src/main/docker/Dockerfile.legacy-jar b/integration/semrack-integration/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 0000000..c7196af --- /dev/null +++ b/integration/semrack-integration/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,94 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.jar.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/semrack-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/semrack-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/semrack-legacy-jar +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override +# the default JVM options, use `JAVA_OPTS_APPEND` to append options +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi9/openjdk-21:1.21 + +ENV LANGUAGE='en_US:en' + + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/quarkus-run.jar + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] diff --git a/integration/semrack-integration/src/main/docker/Dockerfile.native b/integration/semrack-integration/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..69d2b69 --- /dev/null +++ b/integration/semrack-integration/src/main/docker/Dockerfile.native @@ -0,0 +1,29 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/semrack . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/semrack +# +# The ` registry.access.redhat.com/ubi9/ubi-minimal:9.5` base image is based on UBI 9. +# To use UBI 8, switch to `quay.io/ubi8/ubi-minimal:8.10`. +### +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/integration/semrack-integration/src/main/docker/Dockerfile.native-micro b/integration/semrack-integration/src/main/docker/Dockerfile.native-micro new file mode 100644 index 0000000..a4b708f --- /dev/null +++ b/integration/semrack-integration/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,32 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# It uses a micro base image, tuned for Quarkus native executables. +# It reduces the size of the resulting container image. +# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/semrack . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/semrack +# +# The `quay.io/quarkus/ubi9-quarkus-micro-image:2.0` base image is based on UBI 9. +# To use UBI 8, switch to `quay.io/quarkus/quarkus-micro-image:2.0`. +### +FROM quay.io/quarkus/ubi9-quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/integration/semrack-integration/src/main/resources/application.properties b/integration/semrack-integration/src/main/resources/application.properties new file mode 100644 index 0000000..5ce65a2 --- /dev/null +++ b/integration/semrack-integration/src/main/resources/application.properties @@ -0,0 +1,24 @@ +%dev.quarkus.log.min-level=TRACE + +quarkus.hibernate-orm.mapping.format.global=ignore + +# Datasource +quarkus.datasource.devservices.port=35432 +quarkus.hibernate-orm.scripts.generation=none +%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://${semrack.db.host}:${semrack.db.port}/${semrack.db.name} +%prod.quarkus.datasource.reactive.url=postgresql://${semrack.db.host}:${semrack.db.port}/${semrack.db.name} +%prod.quarkus.datasource.username=${semrack.db.user} +%prod.quarkus.datasource.password=${semrack.db.password} + +## Flyway +quarkus.flyway.active=false + +# Security +quarkus.keycloak.devservices.port=35180 +quarkus.oidc.client-id=m2m-client +quarkus.oidc.credentials.secret=m2m-password +quarkus.oidc.discovery-enabled=true +%prod.quarkus.oidc.auth-server-url=${semrack.oidc.url} +%prod.quarkus.oidc.client-id=${semrack.oidc.client-id} +%prod.quarkus.oidc.credentials.secret=${semrack.oidc.client-secret} + diff --git a/integration/semrack-integration/src/test/java/fr/codeanddata/SemrackApiIT.java b/integration/semrack-integration/src/test/java/fr/codeanddata/SemrackApiIT.java new file mode 100644 index 0000000..6f0a011 --- /dev/null +++ b/integration/semrack-integration/src/test/java/fr/codeanddata/SemrackApiIT.java @@ -0,0 +1,8 @@ +package fr.codeanddata; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +class SemrackApiIT extends SemrackApiTest { + // Execute the same tests but in packaged mode. +} diff --git a/integration/semrack-integration/src/test/java/fr/codeanddata/SemrackApiTest.java b/integration/semrack-integration/src/test/java/fr/codeanddata/SemrackApiTest.java new file mode 100644 index 0000000..53be737 --- /dev/null +++ b/integration/semrack-integration/src/test/java/fr/codeanddata/SemrackApiTest.java @@ -0,0 +1,20 @@ +package fr.codeanddata; + +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusTest +class SemrackApiTest { + @Test + void testHelloEndpoint() { + given() + .when().get("/hello") + .then() + .statusCode(200) + .body(is("Hello from Quarkus REST")); + } + +} diff --git a/modules/semrack-api-rest/deployment/pom.xml b/modules/semrack-api-rest/deployment/pom.xml new file mode 100644 index 0000000..a0e0bff --- /dev/null +++ b/modules/semrack-api-rest/deployment/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-api-rest-parent + 1.0-SNAPSHOT + + semrack-api-rest-deployment + Semrack Api Rest - Deployment + + + + fr.codeanddata.semrack + semrack-core-deployment + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-rest-deployment + + + io.quarkus + quarkus-rest-jackson-deployment + + + io.quarkus + quarkus-smallrye-openapi-deployment + + + + + + + fr.codeanddata.semrack + semrack-api-rest + ${project.version} + + + io.quarkus + quarkus-junit5-internal + test + + + + + + + maven-compiler-plugin + + + default-compile + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + + diff --git a/modules/semrack-api-rest/deployment/src/main/java/fr/codeanddata/semrack/api/rest/deployment/SemrackApiRestProcessor.java b/modules/semrack-api-rest/deployment/src/main/java/fr/codeanddata/semrack/api/rest/deployment/SemrackApiRestProcessor.java new file mode 100644 index 0000000..bcb7d13 --- /dev/null +++ b/modules/semrack-api-rest/deployment/src/main/java/fr/codeanddata/semrack/api/rest/deployment/SemrackApiRestProcessor.java @@ -0,0 +1,14 @@ +package fr.codeanddata.semrack.api.rest.deployment; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +class SemrackApiRestProcessor { + + private static final String FEATURE = "semrack-api-rest"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); + } +} diff --git a/modules/semrack-api-rest/deployment/src/test/java/fr/codeanddata/semrack/api/rest/test/SemrackApiRestDevModeTest.java b/modules/semrack-api-rest/deployment/src/test/java/fr/codeanddata/semrack/api/rest/test/SemrackApiRestDevModeTest.java new file mode 100644 index 0000000..7f064d7 --- /dev/null +++ b/modules/semrack-api-rest/deployment/src/test/java/fr/codeanddata/semrack/api/rest/test/SemrackApiRestDevModeTest.java @@ -0,0 +1,23 @@ +package fr.codeanddata.semrack.api.rest.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; + +public class SemrackApiRestDevModeTest { + + // Start hot reload (DevMode) test with your extension loaded + @RegisterExtension + static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnDevModeTest() { + // Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information + Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName()); + } +} diff --git a/modules/semrack-api-rest/deployment/src/test/java/fr/codeanddata/semrack/api/rest/test/SemrackApiRestTest.java b/modules/semrack-api-rest/deployment/src/test/java/fr/codeanddata/semrack/api/rest/test/SemrackApiRestTest.java new file mode 100644 index 0000000..1376b8c --- /dev/null +++ b/modules/semrack-api-rest/deployment/src/test/java/fr/codeanddata/semrack/api/rest/test/SemrackApiRestTest.java @@ -0,0 +1,23 @@ +package fr.codeanddata.semrack.api.rest.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class SemrackApiRestTest { + + // Start unit test with your extension loaded + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnUnitTest() { + // Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information + Assertions.assertTrue(true, "Add some assertions to " + getClass().getName()); + } +} diff --git a/modules/semrack-api-rest/integration-tests/pom.xml b/modules/semrack-api-rest/integration-tests/pom.xml new file mode 100644 index 0000000..b5e0e4a --- /dev/null +++ b/modules/semrack-api-rest/integration-tests/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-api-rest-parent + 1.0.0-SNAPSHOT + + semrack-api-rest-integration-tests + Semrack Api Rest - Integration Tests + + + true + + + + + io.quarkus + quarkus-rest + + + fr.codeanddata.semrack + semrack-api-rest + ${project.version} + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + true + + + + diff --git a/modules/semrack-api-rest/integration-tests/src/main/java/fr/codeanddata/semrack/api/rest/it/SemrackApiRestResource.java b/modules/semrack-api-rest/integration-tests/src/main/java/fr/codeanddata/semrack/api/rest/it/SemrackApiRestResource.java new file mode 100644 index 0000000..8dd21a8 --- /dev/null +++ b/modules/semrack-api-rest/integration-tests/src/main/java/fr/codeanddata/semrack/api/rest/it/SemrackApiRestResource.java @@ -0,0 +1,32 @@ +/* +* 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 +* +* http://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. +*/ +package fr.codeanddata.semrack.api.rest.it; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("/semrack-api-rest") +@ApplicationScoped +public class SemrackApiRestResource { + // add some rest methods here + + @GET + public String hello() { + return "Hello semrack-api-rest"; + } +} diff --git a/modules/semrack-api-rest/integration-tests/src/main/resources/application.properties b/modules/semrack-api-rest/integration-tests/src/main/resources/application.properties new file mode 100644 index 0000000..e69de29 diff --git a/modules/semrack-api-rest/integration-tests/src/test/java/fr/codeanddata/semrack/api/rest/it/SemrackApiRestResourceIT.java b/modules/semrack-api-rest/integration-tests/src/test/java/fr/codeanddata/semrack/api/rest/it/SemrackApiRestResourceIT.java new file mode 100644 index 0000000..1952b9a --- /dev/null +++ b/modules/semrack-api-rest/integration-tests/src/test/java/fr/codeanddata/semrack/api/rest/it/SemrackApiRestResourceIT.java @@ -0,0 +1,7 @@ +package fr.codeanddata.semrack.api.rest.it; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class SemrackApiRestResourceIT extends SemrackApiRestResourceTest { +} diff --git a/modules/semrack-api-rest/integration-tests/src/test/java/fr/codeanddata/semrack/api/rest/it/SemrackApiRestResourceTest.java b/modules/semrack-api-rest/integration-tests/src/test/java/fr/codeanddata/semrack/api/rest/it/SemrackApiRestResourceTest.java new file mode 100644 index 0000000..4f36f21 --- /dev/null +++ b/modules/semrack-api-rest/integration-tests/src/test/java/fr/codeanddata/semrack/api/rest/it/SemrackApiRestResourceTest.java @@ -0,0 +1,21 @@ +package fr.codeanddata.semrack.api.rest.it; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class SemrackApiRestResourceTest { + + @Test + public void testHelloEndpoint() { + given() + .when().get("/semrack-api-rest") + .then() + .statusCode(200) + .body(is("Hello semrack-api-rest")); + } +} diff --git a/modules/semrack-api-rest/pom.xml b/modules/semrack-api-rest/pom.xml new file mode 100644 index 0000000..a34fae5 --- /dev/null +++ b/modules/semrack-api-rest/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + fr.codeanddata.semrack + semrack-parent + 1.0-SNAPSHOT + ../../pom.xml + + semrack-api-rest-parent + pom + Semrack Api Rest - Parent + + + deployment + runtime + + diff --git a/modules/semrack-api-rest/runtime/pom.xml b/modules/semrack-api-rest/runtime/pom.xml new file mode 100644 index 0000000..171b21c --- /dev/null +++ b/modules/semrack-api-rest/runtime/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-api-rest-parent + 1.0-SNAPSHOT + + semrack-api-rest + Semrack Api Rest - Runtime + + + + fr.codeanddata.semrack + semrack-core + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-rest + + + io.quarkus + quarkus-rest-jackson + + + io.quarkus + quarkus-smallrye-openapi + + + + + + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + + + + maven-compiler-plugin + + + default-compile + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + + diff --git a/modules/semrack-api-rest/runtime/src/main/java/fr/codeanddata/semrack/api/rest/SemrackApi.java b/modules/semrack-api-rest/runtime/src/main/java/fr/codeanddata/semrack/api/rest/SemrackApi.java new file mode 100644 index 0000000..5b1aeff --- /dev/null +++ b/modules/semrack-api-rest/runtime/src/main/java/fr/codeanddata/semrack/api/rest/SemrackApi.java @@ -0,0 +1,54 @@ +package fr.codeanddata.semrack.api.rest; + +import fr.codeanddata.semrack.core.SemdocStorage; +import fr.codeanddata.semrack.core.models.SemrackDocument; +import fr.codeanddata.semrack.core.models.PushDocumentRequest; +import fr.codeanddata.semrack.core.models.SearchRequest; +import fr.codeanddata.semrack.core.models.SearchResult; +import fr.codeanddata.semrack.core.repositories.SemdocRepository; +import io.smallrye.mutiny.Uni; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.jboss.resteasy.reactive.RestPath; + +@Path("/semrack/documents") +//@Authenticated +public class SemrackApi { + + @Inject + SemdocStorage storage; + + @Inject + SemdocRepository repository; + + @POST + @Produces(MediaType.APPLICATION_JSON) + public Uni pushDocument(PushDocumentRequest semrackDocument) { + return repository.pushDocument(semrackDocument); + } + + @POST + @Path("/search") + @Produces(MediaType.APPLICATION_JSON) + public Uni searchDocument(SearchRequest query) { + return storage.searchDocument(query); + } + + @GET + @Path("{uid}") + @Produces(MediaType.APPLICATION_JSON) + public Uni getDocument(@RestPath String uid) { + return storage.readDocument(uid); + } + + @POST + @Path("{uid}") + @Produces(MediaType.APPLICATION_JSON) + public Uni updateDocument(@RestPath String uid, PushDocumentRequest semrackDocument) { + return repository.pushDocument(semrackDocument); + } +} diff --git a/modules/semrack-api-rest/runtime/src/main/resources/META-INF/beans.xml b/modules/semrack-api-rest/runtime/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..e69de29 diff --git a/modules/semrack-api-rest/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/modules/semrack-api-rest/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000..a4aa5e5 --- /dev/null +++ b/modules/semrack-api-rest/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +name: Semrack Api Rest +#description: Do something useful. +metadata: +# keywords: +# - semrack-api-rest +# guide: ... # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension +# categories: +# - "miscellaneous" +# status: "preview" diff --git a/modules/semrack-core/deployment/pom.xml b/modules/semrack-core/deployment/pom.xml new file mode 100644 index 0000000..e48bc1c --- /dev/null +++ b/modules/semrack-core/deployment/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-core-parent + 1.0-SNAPSHOT + + semrack-core-deployment + Semrack Core - Deployment + + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-security-deployment + + + io.quarkus + quarkus-jackson-deployment + + + fr.codeanddata.semrack + semrack-core + ${project.version} + + + io.quarkus + quarkus-junit5-internal + test + + + + + + + maven-compiler-plugin + + + default-compile + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + + diff --git a/modules/semrack-core/deployment/src/main/java/fr/codeanddata/semrack/core/deployment/SemrackCoreProcessor.java b/modules/semrack-core/deployment/src/main/java/fr/codeanddata/semrack/core/deployment/SemrackCoreProcessor.java new file mode 100644 index 0000000..6f7ddc3 --- /dev/null +++ b/modules/semrack-core/deployment/src/main/java/fr/codeanddata/semrack/core/deployment/SemrackCoreProcessor.java @@ -0,0 +1,14 @@ +package fr.codeanddata.semrack.core.deployment; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +class SemrackCoreProcessor { + + private static final String FEATURE = "semrack-core"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); + } +} diff --git a/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/MockedStorageImpl.java b/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/MockedStorageImpl.java new file mode 100644 index 0000000..3f85e40 --- /dev/null +++ b/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/MockedStorageImpl.java @@ -0,0 +1,36 @@ +package fr.codeanddata.semrack.core.test; + +import fr.codeanddata.semrack.core.SemdocStorage; +import fr.codeanddata.semrack.core.models.SearchRequest; +import fr.codeanddata.semrack.core.models.SearchResult; +import fr.codeanddata.semrack.core.models.SemrackDocument; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class MockedStorageImpl implements SemdocStorage { + @Override + public Uni readDocument(String uid) { + return null; + } + + @Override + public Uni searchDocument(SearchRequest query) { + return null; + } + + @Override + public Uni countDocuments(SearchRequest request) { + return null; + } + + @Override + public Uni documentsExist(SearchRequest query) { + return null; + } + + @Override + public Uni storeDocument(SemrackDocument document) { + return null; + } +} diff --git a/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/SemrackCoreDevModeTest.java b/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/SemrackCoreDevModeTest.java new file mode 100644 index 0000000..224b2a3 --- /dev/null +++ b/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/SemrackCoreDevModeTest.java @@ -0,0 +1,23 @@ +package fr.codeanddata.semrack.core.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; + +public class SemrackCoreDevModeTest { + + // Start hot reload (DevMode) test with your extension loaded + @RegisterExtension + static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnDevModeTest() { + // Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information + Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName()); + } +} diff --git a/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/SemrackCoreTest.java b/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/SemrackCoreTest.java new file mode 100644 index 0000000..e520339 --- /dev/null +++ b/modules/semrack-core/deployment/src/test/java/fr/codeanddata/semrack/core/test/SemrackCoreTest.java @@ -0,0 +1,23 @@ +package fr.codeanddata.semrack.core.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class SemrackCoreTest { + + // Start unit test with your extension loaded + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnUnitTest() { + // Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information + Assertions.assertTrue(true, "Add some assertions to " + getClass().getName()); + } +} diff --git a/modules/semrack-core/integration-tests/pom.xml b/modules/semrack-core/integration-tests/pom.xml new file mode 100644 index 0000000..77582c7 --- /dev/null +++ b/modules/semrack-core/integration-tests/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-core-parent + 1.0.0-SNAPSHOT + + semrack-core-integration-tests + Semrack Core - Integration Tests + + + true + + + + + io.quarkus + quarkus-rest + + + fr.codeanddata.semrack + semrack-core + ${project.version} + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + true + + + + diff --git a/modules/semrack-core/integration-tests/src/main/java/fr/codeanddata/semrack/core/it/SemrackCoreResource.java b/modules/semrack-core/integration-tests/src/main/java/fr/codeanddata/semrack/core/it/SemrackCoreResource.java new file mode 100644 index 0000000..283209f --- /dev/null +++ b/modules/semrack-core/integration-tests/src/main/java/fr/codeanddata/semrack/core/it/SemrackCoreResource.java @@ -0,0 +1,32 @@ +/* +* 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 +* +* http://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. +*/ +package fr.codeanddata.semrack.core.it; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("/semrack-core") +@ApplicationScoped +public class SemrackCoreResource { + // add some rest methods here + + @GET + public String hello() { + return "Hello semrack-core"; + } +} diff --git a/modules/semrack-core/integration-tests/src/main/resources/application.properties b/modules/semrack-core/integration-tests/src/main/resources/application.properties new file mode 100644 index 0000000..e69de29 diff --git a/modules/semrack-core/integration-tests/src/test/java/fr/codeanddata/semrack/core/it/SemrackCoreResourceIT.java b/modules/semrack-core/integration-tests/src/test/java/fr/codeanddata/semrack/core/it/SemrackCoreResourceIT.java new file mode 100644 index 0000000..058ca78 --- /dev/null +++ b/modules/semrack-core/integration-tests/src/test/java/fr/codeanddata/semrack/core/it/SemrackCoreResourceIT.java @@ -0,0 +1,7 @@ +package fr.codeanddata.semrack.core.it; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class SemrackCoreResourceIT extends SemrackCoreResourceTest { +} diff --git a/modules/semrack-core/integration-tests/src/test/java/fr/codeanddata/semrack/core/it/SemrackCoreResourceTest.java b/modules/semrack-core/integration-tests/src/test/java/fr/codeanddata/semrack/core/it/SemrackCoreResourceTest.java new file mode 100644 index 0000000..cd814a6 --- /dev/null +++ b/modules/semrack-core/integration-tests/src/test/java/fr/codeanddata/semrack/core/it/SemrackCoreResourceTest.java @@ -0,0 +1,21 @@ +package fr.codeanddata.semrack.core.it; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class SemrackCoreResourceTest { + + @Test + public void testHelloEndpoint() { + given() + .when().get("/semrack-core") + .then() + .statusCode(200) + .body(is("Hello semrack-core")); + } +} diff --git a/modules/semrack-core/pom.xml b/modules/semrack-core/pom.xml new file mode 100644 index 0000000..7305ecf --- /dev/null +++ b/modules/semrack-core/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + fr.codeanddata.semrack + semrack-parent + 1.0-SNAPSHOT + ../../pom.xml + + semrack-core-parent + pom + Semrack Core - Parent + + + deployment + runtime + + + diff --git a/modules/semrack-core/runtime/pom.xml b/modules/semrack-core/runtime/pom.xml new file mode 100644 index 0000000..e2cd7c9 --- /dev/null +++ b/modules/semrack-core/runtime/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-core-parent + 1.0-SNAPSHOT + + semrack-core + Semrack Core - Runtime + + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-security + + + io.quarkus + quarkus-jackson + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + commons-codec + commons-codec + + + org.projectlombok + lombok + + + org.mapstruct + mapstruct + + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + + + + maven-compiler-plugin + + + default-compile + + + + org.projectlombok + lombok + ${lombok.version} + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + + diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocReadInterceptor.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocReadInterceptor.java new file mode 100644 index 0000000..af08011 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocReadInterceptor.java @@ -0,0 +1,8 @@ +package fr.codeanddata.semrack.core; + +import fr.codeanddata.semrack.core.models.SemdocReadContext; +import io.smallrye.mutiny.Uni; + +public interface SemdocReadInterceptor { + Uni interceptSemdocRead(SemdocReadContext context); +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocSearchInterceptor.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocSearchInterceptor.java new file mode 100644 index 0000000..c6719a9 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocSearchInterceptor.java @@ -0,0 +1,8 @@ +package fr.codeanddata.semrack.core; + +import fr.codeanddata.semrack.core.models.SemdocSearchContext; +import io.smallrye.mutiny.Uni; + +public interface SemdocSearchInterceptor { + Uni interceptSemdocSearch(SemdocSearchContext context); +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocStorage.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocStorage.java new file mode 100644 index 0000000..8bdce57 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocStorage.java @@ -0,0 +1,14 @@ +package fr.codeanddata.semrack.core; + +import fr.codeanddata.semrack.core.models.SearchRequest; +import fr.codeanddata.semrack.core.models.SearchResult; +import fr.codeanddata.semrack.core.models.SemrackDocument; +import io.smallrye.mutiny.Uni; + +public interface SemdocStorage { + Uni readDocument(String uid); + Uni searchDocument(SearchRequest query); + Uni countDocuments(SearchRequest request); + Uni documentsExist(SearchRequest query); + Uni storeDocument(SemrackDocument document); +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocWriteInterceptor.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocWriteInterceptor.java new file mode 100644 index 0000000..ed7d7da --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemdocWriteInterceptor.java @@ -0,0 +1,8 @@ +package fr.codeanddata.semrack.core; + +import fr.codeanddata.semrack.core.models.SemdocWriteContext; +import io.smallrye.mutiny.Uni; + +public interface SemdocWriteInterceptor { + Uni interceptSemdocWrite(SemdocWriteContext context); +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemrackLookupExpression.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemrackLookupExpression.java new file mode 100644 index 0000000..8645964 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/SemrackLookupExpression.java @@ -0,0 +1,17 @@ +package fr.codeanddata.semrack.core; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.enterprise.inject.spi.CDI; + +public interface SemrackLookupExpression { + String apply(Object params); + + default T convert(Object params) { + return CDI.current().select(ObjectMapper.class).get().convertValue(params, new TypeReference() {}); + } + + default T convert(Object params, Class clazz) { + return CDI.current().select(ObjectMapper.class).get().convertValue(params, clazz); + } +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/enums/SemrackSortDirection.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/enums/SemrackSortDirection.java new file mode 100644 index 0000000..c7ab71a --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/enums/SemrackSortDirection.java @@ -0,0 +1,5 @@ +package fr.codeanddata.semrack.core.enums; + +public enum SemrackSortDirection { + asc, desc +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/exceptions/SemrackException.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/exceptions/SemrackException.java new file mode 100644 index 0000000..99bb5f1 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/exceptions/SemrackException.java @@ -0,0 +1,18 @@ +package fr.codeanddata.semrack.core.exceptions; + +public class SemrackException extends Exception { + public SemrackException() { + } + + public SemrackException(String message) { + super(message); + } + + public SemrackException(Throwable cause) { + super(cause); + } + + public SemrackException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/exceptions/SemrackRuntimeException.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/exceptions/SemrackRuntimeException.java new file mode 100644 index 0000000..de688ca --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/exceptions/SemrackRuntimeException.java @@ -0,0 +1,18 @@ +package fr.codeanddata.semrack.core.exceptions; + +public class SemrackRuntimeException extends RuntimeException { + public SemrackRuntimeException() { + } + + public SemrackRuntimeException(String message) { + super(message); + } + + public SemrackRuntimeException(Throwable cause) { + super(cause); + } + + public SemrackRuntimeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/interceptors/AnnotateWriteInterceptor.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/interceptors/AnnotateWriteInterceptor.java new file mode 100644 index 0000000..77bce5c --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/interceptors/AnnotateWriteInterceptor.java @@ -0,0 +1,33 @@ +package fr.codeanddata.semrack.core.interceptors; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.codeanddata.semrack.core.SemdocWriteInterceptor; +import fr.codeanddata.semrack.core.models.SemdocWriteContext; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.Map; + +@ApplicationScoped +public class AnnotateWriteInterceptor implements SemdocWriteInterceptor { + + public static final String ANNOTATE_KEY = "annotate"; + public static final String CUSTOM_PREFIX = "custom/"; + + @Inject + ObjectMapper objectMapper; + + @Override + public Uni interceptSemdocWrite(SemdocWriteContext context) { + final Map directives = context.getDirectives(); + + if (directives.containsKey(ANNOTATE_KEY)) { + final Map annotate = objectMapper.convertValue(directives.get(ANNOTATE_KEY), new TypeReference>() {}); + annotate.keySet().forEach(key -> context.getNextDocument().getAnnotations().put(CUSTOM_PREFIX + key, annotate.get(key))); + } + + return Uni.createFrom().voidItem(); + } +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/interceptors/PublishWriteInterceptor.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/interceptors/PublishWriteInterceptor.java new file mode 100644 index 0000000..ca37090 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/interceptors/PublishWriteInterceptor.java @@ -0,0 +1,51 @@ +package fr.codeanddata.semrack.core.interceptors; + +import fr.codeanddata.semrack.core.SemdocWriteInterceptor; +import fr.codeanddata.semrack.core.models.SemdocWriteContext; +import fr.codeanddata.semrack.core.models.SemrackDocument; +import fr.codeanddata.semrack.core.utils.UIDGenerator; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.Map; +import java.util.Optional; + +@ApplicationScoped +public class PublishWriteInterceptor implements SemdocWriteInterceptor { + + final String PUBLICATION_PREFIX = "publication/"; + + @Inject + UIDGenerator uidGenerator; + + @Override + public Uni interceptSemdocWrite(SemdocWriteContext context) { + final Map directives = context.getDirectives(); + final SemrackDocument current = context.getCurrentDocument(); + final SemrackDocument next = context.getNextDocument(); + + int version = (int) Optional.ofNullable(current) + .map(SemrackDocument::getAnnotations) + .map(annotations -> annotations.getOrDefault(PUBLICATION_PREFIX + "version", 0)) + .orElse(0); + + next.getAnnotations().put(PUBLICATION_PREFIX + "reference", Optional.ofNullable(current) + .map(SemrackDocument::getAnnotations) + .map(annotations -> annotations.getOrDefault(PUBLICATION_PREFIX + "reference", next.getUid())) + .orElse(next.getUid())); + + final boolean publish = directives.getOrDefault("publish", false).equals(true); + final boolean snapshot = directives.getOrDefault("snapshot", publish).equals(true); + + next.getAnnotations().put(PUBLICATION_PREFIX + "publish", publish); + next.getAnnotations().put(PUBLICATION_PREFIX + "snapshot", snapshot); + next.getAnnotations().put(PUBLICATION_PREFIX + "version", snapshot ? version + 1 : version); + if (snapshot && current != null) { + next.setUid(uidGenerator.apply(next)); + } + + return Uni.createFrom().voidItem(); + } + +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/mappers/SemrackDocumentMapper.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/mappers/SemrackDocumentMapper.java new file mode 100644 index 0000000..c2cf8de --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/mappers/SemrackDocumentMapper.java @@ -0,0 +1,14 @@ +package fr.codeanddata.semrack.core.mappers; + +import fr.codeanddata.semrack.core.models.SemrackDocument; +import fr.codeanddata.semrack.core.models.PushDocumentRequest; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingConstants; + +@Mapper(componentModel = MappingConstants.ComponentModel.CDI) +public abstract class SemrackDocumentMapper { + @Mapping(source = "uid", target = "uid") + @Mapping(source = "metadata", target = "metadata") + abstract SemrackDocument toDocument(PushDocumentRequest document); +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/PaginationInfo.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/PaginationInfo.java new file mode 100644 index 0000000..3728829 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/PaginationInfo.java @@ -0,0 +1,14 @@ +package fr.codeanddata.semrack.core.models; + +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PaginationInfo { + long page; + long size; + long total; +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/PushDocumentRequest.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/PushDocumentRequest.java new file mode 100644 index 0000000..45f94b7 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/PushDocumentRequest.java @@ -0,0 +1,29 @@ +package fr.codeanddata.semrack.core.models; + +import lombok.*; + +import java.util.Map; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class PushDocumentRequest { + /** + * L'identifiant unique du document. + */ + String uid; + + /** + * Les directives sont les paramètres d'extensions. + * Elles ont pour vocation de générer des actions sur les documents, comme le publish, etc... + */ + Map directives; + + /** + * Le contenu du document. + */ + Map metadata; + +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SearchRequest.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SearchRequest.java new file mode 100644 index 0000000..e1a1770 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SearchRequest.java @@ -0,0 +1,18 @@ +package fr.codeanddata.semrack.core.models; + +import lombok.*; + +import java.util.List; +import java.util.Map; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SearchRequest { + Map filter; + List sort; + SemrackPagination paginate; + List fields; +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SearchResult.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SearchResult.java new file mode 100644 index 0000000..9a3c84c --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SearchResult.java @@ -0,0 +1,15 @@ +package fr.codeanddata.semrack.core.models; + +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SearchResult { + List documents; + PaginationInfo pagination; +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemdocReadContext.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemdocReadContext.java new file mode 100644 index 0000000..aeec61b --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemdocReadContext.java @@ -0,0 +1,16 @@ +package fr.codeanddata.semrack.core.models; + +import lombok.*; + +import java.util.Map; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SemdocReadContext { + + SemrackDocument currentDocument; + +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemdocSearchContext.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemdocSearchContext.java new file mode 100644 index 0000000..1dcc025 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemdocSearchContext.java @@ -0,0 +1,15 @@ +package fr.codeanddata.semrack.core.models; + +import lombok.*; + +import java.util.Map; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SemdocSearchContext { + + Map search; +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemdocWriteContext.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemdocWriteContext.java new file mode 100644 index 0000000..831da6e --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemdocWriteContext.java @@ -0,0 +1,30 @@ +package fr.codeanddata.semrack.core.models; + +import lombok.*; + +import java.util.Map; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SemdocWriteContext { + + /** + * Previous stored document + */ + SemrackDocument currentDocument; + + /** + * Document to be store. It initialized with : + * - the currentDocument uid if exists, or a new one + * - the metadata to be persisted + */ + SemrackDocument nextDocument; + + /** + * The directives to be applied + */ + Map directives; +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemrackDocument.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemrackDocument.java new file mode 100644 index 0000000..e1c1651 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemrackDocument.java @@ -0,0 +1,32 @@ +package fr.codeanddata.semrack.core.models; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.*; + +import java.util.Map; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@RegisterForReflection(serialization = true) +public class SemrackDocument { + + /** + * L'identifiant unique du document. + */ + String uid; + + /** + * Les annotations sont créées par le système, au travers des extensions. Elles sont en lecture seule. + * Elles ont pour vocation d'aider au classement et à la recherche du document (tags, catégories, date de création, etc.). + */ + Map annotations; + + /** + * Le contenu du document. + */ + Map metadata; + +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemrackPagination.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemrackPagination.java new file mode 100644 index 0000000..d66ede0 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemrackPagination.java @@ -0,0 +1,13 @@ +package fr.codeanddata.semrack.core.models; + +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SemrackPagination { + Integer page; + Integer size; +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemrackSort.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemrackSort.java new file mode 100644 index 0000000..62b03ce --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/models/SemrackSort.java @@ -0,0 +1,14 @@ +package fr.codeanddata.semrack.core.models; + +import fr.codeanddata.semrack.core.enums.SemrackSortDirection; +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SemrackSort { + String field; + SemrackSortDirection direction; +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/repositories/SemdocRepository.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/repositories/SemdocRepository.java new file mode 100644 index 0000000..20126eb --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/repositories/SemdocRepository.java @@ -0,0 +1,68 @@ +package fr.codeanddata.semrack.core.repositories; + +import fr.codeanddata.semrack.core.SemdocStorage; +import fr.codeanddata.semrack.core.SemdocWriteInterceptor; +import fr.codeanddata.semrack.core.exceptions.SemrackRuntimeException; +import fr.codeanddata.semrack.core.models.*; +import fr.codeanddata.semrack.core.utils.UIDGenerator; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Any; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import java.util.HashMap; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; + +@ApplicationScoped +public class SemdocRepository { + + @Inject + SemdocStorage semdocStorage; + + @Inject + @Any + Instance writeInterceptors; + + @Inject + UIDGenerator uidGenerator; + + Uni searchDocument(SearchRequest query) { + return semdocStorage.searchDocument(query); + } + + public Uni pushDocument(PushDocumentRequest pushDocument) { + return Optional.ofNullable(pushDocument.getUid()).map(semdocStorage::readDocument).orElse(Uni.createFrom().nullItem()) + .chain(currentDocument -> { + + final SemdocWriteContext context = SemdocWriteContext.builder() + .directives(pushDocument.getDirectives()) + .currentDocument(currentDocument) + .nextDocument(SemrackDocument.builder() + .uid(Optional.ofNullable(currentDocument).map(SemrackDocument::getUid).orElse(uidGenerator.apply(null))) + .metadata(pushDocument.getMetadata()) + .annotations(new HashMap<>()) + .build()) + .build(); + + final AtomicReference> documentUni = new AtomicReference<>(Uni.createFrom().voidItem()); + + writeInterceptors.stream().forEach(filter -> documentUni.set(documentUni.get().chain(() -> filter.interceptSemdocWrite(context)))); + return documentUni.get().chain(() -> semdocStorage.storeDocument(context.getNextDocument())); + }); + } + + public Uni getOrCreate(SearchRequest query, PushDocumentRequest toCreate) { + return searchDocument(query) + .chain(searchResult -> { + if (searchResult.getDocuments().isEmpty()) { + return pushDocument(toCreate); + } else if (searchResult.getDocuments().size() > 1) { + throw new SemrackRuntimeException("Multiple documents found"); + } else { + return Uni.createFrom().item(searchResult.getDocuments().getFirst()); + } + }); + } +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/services/SemrackLookupService.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/services/SemrackLookupService.java new file mode 100644 index 0000000..9c6bf9a --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/services/SemrackLookupService.java @@ -0,0 +1,52 @@ +package fr.codeanddata.semrack.core.services; + +import fr.codeanddata.semrack.core.SemrackLookupExpression; +import io.quarkus.runtime.StartupEvent; +import io.smallrye.common.annotation.Identifier; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.Any; +import jakarta.enterprise.inject.Instance; +import jakarta.inject.Inject; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@ApplicationScoped +public class SemrackLookupService { + + @Inject + @Any + Instance> operators; + + final Map> operatorsIndex = new HashMap<>(); + + void startup(@Observes StartupEvent event) { + operators.stream() + .filter(s -> s.getClass().getSuperclass().isAnnotationPresent(Identifier.class)) + .forEach(s -> { + Identifier identifier = s.getClass().getSuperclass().getAnnotation(Identifier.class); + operatorsIndex.put(identifier.value(), s); + }); + } + + public String lookup(Map lookupExpression) { + final Set lookupKeys = lookupExpression.keySet(); + if (lookupKeys.isEmpty()) { + return ""; + } else if (lookupKeys.size() > 1) { + throw new RuntimeException("Only one lookup expression is allowed"); + } else { + final String lookupKey = lookupKeys.stream().findFirst().get(); + if (! operatorsIndex.containsKey(lookupKey)) { + throw new RuntimeException("Unknown lookup expression '" + lookupKey + "'"); + } else { + final SemrackLookupExpression lookup = operatorsIndex.get(lookupKey); + final Object lookupParams = lookupExpression.get(lookupKey); + return lookup.apply(lookupParams); + } + } + } + +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/utils/SemdocUtils.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/utils/SemdocUtils.java new file mode 100644 index 0000000..9620e94 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/utils/SemdocUtils.java @@ -0,0 +1,27 @@ +package fr.codeanddata.semrack.core.utils; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.Map; + +@ApplicationScoped +public class SemdocUtils { + @Inject + ObjectMapper objectMapper; + + public T map(Object map, Class clazz) { + return objectMapper.convertValue(map, clazz); + } + + public T map(Object map, TypeReference typeRef) { + return objectMapper.convertValue(map, typeRef); + } + + public Map remap(Object map) { + return objectMapper.convertValue(map, new TypeReference<>() {}); + } + +} diff --git a/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/utils/UIDGenerator.java b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/utils/UIDGenerator.java new file mode 100644 index 0000000..50bf783 --- /dev/null +++ b/modules/semrack-core/runtime/src/main/java/fr/codeanddata/semrack/core/utils/UIDGenerator.java @@ -0,0 +1,23 @@ +package fr.codeanddata.semrack.core.utils; + +import fr.codeanddata.semrack.core.models.SemrackDocument; +import jakarta.enterprise.context.ApplicationScoped; +import org.apache.commons.codec.digest.DigestUtils; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.UUID; +import java.util.function.Function; + +@ApplicationScoped +public class UIDGenerator implements Function { + + @Override + public String apply(SemrackDocument semrackDocument) { + final String uuid = UUID.randomUUID().toString(); + final LocalDateTime now = LocalDateTime.now(); + final long timestamp = now.toInstant(ZoneOffset.UTC).toEpochMilli(); + + return DigestUtils.sha256Hex(uuid + timestamp); + } +} diff --git a/modules/semrack-core/runtime/src/main/resources/META-INF/beans.xml b/modules/semrack-core/runtime/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..e69de29 diff --git a/modules/semrack-core/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/modules/semrack-core/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000..536c12a --- /dev/null +++ b/modules/semrack-core/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +name: Semrack Core +#description: Do something useful. +metadata: +# keywords: +# - semrack-core +# guide: ... # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension +# categories: +# - "miscellaneous" +# status: "preview" diff --git a/modules/semrack-storage-postgres/deployment/pom.xml b/modules/semrack-storage-postgres/deployment/pom.xml new file mode 100644 index 0000000..c3432df --- /dev/null +++ b/modules/semrack-storage-postgres/deployment/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-storage-postgres-parent + 1.0-SNAPSHOT + + semrack-storage-postgres-deployment + Semrack Storage Postgres - Deployment + + + + fr.codeanddata.semrack + semrack-core-deployment + + + io.quarkus + quarkus-arc-deployment + + + io.quarkus + quarkus-hibernate-reactive-panache-deployment + + + io.quarkus + quarkus-reactive-pg-client-deployment + + + io.quarkus + quarkus-flyway-deployment + + + io.quarkus + quarkus-jdbc-postgresql-deployment + + + fr.codeanddata.semrack + semrack-storage-postgres + ${project.version} + + + io.quarkus + quarkus-junit5-internal + test + + + + + + + maven-compiler-plugin + + + default-compile + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + + diff --git a/modules/semrack-storage-postgres/deployment/src/main/java/fr/codeanddata/semrack/storage/postgres/deployment/SemrackStoragePostgresProcessor.java b/modules/semrack-storage-postgres/deployment/src/main/java/fr/codeanddata/semrack/storage/postgres/deployment/SemrackStoragePostgresProcessor.java new file mode 100644 index 0000000..c938834 --- /dev/null +++ b/modules/semrack-storage-postgres/deployment/src/main/java/fr/codeanddata/semrack/storage/postgres/deployment/SemrackStoragePostgresProcessor.java @@ -0,0 +1,14 @@ +package fr.codeanddata.semrack.storage.postgres.deployment; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.FeatureBuildItem; + +class SemrackStoragePostgresProcessor { + + private static final String FEATURE = "semrack-storage-postgres"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); + } +} diff --git a/modules/semrack-storage-postgres/deployment/src/test/java/fr/codeanddata/semrack/storage/postgres/test/SemrackStoragePostgresDevModeTest.java b/modules/semrack-storage-postgres/deployment/src/test/java/fr/codeanddata/semrack/storage/postgres/test/SemrackStoragePostgresDevModeTest.java new file mode 100644 index 0000000..3af02e7 --- /dev/null +++ b/modules/semrack-storage-postgres/deployment/src/test/java/fr/codeanddata/semrack/storage/postgres/test/SemrackStoragePostgresDevModeTest.java @@ -0,0 +1,23 @@ +package fr.codeanddata.semrack.storage.postgres.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; + +public class SemrackStoragePostgresDevModeTest { + + // Start hot reload (DevMode) test with your extension loaded + @RegisterExtension + static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnDevModeTest() { + // Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information + Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName()); + } +} diff --git a/modules/semrack-storage-postgres/deployment/src/test/java/fr/codeanddata/semrack/storage/postgres/test/SemrackStoragePostgresTest.java b/modules/semrack-storage-postgres/deployment/src/test/java/fr/codeanddata/semrack/storage/postgres/test/SemrackStoragePostgresTest.java new file mode 100644 index 0000000..795c0ed --- /dev/null +++ b/modules/semrack-storage-postgres/deployment/src/test/java/fr/codeanddata/semrack/storage/postgres/test/SemrackStoragePostgresTest.java @@ -0,0 +1,23 @@ +package fr.codeanddata.semrack.storage.postgres.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class SemrackStoragePostgresTest { + + // Start unit test with your extension loaded + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnUnitTest() { + // Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information + Assertions.assertTrue(true, "Add some assertions to " + getClass().getName()); + } +} diff --git a/modules/semrack-storage-postgres/integration-tests/pom.xml b/modules/semrack-storage-postgres/integration-tests/pom.xml new file mode 100644 index 0000000..73a7da0 --- /dev/null +++ b/modules/semrack-storage-postgres/integration-tests/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-storage-postgres-parent + 1.0.0-SNAPSHOT + + semrack-storage-postgres-integration-tests + Semrack Storage Postgres - Integration Tests + + + true + + + + + io.quarkus + quarkus-rest + + + fr.codeanddata.semrack + semrack-storage-postgres + ${project.version} + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + true + + + + diff --git a/modules/semrack-storage-postgres/integration-tests/src/main/java/fr/codeanddata/semrack/storage/postgres/it/SemrackStoragePostgresResource.java b/modules/semrack-storage-postgres/integration-tests/src/main/java/fr/codeanddata/semrack/storage/postgres/it/SemrackStoragePostgresResource.java new file mode 100644 index 0000000..4346a5c --- /dev/null +++ b/modules/semrack-storage-postgres/integration-tests/src/main/java/fr/codeanddata/semrack/storage/postgres/it/SemrackStoragePostgresResource.java @@ -0,0 +1,32 @@ +/* +* 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 +* +* http://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. +*/ +package fr.codeanddata.semrack.storage.postgres.it; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +@Path("/semrack-storage-postgres") +@ApplicationScoped +public class SemrackStoragePostgresResource { + // add some rest methods here + + @GET + public String hello() { + return "Hello semrack-storage-postgres"; + } +} diff --git a/modules/semrack-storage-postgres/integration-tests/src/main/resources/application.properties b/modules/semrack-storage-postgres/integration-tests/src/main/resources/application.properties new file mode 100644 index 0000000..e69de29 diff --git a/modules/semrack-storage-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/storage/postgres/it/SemrackStoragePostgresResourceIT.java b/modules/semrack-storage-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/storage/postgres/it/SemrackStoragePostgresResourceIT.java new file mode 100644 index 0000000..0a6de17 --- /dev/null +++ b/modules/semrack-storage-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/storage/postgres/it/SemrackStoragePostgresResourceIT.java @@ -0,0 +1,7 @@ +package fr.codeanddata.semrack.storage.postgres.it; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class SemrackStoragePostgresResourceIT extends SemrackStoragePostgresResourceTest { +} diff --git a/modules/semrack-storage-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/storage/postgres/it/SemrackStoragePostgresResourceTest.java b/modules/semrack-storage-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/storage/postgres/it/SemrackStoragePostgresResourceTest.java new file mode 100644 index 0000000..fec6fe2 --- /dev/null +++ b/modules/semrack-storage-postgres/integration-tests/src/test/java/fr/codeanddata/semrack/storage/postgres/it/SemrackStoragePostgresResourceTest.java @@ -0,0 +1,21 @@ +package fr.codeanddata.semrack.storage.postgres.it; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class SemrackStoragePostgresResourceTest { + + @Test + public void testHelloEndpoint() { + given() + .when().get("/semrack-storage-postgres") + .then() + .statusCode(200) + .body(is("Hello semrack-storage-postgres")); + } +} diff --git a/modules/semrack-storage-postgres/pom.xml b/modules/semrack-storage-postgres/pom.xml new file mode 100644 index 0000000..419d3c8 --- /dev/null +++ b/modules/semrack-storage-postgres/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + fr.codeanddata.semrack + semrack-parent + 1.0-SNAPSHOT + ../../pom.xml + + semrack-storage-postgres-parent + Semrack Storage Postgres - Parent + pom + + + deployment + runtime + + + diff --git a/modules/semrack-storage-postgres/runtime/pom.xml b/modules/semrack-storage-postgres/runtime/pom.xml new file mode 100644 index 0000000..ddfbc27 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + + + fr.codeanddata.semrack + semrack-storage-postgres-parent + 1.0-SNAPSHOT + + semrack-storage-postgres + Semrack Storage Postgres - Runtime + + + + fr.codeanddata.semrack + semrack-core + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-hibernate-reactive-panache + + + io.quarkus + quarkus-reactive-pg-client + + + io.quarkus + quarkus-flyway + + + io.quarkus + quarkus-jdbc-postgresql + + + org.flywaydb + flyway-database-postgresql + + + org.projectlombok + lombok + + + io.quarkus + quarkus-junit5 + test + + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + + + + maven-compiler-plugin + + + default-compile + + + + org.projectlombok + lombok + ${lombok.version} + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + + + diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/dtos/InLookupParams.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/dtos/InLookupParams.java new file mode 100644 index 0000000..924aae3 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/dtos/InLookupParams.java @@ -0,0 +1,15 @@ +package fr.codeanddata.semrack.storage.postgres.dtos; + +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class InLookupParams { + String field; + List values; +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/entities/SemrackDocumentEntity.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/entities/SemrackDocumentEntity.java new file mode 100644 index 0000000..6c2d58f --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/entities/SemrackDocumentEntity.java @@ -0,0 +1,27 @@ +package fr.codeanddata.semrack.storage.postgres.entities; + +import fr.codeanddata.semrack.core.models.SemrackDocument; +import io.quarkus.hibernate.reactive.panache.PanacheEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.*; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +import java.io.Serializable; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Entity(name = "SemrackDocument") +@Table(name = "semrack_document") +public class SemrackDocumentEntity extends PanacheEntity implements Serializable { + @Column(unique = true, columnDefinition = "varchar(256)") + String uid; + + @JdbcTypeCode(SqlTypes.JSON) + SemrackDocument document; +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/AndLookup.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/AndLookup.java new file mode 100644 index 0000000..c7022e3 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/AndLookup.java @@ -0,0 +1,23 @@ +package fr.codeanddata.semrack.storage.postgres.operators; + +import fr.codeanddata.semrack.core.services.SemrackLookupService; +import io.smallrye.common.annotation.Identifier; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.List; +import java.util.Map; + +@Identifier("and") +@ApplicationScoped +public class AndLookup implements SemrackJpaLookupExpression>> { + + @Inject + SemrackLookupService lookupService; + + @Override + public String apply(Object expressions) { + final List> typedExpressions = convert(expressions); + return String.join(" AND ", typedExpressions.stream().map(lookupService::lookup).toList()); + } +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookup.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookup.java new file mode 100644 index 0000000..7b6d4c8 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookup.java @@ -0,0 +1,24 @@ +package fr.codeanddata.semrack.storage.postgres.operators; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.smallrye.common.annotation.Identifier; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@Identifier("contains") +@ApplicationScoped +public class ContainsLookup implements SemrackJpaLookupExpression { + + @Inject + ObjectMapper objectMapper; + + @Override + public String apply(Object params) { + try { + return "document @> '" + objectMapper.writeValueAsString(params) + "'"; + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/InLookup.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/InLookup.java new file mode 100644 index 0000000..c7e3e1a --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/InLookup.java @@ -0,0 +1,31 @@ +package fr.codeanddata.semrack.storage.postgres.operators; + +import fr.codeanddata.semrack.core.exceptions.SemrackRuntimeException; +import fr.codeanddata.semrack.storage.postgres.dtos.InLookupParams; +import io.smallrye.common.annotation.Identifier; +import jakarta.enterprise.context.ApplicationScoped; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Identifier("in") +@ApplicationScoped +public class InLookup implements SemrackJpaLookupExpression { + + @Override + public String apply(Object params) { + final InLookupParams inLookupParams = convert(params, InLookupParams.class); + final List fields = new ArrayList<>(Arrays.asList(inLookupParams.getField().split("\\."))); + if (fields.isEmpty()) { + throw new SemrackRuntimeException("Field expected"); + } + + String query = "->> '" + fields.removeLast() + "'"; + if (!fields.isEmpty()) { + query = "-> '" + String.join("' -> '", fields) + "' " + query; + } + + return "document " + query + " in " + "('" + String.join("', '", inLookupParams.getValues()) + "')"; + } +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/OrLookup.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/OrLookup.java new file mode 100644 index 0000000..570bae7 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/OrLookup.java @@ -0,0 +1,23 @@ +package fr.codeanddata.semrack.storage.postgres.operators; + +import fr.codeanddata.semrack.core.services.SemrackLookupService; +import io.smallrye.common.annotation.Identifier; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.List; +import java.util.Map; + +@Identifier("or") +@ApplicationScoped +public class OrLookup implements SemrackJpaLookupExpression>> { + + @Inject + SemrackLookupService lookupService; + + @Override + public String apply(Object expressions) { + final List> typedExpressions = convert(expressions); + return String.join(" OR ", typedExpressions.stream().map(lookupService::lookup).toList()); + } +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/SemrackJpaLookupExpression.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/SemrackJpaLookupExpression.java new file mode 100644 index 0000000..71faeeb --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/operators/SemrackJpaLookupExpression.java @@ -0,0 +1,6 @@ +package fr.codeanddata.semrack.storage.postgres.operators; + +import fr.codeanddata.semrack.core.SemrackLookupExpression; + +public interface SemrackJpaLookupExpression extends SemrackLookupExpression { +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/storage/SemdocJpaStorage.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/storage/SemdocJpaStorage.java new file mode 100644 index 0000000..b1e72c8 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/storage/SemdocJpaStorage.java @@ -0,0 +1,168 @@ +package fr.codeanddata.semrack.storage.postgres.storage; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.codeanddata.semrack.core.SemdocStorage; +import fr.codeanddata.semrack.core.enums.SemrackSortDirection; +import fr.codeanddata.semrack.core.models.*; +import fr.codeanddata.semrack.core.services.SemrackLookupService; +import fr.codeanddata.semrack.core.utils.UIDGenerator; +import fr.codeanddata.semrack.storage.postgres.entities.SemrackDocumentEntity; +import fr.codeanddata.semrack.storage.postgres.utils.PathMapper; +import io.quarkus.hibernate.reactive.panache.PanacheRepository; +import io.quarkus.hibernate.reactive.panache.common.WithSession; +import io.quarkus.hibernate.reactive.panache.common.WithTransaction; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.*; + +@ApplicationScoped +public class SemdocJpaStorage implements SemdocStorage, PanacheRepository { + + @Inject + UIDGenerator generator; + + @Inject + PathMapper pathMapper; + + @Inject + SemrackLookupService lookupService; + + @Inject + ObjectMapper objectMapper; + + @Override + @WithSession + public Uni readDocument(String uid) { + return find("uid = ?1", uid) + .firstResult().map(d -> Optional.ofNullable(d).map(SemrackDocumentEntity::getDocument).orElse(null)); + } + + @Override + @WithSession + public Uni searchDocument(SearchRequest query) { + return search(query); + } + + @Override + public Uni countDocuments(SearchRequest request) { + final String lookup = lookupService.lookup(request.getFilter()); + final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup; + + return getSession() + .chain(s -> s.createNativeQuery("SELECT count(id) FROM semrack_document" + whereClause, Long.class).getSingleResultOrNull()) + .map(count -> count == null ? 0 : count); + } + + @Override + public Uni documentsExist(SearchRequest query) { + return countDocuments(query) + .map(c -> c > 0); + } + + @WithTransaction + @Override + public Uni storeDocument(SemrackDocument document) { + if (document.getUid() == null) { + return createDocument(document); + } else { + return find("uid = ?1", document.getUid()) + .count() + .chain(n -> n == 0 ? createDocument(document) : updateDocument(document)); + } + } + + // TODO err : handle existing document + @WithTransaction + Uni createDocument(SemrackDocument document) { + final String uid = document.getUid() == null ? generator.apply(document) : document.getUid(); + document.setUid(uid); + final SemrackDocumentEntity entity = SemrackDocumentEntity.builder() + .uid(uid) + .document(document) + .build(); + return persist(entity).map(SemrackDocumentEntity::getDocument); + } + + // TODO err : document not exists + @WithTransaction + Uni updateDocument(SemrackDocument document) { + return update("uid = ?1, document = ?2 WHERE uid = ?1", document.getUid(), document) + .map(x -> document); + } + + Uni search(SearchRequest request) { + final String baseQuery = buildBaseQuery(request); + + final StringBuilder sorting = new StringBuilder(); + if (request.getSort() != null && !request.getSort().isEmpty()) { + sorting.append(" ORDER BY "); + sorting.append(String.join(", ", request.getSort().stream().map(sort -> sort.getField() + " " + Optional.ofNullable(sort.getDirection()).orElse(SemrackSortDirection.asc).name()).toList())); + } + + final StringBuilder paginate = new StringBuilder(); + if (request.getPaginate() != null) { + final SemrackPagination pagination = request.getPaginate(); + final Integer size = pagination.getSize() == null ? 200 : pagination.getSize(); + final Integer page = pagination.getPage() == null ? 0 : pagination.getPage(); + + paginate.append(" LIMIT ").append(size).append(" OFFSET ").append(size * page); + } + + return getSession() + .chain(s -> { + final SearchResult searchResult = SearchResult.builder().build(); + return countDocuments(request) + .invoke(count -> searchResult.setPagination(PaginationInfo.builder() + .page(Optional.ofNullable(request.getPaginate()).map(SemrackPagination::getPage).orElse(0)) + .size(Optional.ofNullable(request.getPaginate()).map(SemrackPagination::getSize).orElse(0)) + .total(count) + .build())) + .call(() -> s + .createNativeQuery(baseQuery + sorting + paginate, Object.class).getResultList() + .invoke(results -> searchResult.setDocuments(project(request.getFields(), results)))) + .map(count -> searchResult); + }); + } + + String buildBaseQuery(SearchRequest request) { + final String lookup = lookupService.lookup(request.getFilter()); + final String whereClause = lookup.isEmpty() ? "" : " WHERE " + lookup; + + if (request.getFields() == null || request.getFields().isEmpty()) { + return "SELECT document FROM semrack_document" + whereClause; + } else { + return "SELECT " + toJsonbPathExtract(request.getFields()) + " FROM semrack_document" + whereClause; + } + } + + String toJsonbPathExtract(List fields) { + return String.join(",", fields.stream().map(field -> { + final String serializedField = "'" + String.join("','", field.split("\\.")) + "'"; + return "jsonb_extract_path(document, " + serializedField + ")"; + }).toList()); + } + + List project(List fields, Object results) { + final List projectedResults = objectMapper.convertValue(results, List.class); + if (fields == null || fields.isEmpty()) { + return projectedResults.stream().map(result -> objectMapper.convertValue(result, SemrackDocument.class)).toList(); + } else { + return projectedResults.stream().map(result -> { + final List row = fields.size() == 1 ? List.of(result) : objectMapper.convertValue(result, new TypeReference<>() { + }); + final List rowMut = new ArrayList<>(row); + final List fieldCp = new ArrayList<>(fields); + final Map document = new HashMap<>(); + while (!fieldCp.isEmpty() && !row.isEmpty()) { + final String field = fieldCp.removeFirst(); + final Object value = rowMut.removeFirst(); + pathMapper.map(field, document, value); + } + return objectMapper.convertValue(document, SemrackDocument.class); + }).toList(); + } + } +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/utils/PathMapper.java b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/utils/PathMapper.java new file mode 100644 index 0000000..3f30ea4 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/java/fr/codeanddata/semrack/storage/postgres/utils/PathMapper.java @@ -0,0 +1,44 @@ +package fr.codeanddata.semrack.storage.postgres.utils; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.*; +import java.util.stream.Stream; + +@ApplicationScoped +public class PathMapper { + + @Inject + ObjectMapper objectMapper; + + public Map map(String path, Object value) { + return map(path, new HashMap<>(), value); + } + + public Map map(String path, Map target, Object value) { + setValue(new ArrayList<>(Stream.of(path.split("\\.")).toList()), target, value); + return target; + } + + void setValue(List paths, Map container, Object value) { + if (!paths.isEmpty()) { + final String key = paths.removeFirst(); + if (paths.isEmpty()) { + container.put(key, value); + return; + } + + if (!container.containsKey(key) || !(container.get(key) instanceof Map)) { + container.put(key, new HashMap<>()); + } + + final Map target = objectMapper.convertValue(container.get(key), new TypeReference<>() {}); + container.put(key, target); + setValue(paths, target, value); + } + } + +} diff --git a/modules/semrack-storage-postgres/runtime/src/main/resources/META-INF/beans.xml b/modules/semrack-storage-postgres/runtime/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..e69de29 diff --git a/modules/semrack-storage-postgres/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/modules/semrack-storage-postgres/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000..05c2688 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +name: Semrack Storage Postgres +#description: Do something useful. +metadata: +# keywords: +# - semrack-storage-postgres +# guide: ... # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension +# categories: +# - "miscellaneous" +# status: "preview" diff --git a/modules/semrack-storage-postgres/runtime/src/main/resources/application.properties b/modules/semrack-storage-postgres/runtime/src/main/resources/application.properties new file mode 100644 index 0000000..e2217b5 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/resources/application.properties @@ -0,0 +1,7 @@ +quarkus.datasource.db-kind=postgresql +quarkus.flyway.enabled=true +quarkus.flyway.active=false +quarkus.flyway.migrate-at-start=true + +quarkus.native.resources.includes=src/main/resources/db/migration/** +quarkus.native.additional-build-args=-H:SerializationConfigurationResources=serialization-config.json diff --git a/modules/semrack-storage-postgres/runtime/src/main/resources/migration/V1.0.0__Base.sql b/modules/semrack-storage-postgres/runtime/src/main/resources/migration/V1.0.0__Base.sql new file mode 100644 index 0000000..9f14894 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/resources/migration/V1.0.0__Base.sql @@ -0,0 +1,10 @@ +create sequence semrack_document_SEQ start with 1 increment by 50; +create table semrack_document +( + id bigint not null, + uid varchar(256) unique, + document jsonb, + primary key (id) +); +alter table semrack_document + add constraint UK307dbjrfh151l0r7bb36xdwew unique (uid); diff --git a/modules/semrack-storage-postgres/runtime/src/main/resources/serialization-config.json b/modules/semrack-storage-postgres/runtime/src/main/resources/serialization-config.json new file mode 100644 index 0000000..e46f036 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/main/resources/serialization-config.json @@ -0,0 +1,3 @@ +[ + {"name": "java.lang.String"} +] diff --git a/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookupTest.java b/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookupTest.java new file mode 100644 index 0000000..b6c4283 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/ContainsLookupTest.java @@ -0,0 +1,25 @@ +package fr.codeanddata.semrack.storage.postgres.operators; + +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.common.annotation.Identifier; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +@QuarkusTest +class ContainsLookupTest { + + @Inject + @Identifier("contains") + ContainsLookup subject; + + @Test + void testLookup() { + final String lookup = subject.apply(Map.of("product", Map.of("sku", "xxx"))); + final String expectedLookup = "document @> '{\"product\":{\"sku\":\"xxx\"}}'"; + Assertions.assertEquals(expectedLookup, lookup); + } + +} diff --git a/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/InLookupTest.java b/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/InLookupTest.java new file mode 100644 index 0000000..6132df7 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/operators/InLookupTest.java @@ -0,0 +1,43 @@ +package fr.codeanddata.semrack.storage.postgres.operators; + +import fr.codeanddata.semrack.storage.postgres.dtos.InLookupParams; +import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.common.annotation.Identifier; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +@QuarkusTest +class InLookupTest { + + @Inject + @Identifier("in") + InLookup subject; + + @Test + void testConversion() { + final String field = "root.parent.child.field"; + final List values = List.of("un", "deux", "trois"); + final Map inParams = Map.of( + "field", field, + "values", values + ); + + final InLookupParams convertedParams = subject.convert(inParams, InLookupParams.class); + Assertions.assertEquals(field, convertedParams.getField()); + Assertions.assertEquals(values, convertedParams.getValues()); + } + + @Test + void testLookup() { + final String lookup = subject.apply(Map.of( + "field", "lookup.field", + "values", List.of("list", "of", "values"))); + final String expectedLookup = "document -> 'lookup' ->> 'field' in ('list', 'of', 'values')"; + Assertions.assertEquals(expectedLookup, lookup); + } + +} diff --git a/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/utils/PathMapperTest.java b/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/utils/PathMapperTest.java new file mode 100644 index 0000000..d01ffa7 --- /dev/null +++ b/modules/semrack-storage-postgres/runtime/src/test/java/fr/codeanddata/semrack/storage/postgres/utils/PathMapperTest.java @@ -0,0 +1,42 @@ +package fr.codeanddata.semrack.storage.postgres.utils; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +@QuarkusTest +public class PathMapperTest { + + @Inject + PathMapper pathMapper; + + @Inject + ObjectMapper objectMapper; + + @Test + void testMapper() { + final Map map = new HashMap<>(); + final Map root = new HashMap<>(); + map.put("root", root); + root.put("name", "bob"); + + pathMapper.map("root.child.subchild1.value", map, 1); + pathMapper.map("root.child.subchild2.value", map, 2); + pathMapper.map("hello", map, "world"); + + Assertions.assertEquals("world", map.get("hello")); + Assertions.assertEquals(1, conv(conv(conv(map, "root"), "child"), "subchild1").get("value")); + Assertions.assertEquals(2, conv(conv(conv(map, "root"), "child"), "subchild2").get("value")); + Assertions.assertEquals("bob", conv(map, "root").get( "name")); + } + + Map conv(Map container, String key) { + return objectMapper.convertValue(container.get(key), new TypeReference<>() {}); + } +} diff --git a/mvnw b/mvnw new file mode 100755 index 0000000..121babf --- /dev/null +++ b/mvnw @@ -0,0 +1,332 @@ +#!/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 +# +# http://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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, publish 3.3.2 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# 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 /usr/local/etc/mavenrc ]; then + . /usr/local/etc/mavenrc + fi + + 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 + JAVA_HOME="$(/usr/libexec/java_home)" + export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home" + export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -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 "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \ + && JAVA_HOME="$( + cd "$JAVA_HOME" || ( + echo "cannot cd into $JAVA_HOME." >&2 + exit 1 + ) + 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="$( + \unset -f command 2>/dev/null + \command -v 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." >&2 +fi + +# 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" >&2 + 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/.." || exit 1 + pwd + ) + fi + # end of workaround + done + printf '%s' "$( + cd "$basedir" || exit 1 + pwd + )" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the storage with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' <"$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1 +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# 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. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in wrapperUrl) + wrapperUrl="$safeValue" + break + ;; + esac + done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget >/dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl >/dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in wrapperSha256Sum) + wrapperSha256Sum=$value + break + ;; + esac +done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum >/dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +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 "$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 + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..4136715 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,206 @@ +@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 http://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 Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@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 "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\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. >&2 +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. >&2 +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. >&2 +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. >&2 +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 WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_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 WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_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('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@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 "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\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% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..e8e50b4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,160 @@ + + + 4.0.0 + fr.codeanddata.semrack + semrack-parent + 1.0-SNAPSHOT + pom + + + modules/semrack-core + modules/semrack-storage-postgres + modules/semrack-api-rest + + + + + + 3.14.0 + 21 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + ${quarkus.version} + true + 3.5.3 + + + 1.16.0 + 1.18.38 + 1.6.3 + 1.0-SNAPSHOT + 3.25.3 + 2.9.0 + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + commons-codec + commons-codec + ${commons-codec.version} + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + + fr.codeanddata.semrack + semrack-core + ${semrack.version} + + + fr.codeanddata.semrack + semrack-core-deployment + ${semrack.version} + + + fr.codeanddata.semrack + semrack-storage-postgres + ${semrack.version} + + + fr.codeanddata.semrack + semrack-storage-postgres-deployment + ${semrack.version} + + + fr.codeanddata.semrack + semrack-api-rest + ${semrack.version} + + + fr.codeanddata.semrack + semrack-api-rest-deployment + ${semrack.version} + + + com.jayway.jsonpath + json-path + ${jsonpath.version} + + + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-failsafe-plugin + ${failsafe-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + true + + + + + + + + + gitea + https://gitea.tech.codeanddata.fr/api/packages/${env.MAVEN_REPO_OWNER}/maven + + + + + gitea + https://gitea.tech.codeanddata.fr/api/packages/${env.MAVEN_REPO_OWNER}/maven + + + gitea + https://gitea.tech.codeanddata.fr/api/packages/${env.MAVEN_REPO_OWNER}/maven + + +