=UTF-8
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..be45cb9
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,243 @@
+//
+// Created by Patrick Verboom on 04.11.2024.
+// Copyright © 2024 heyday Marketing GmbH. All rights reserved.
+//
+
+def defaultRocketChatChannel = "#builds"
+def rocketChatColor = "#e813c8"
+def rocketChatEmoji = ":skunk:"
+
+def numOfArtifactsToKeep = env.GIT_BRANCH == "master" ? "10" : "5"
+
+
+def result = []
+
+pipeline {
+ agent {
+ label "macOS"
+ }
+ tools {
+ maven 'Maven Latest'
+ jdk 'OpenJDK-21.0.5'
+
+ }
+
+ environment {
+ PATH = "$PATH:/Users/Shared/jenkins/flutter/bin"
+ }
+
+ options {
+ // keeps only the atifacts of the last 15 builds
+ buildDiscarder(
+ logRotator(
+ artifactDaysToKeepStr: '',
+ artifactNumToKeepStr: numOfArtifactsToKeep,
+ daysToKeepStr: '',
+ numToKeepStr: numOfArtifactsToKeep
+ )
+ )
+ }
+ stages {
+ stage ('Initialize') {
+ steps {
+ script {
+ def msg = "Pipeline ${env.JOB_NAME} ${env.BUILD_NUMBER} has started. \n More info at: ${env.BUILD_URL} "
+ try {
+ echo msg
+ rocketSend channel: defaultRocketChatChannel, message: "jenkins-${env.JOB_NAME}-${env.BUILD_NUMBER}", emoji: rocketChatEmoji, attachments: [[$class: 'MessageAttachment', color: rocketChatColor, text: msg, title: 'Build started']]
+ } catch (Exception e) {
+ echo "Exception occurred sending : " + e.toString() + "\nmessage: " + msg
+ throw e
+ }
+ }
+
+ sh '''
+ echo "PATH = ${PATH}"
+ echo "M2_HOME = ${M2_HOME}"
+ printenv
+ '''
+ }
+ }
+/*
+ stage ('Build Frontend') {
+ steps {
+ echo "running Frontend build for branch ${env.BRANCH_NAME}"
+
+ dir("hartmann-foto-documentation-frontend"){
+ //flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=OURBASEURL/canvaskit/
+ sh 'flutter pub get'
+ //sh 'dart run build_runner build'
+ sh 'flutter build web --no-tree-shake-icons'
+ dir("build/web"){
+ sh "cp -R . ../../../hartmann-foto-documentation-web/src/main/webapp/."
+ }
+ }
+ }
+ post {
+ always {
+ script {
+ def msg = "Build ${env.JOB_NAME} ${env.BUILD_NUMBER} frontend build has finished. Result: ${currentBuild.currentResult}. Took ${currentBuild.duration}.\n More info at: ${env.BUILD_URL} "
+ result.add( ["Stage: Build ${currentBuild.currentResult}", msg] )
+ }
+ }
+ }
+ }
+ stage ('Test Frontend') {
+ steps {
+ echo "running Frontend test for branch ${env.BRANCH_NAME}"
+
+ dir("hartmann-foto-documentation-frontend"){
+ // Run tests with JSON output for Jenkins parsing
+ sh '''
+ flutter test --coverage --reporter=json --file-reporter=json:test_results.json --reporter=expanded || true
+ dart test_runner.dart test_results.json test_results.xml
+ '''
+ }
+ }
+ post {
+ success {
+ archiveArtifacts artifacts: 'hartmann-foto-documentation-frontend/coverage/lcov.info', fingerprint: true
+ }
+ always {
+ // Publish test results to Jenkins
+ junit 'hartmann-foto-documentation-frontend/test_results.xml'
+
+ // Archive test artifacts
+ archiveArtifacts artifacts: 'hartmann-foto-documentation-frontend/test_results.json, hartmann-foto-documentation-frontend/test_results.xml', allowEmptyArchive: true
+
+ script {
+ def msg = "Build ${env.JOB_NAME} ${env.BUILD_NUMBER} frontend test has finished. Result: ${currentBuild.currentResult}. Took ${currentBuild.duration}.\n More info at: ${env.BUILD_URL} "
+ result.add( ["Stage: Build ${currentBuild.currentResult}", msg] )
+ }
+ }
+ }
+ }
+*/
+ stage ('Build') {
+ steps {
+ echo "running build for branch ${env.BRANCH_NAME}"
+ //sh 'mvn dependency:purge-local-repository clean -U -f hartmann-foto-documentation/pom.xml '
+ sh 'mvn deploy -U -f pom.xml '
+ }
+ post {
+ success {
+ junit '**/target/surefire-reports/*.xml'
+ archiveArtifacts artifacts: '**/target/*.war, **/target/*.zip', fingerprint: true
+ }
+
+ always {
+ script {
+ def msg = "Build ${env.JOB_NAME} ${env.BUILD_NUMBER} build has finished. Result: ${currentBuild.currentResult}. Took ${currentBuild.duration}.\n More info at: ${env.BUILD_URL} "
+ result.add( ["Stage: Build ${currentBuild.currentResult}", msg] )
+ }
+ }
+ }
+ }
+
+ stage ('Tests') {
+ steps {
+ sh 'mvn deploy -P docker -f hartmann-foto-documentation-docker/pom.xml'
+ }
+ post {
+ success {
+ junit '**/target/surefire-reports/*.xml'
+ }
+ always {
+ script {
+ def msg = "Build ${env.JOB_NAME} ${env.BUILD_NUMBER} tests has finished. Result: ${currentBuild.currentResult}. Took ${currentBuild.duration}.\n More info at: ${env.BUILD_URL} "
+ result.add( ["Stage: Test ${currentBuild.currentResult}", msg])
+ }
+ }
+ }
+ }
+
+ stage ('Code Coverage') {
+ steps {
+ sh 'mvn jacoco:report-aggregate -P docker -f pom.xml'
+ sh 'cp hartmann-foto-documentation-docker/target/site/jacoco-aggregate/jacoco.xml hartmann-foto-documentation-app/target/site/jacoco-aggregate/jacoco.xml'
+ }
+ post {
+ always {
+ script {
+ def msg = "Build ${env.JOB_NAME} ${env.BUILD_NUMBER} Code coverage has finished. Result: ${currentBuild.currentResult}. Took ${currentBuild.duration}.\n More info at: ${env.BUILD_URL} "
+ result.add( ["Stage: Test ${currentBuild.currentResult}", msg])
+ }
+ }
+ }
+ }
+
+ stage ('SonarQube analysis') {
+ steps {
+ withSonarQubeEnv('heyday sonar') {
+ script {
+ def key = "${env.BRANCH_NAME.replaceAll("/", "_")}"
+ def projectKey = "\"marketing.heyday.hartmann:hartmann-foto-documentation:${key}\""
+ echo "running sonar for branch ${projectKey}"
+ sh "mvn org.sonarsource.scanner.maven:sonar-maven-plugin:5.3.0.6276:sonar -f pom.xml -Dsonar.projectKey=${projectKey}"
+ }
+ }
+ }
+ post {
+ always {
+ script {
+ def msg = "Build ${env.JOB_NAME} ${env.BUILD_NUMBER} sonar has finished. Result: ${currentBuild.currentResult}. Took ${currentBuild.duration}.\n More info at: ${env.BUILD_URL} "
+ result.add(["Stage: Sonar ${currentBuild.currentResult}", msg])
+ }
+ }
+ }
+ }
+
+ stage ('Release') {
+ when {
+ branch 'master'
+ }
+
+ steps {
+ sh "mvn -B -U -X -e -Dmaven.test.skip=true -Djava.awt.headless=true release:prepare release:perform -DbambooBuildNumber=${env.BUILD_NUMBER} -Dmaven.javadoc.skip=true -f pom.xml "
+ }
+ post {
+ success {
+ archiveArtifacts artifacts: '**/target/*.war, **/target/*.zip', fingerprint: true
+ }
+ always {
+ script {
+ def msg = "Build ${env.JOB_NAME} ${env.BUILD_NUMBER} release has finished. Result: ${currentBuild.currentResult}. Took ${currentBuild.duration}.\n More info at: ${env.BUILD_URL} "
+ result.add(["Stage: Release ${currentBuild.currentResult}",msg])
+ }
+ }
+ }
+ }
+ }
+
+ post {
+ always {
+ script {
+ def msg = "Build ${env.JOB_NAME} ${env.BUILD_NUMBER} Pipeline finished. Result: ${currentBuild.currentResult}. Took ${currentBuild.duration}.\n More info at: ${env.BUILD_URL} "
+ result.add(["Stage: Pipeline ${currentBuild.currentResult}", msg])
+ try {
+ //echo msg
+ def attachements = []
+ for (elem in result) {
+ attachements+=([$class: 'MessageAttachment', color: rocketChatColor, text: elem.get(1), title: elem.get(0)])
+ }
+ rocketSend channel: defaultRocketChatChannel, message: "jenkins-${env.JOB_NAME}-${env.BUILD_NUMBER}", emoji: rocketChatEmoji, attachments: attachements
+ } catch (Exception e) {
+ echo "Exception occurred sending : " + e.toString() + "\nmessage: " + msg
+ throw e
+ }
+ }
+ }
+
+ unsuccessful {
+ emailext body: "${currentBuild.currentResult}: Job ${env.JOB_NAME} build ${env.BUILD_NUMBER}\n More info at: ${env.BUILD_URL}",
+ recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']],
+ subject: "Failed Jenkins Build ${currentBuild.currentResult}: Job ${env.JOB_NAME}"
+ }
+
+ fixed {
+ emailext body: "${currentBuild.currentResult}: Job ${env.JOB_NAME} build ${env.BUILD_NUMBER}\n More info at: ${env.BUILD_URL}",
+ recipientProviders: [[$class: 'DevelopersRecipientProvider'], [$class: 'RequesterRecipientProvider']],
+ subject: "Success Jenkins Build ${currentBuild.currentResult}: Job ${env.JOB_NAME}"
+ }
+ }
+}
diff --git a/hartmann-foto-documentation-app/pom.xml b/hartmann-foto-documentation-app/pom.xml
new file mode 100644
index 0000000..2650d2c
--- /dev/null
+++ b/hartmann-foto-documentation-app/pom.xml
@@ -0,0 +1,343 @@
+
+
+
+ 4.0.0
+
+ marketing.heyday.hartmann.fotodocumentation
+ hartmann-foto-documentation
+ 1.0.0
+ ../hartmann-foto-documentation/pom.xml
+
+ hartmann-foto-documentation-app
+ 1.0.0-SNAPSHOT
+ jar
+ hartmann-foto-documentation app
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+ org.wildfly.security
+ wildfly-elytron
+ 2.5.2.Final
+ provided
+
+
+
+ org.wildfly.security
+ wildfly-elytron-credential
+ 2.5.2.Final
+ provided
+
+
+
+
+
+ org.apache.poi
+ poi
+
+
+ org.apache.poi
+ poi-ooxml
+
+
+
+
+
+ org.apache.httpcomponents
+ fluent-hc
+ 4.5.1
+
+
+
+
+ io.swagger.core.v3
+ swagger-jaxrs2-jakarta
+
+
+
+ org.webjars
+ swagger-ui
+ 3.22.2
+
+
+
+ com.github.spullara.mustache.java
+ compiler
+ 0.9.5
+
+
+
+ com.networknt
+ json-schema-validator
+ 0.1.2
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ io.undertow
+ undertow-core
+
+
+
+
+
+ commons-logging
+ commons-logging
+ ${version.commons-logging}
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${version.commons-lang3}
+
+
+
+ io.javaslang
+ javaslang
+ ${version.javaslang}
+
+
+
+ org.flywaydb
+ flyway-core
+ 3.1
+
+
+
+ org.jboss.ejb3
+ jboss-ejb3-ext-api
+ ${version.org.jboss.ejb3.ext-api}
+ provided
+
+
+
+ jakarta.platform
+ jakarta.jakartaee-api
+ provided
+
+
+
+
+
+ org.jboss.resteasy
+ resteasy-core
+ provided
+
+
+
+ org.jboss.resteasy
+ resteasy-core-spi
+ provided
+
+
+
+ org.jboss.resteasy
+ resteasy-jackson2-provider
+ provided
+
+
+
+ org.jboss.resteasy
+ resteasy-multipart-provider
+ provided
+
+
+
+
+
+ commons-fileupload
+ commons-fileupload
+ ${version.commons-fileupload}
+
+
+ commons-io
+ commons-io
+ ${version.commons-io}
+
+
+
+
+
+ org.apache.poi
+ poi
+
+
+ org.apache.poi
+ poi-ooxml
+
+
+
+
+ org.hibernate.orm
+ hibernate-core
+ provided
+
+
+
+
+
+
+ log4j
+ log4j
+ 1.2.15
+ test
+
+
+ javax.mail
+ mail
+
+
+ javax.jms
+ jms
+
+
+ com.sun.jdmk
+ jmxtools
+
+
+ com.sun.jmx
+ jmxri
+
+
+
+
+
+
+ com.google.guava
+ guava
+ 17.0
+ test
+
+
+
+ xml-apis
+ xml-apis
+ 1.4.01
+ test
+
+
+ org.subethamail
+ subethasmtp-smtp
+ ${subethamail.version}
+ test
+
+
+ javax.servlet
+ servlet-api
+
+
+
+
+ org.subethamail
+ subethasmtp-wiser
+ ${subethamail.version}
+ test
+
+
+ org.subethamail
+ subethasmtp-smtp
+
+
+
+
+
+ org.junit.platform
+ junit-platform-suite-engine
+ test
+
+
+ org.junit.platform
+ junit-platform-commons
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+ org.junit.platform
+ junit-platform-launcher
+ test
+
+
+ org.postgresql
+ postgresql
+ test
+
+
+
+ org.jacoco
+ org.jacoco.core
+ test
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.5
+ test
+
+
+
+ org.slf4j
+ slf4j-log4j12
+ test
+
+
+
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/db/migration/DbMigrator.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/db/migration/DbMigrator.java
new file mode 100644
index 0000000..d2d21f8
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/db/migration/DbMigrator.java
@@ -0,0 +1,48 @@
+package marketing.heyday.hartmann.fotodocumentation.core.db.migration;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.Resource;
+import jakarta.ejb.Singleton;
+import jakarta.ejb.Startup;
+import jakarta.ejb.TransactionManagement;
+import jakarta.ejb.TransactionManagementType;
+import javax.sql.DataSource;
+
+import org.flywaydb.core.Flyway;
+
+/**
+ *
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+
+@Singleton
+@Startup
+@TransactionManagement(TransactionManagementType.BEAN)
+public class DbMigrator {
+
+ @Resource(lookup = "java:/jdbc/fotoDocumentationDS")
+ private DataSource dataSource;
+
+ @PostConstruct
+ public void onStartup() {
+ Flyway flyway = getFlyway();
+ flyway.setDataSource(dataSource);
+ flyway.setTable("DB_MIGRATION");
+ flyway.setLocations(this.getClass().getPackage().getName());
+ flyway.setBaselineOnMigrate(false);
+ flyway.setValidateOnMigrate(false);
+ flyway.setIgnoreFailedFutureMigration(true);
+
+ flyway.migrate();
+ }
+
+ protected Flyway getFlyway() {
+ return new Flyway();
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/AbstractDateEntity.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/AbstractDateEntity.java
new file mode 100644
index 0000000..1f44060
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/AbstractDateEntity.java
@@ -0,0 +1,71 @@
+package marketing.heyday.hartmann.fotodocumentation.core.model;
+
+import java.util.Date;
+
+import jakarta.persistence.*;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ *
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+@MappedSuperclass
+public abstract class AbstractDateEntity extends AbstractEntity {
+ private static final long serialVersionUID = 1L;
+
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "JPA_CREATED", nullable = false)
+ @JsonIgnore
+ private Date jpaCreated;
+
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "JPA_UPDATED", nullable = false)
+ @JsonIgnore
+ private Date jpaUpdated;
+
+ @Column(name = "JPA_ACTIVE", nullable = false)
+ private boolean active = true;
+
+ @Version
+ @Column(name = "JPA_VERSION", nullable = false)
+ private int jpaVersion = 0;
+
+ @PrePersist
+ protected void onCreate() {
+ Date now = new Date();
+ jpaCreated = now;
+ jpaUpdated = now;
+ }
+
+ @PreUpdate
+ protected void onUpdate() {
+ jpaUpdated = new Date();
+ }
+
+ public Date getJpaCreated() {
+ return jpaCreated;
+ }
+
+ public Date getJpaUpdated() {
+ return jpaUpdated;
+ }
+
+ public int getJpaVersion() {
+ return jpaVersion;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/AbstractEntity.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/AbstractEntity.java
new file mode 100644
index 0000000..b112849
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/AbstractEntity.java
@@ -0,0 +1,21 @@
+package marketing.heyday.hartmann.fotodocumentation.core.model;
+
+import java.io.Serializable;
+
+import jakarta.persistence.MappedSuperclass;
+
+
+/**
+ *
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+@MappedSuperclass
+public abstract class AbstractEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/Customer.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/Customer.java
new file mode 100644
index 0000000..c9d5a6b
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/Customer.java
@@ -0,0 +1,108 @@
+package marketing.heyday.hartmann.fotodocumentation.core.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import jakarta.persistence.*;
+
+/**
+ *
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+
+@Entity
+@Table(name = "customer")
+@NamedQuery(name = Customer.FIND_BY_NUMBER, query = "select c from Customer c where c.customerNumber = :cutomerNumber")
+public class Customer extends AbstractDateEntity {
+ private static final long serialVersionUID = 1L;
+ public static final String SEQUENCE = "customer_seq";
+ public static final String FIND_BY_NUMBER = "Customer.findByNumber";
+ public static final String PARAM_NUMBER = "cutomerNumber";
+
+ @Id
+ @Column(name = "customer_id", length = 22)
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
+ @SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE, allocationSize = 1)
+ private Long customerId;
+
+ @Column(name = "customer_number", unique = true, nullable = false)
+ private String customerNumber;
+
+ @Column(name = "name", nullable = false)
+ private String name;
+
+ @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
+ @JoinColumn(name = "customer_id_fk")
+ private Set pictures = new HashSet<>();
+
+ public Long getCustomerId() {
+ return customerId;
+ }
+
+ public void setCustomerId(Long customerId) {
+ this.customerId = customerId;
+ }
+
+ public String getCustomerNumber() {
+ return customerNumber;
+ }
+
+ public void setCustomerNumber(String customerNumber) {
+ this.customerNumber = customerNumber;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Set getPictures() {
+ return pictures;
+ }
+
+ public void setPictures(Set pictures) {
+ this.pictures = pictures;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().append(customerNumber).toHashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || this.getClass() != obj.getClass()) {
+ return false;
+ }
+ return this.customerNumber.equals(((Customer) obj).getCustomerNumber());
+ }
+
+ public static class Builder {
+ private Customer instance = new Customer();
+
+ public Builder customerNumber(String customerNumber) {
+ instance.setCustomerNumber(customerNumber);
+ return this;
+ }
+
+ public Builder name(String name) {
+ instance.setName(name);
+ return this;
+ }
+
+ public Customer build() {
+ return instance;
+ }
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/Picture.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/Picture.java
new file mode 100644
index 0000000..81fbb3f
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/Picture.java
@@ -0,0 +1,125 @@
+package marketing.heyday.hartmann.fotodocumentation.core.model;
+
+import java.util.Date;
+
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import jakarta.persistence.*;
+
+/**
+ *
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+
+@Entity
+@Table(name = "picture")
+public class Picture extends AbstractDateEntity {
+ private static final long serialVersionUID = 1L;
+ public static final String SEQUENCE = "picture_seq";
+
+ @Id
+ @Column(name = "picture_id", length = 22)
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
+ @SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE, allocationSize = 1)
+ private Long pictureId;
+
+ // username from the person that shot the picture
+ @Column(name = "username")
+ private String username;
+
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "picture_date", nullable = false)
+ private Date pictureDate;
+
+ private String comment;
+
+ @Column(name = "image")
+ private String image;
+
+ public Long getPictureId() {
+ return pictureId;
+ }
+
+ public void setPictureId(Long pictureId) {
+ this.pictureId = pictureId;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Date getPictureDate() {
+ return pictureDate;
+ }
+
+ public void setPictureDate(Date pictureDate) {
+ this.pictureDate = pictureDate;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public String getImage() {
+ return image;
+ }
+
+ public void setImage(String image) {
+ this.image = image;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().append(pictureId).toHashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || this.getClass() != obj.getClass() || pictureId == null) {
+ return false;
+ }
+ return this.pictureId.equals(((Picture) obj).getPictureId());
+ }
+
+ public static class Builder {
+ private Picture instance = new Picture();
+
+ public Builder username(String username) {
+ instance.setUsername(username);
+ return this;
+ }
+
+ public Builder pictureDate(Date pictureDate) {
+ instance.setPictureDate(pictureDate);
+ return this;
+ }
+
+ public Builder comment(String comment) {
+ instance.setComment(comment);
+ return this;
+ }
+
+ public Builder image(String image) {
+ instance.setImage(image);
+ return this;
+ }
+
+ public Picture build() {
+ return instance;
+ }
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/Right.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/Right.java
new file mode 100644
index 0000000..a4e6645
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/Right.java
@@ -0,0 +1,95 @@
+package marketing.heyday.hartmann.fotodocumentation.core.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+import jakarta.persistence.*;
+
+/**
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+
+@Entity
+@Table(name = "x_right")
+public class Right extends AbstractDateEntity {
+ private static final long serialVersionUID = 1L;
+ public static final String SEQUENCE = "right_seq";
+
+ @Id
+ @Column(name = "right_id", length = 22)
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
+ @SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE, allocationSize = 1)
+ private Long rightId;
+
+ @Column(nullable = false, unique = true)
+ private String code;
+
+ @Column(nullable = false, unique = true)
+ private String name;
+
+ @ManyToMany(mappedBy = "rights")
+ private Set roles = new HashSet<>();
+
+ public Long getRightId() {
+ return rightId;
+ }
+
+ public void setRightId(Long rightId) {
+ this.rightId = rightId;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().append(code).toHashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || this.getClass() != obj.getClass()) {
+ return false;
+ }
+ return this.code.equals(((Right) obj).getCode());
+ }
+
+ public static class Builder {
+ private Right instance = new Right();
+
+ public Builder code(String code) {
+ instance.setCode(code);
+ return this;
+ }
+
+ public Builder name(String name) {
+ instance.setName(name);
+ return this;
+ }
+
+ public Right build() {
+ return instance;
+ }
+ }
+}
\ No newline at end of file
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/User.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/User.java
new file mode 100644
index 0000000..7b2b82d
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/model/User.java
@@ -0,0 +1,179 @@
+package marketing.heyday.hartmann.fotodocumentation.core.model;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import jakarta.persistence.*;
+
+/**
+ *
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+
+@Entity
+@Table(name = "x_user")
+public class User extends AbstractDateEntity {
+ private static final long serialVersionUID = 1L;
+ public static final String SEQUENCE = "user_seq";
+
+ @Id
+ @Column(name = "user_id", length = 22)
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
+ @SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE, allocationSize = 1)
+ private Long userId;
+
+ @Column(name = "username", unique = true, nullable = false)
+ private String username;
+
+ @Column(nullable = false)
+ private String password;
+
+ private String salt;
+
+ @Column(name = "title")
+ private String title;
+
+ @Column(nullable = false)
+ private String firstname;
+
+ @Column(nullable = false)
+ private String lastname;
+
+ @Column(nullable = false)
+ private String email;
+
+ @ManyToMany
+ @JoinTable(name = "user_to_right", joinColumns = { @JoinColumn(name = "user_id_fk") }, inverseJoinColumns = { @JoinColumn(name = "right_id_fk") })
+ private Set rights = new HashSet<>();
+
+ public Long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(Long userId) {
+ this.userId = userId;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getSalt() {
+ return salt;
+ }
+
+ public void setSalt(String salt) {
+ this.salt = salt;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getFirstname() {
+ return firstname;
+ }
+
+ public void setFirstname(String firstname) {
+ this.firstname = firstname;
+ }
+
+ public String getLastname() {
+ return lastname;
+ }
+
+ public void setLastname(String lastname) {
+ this.lastname = lastname;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public Set getRights() {
+ return rights;
+ }
+
+ public void setRights(Set rights) {
+ this.rights = rights;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().append(username).toHashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || this.getClass() != obj.getClass()) {
+ return false;
+ }
+ return this.username.equals(((User) obj).getUsername());
+ }
+
+ public static class Builder {
+ private User instance = new User();
+
+ public Builder email(String email) {
+ instance.setEmail(email);
+ return this;
+ }
+
+ public Builder firstname(String firstname) {
+ instance.setFirstname(firstname);
+ return this;
+ }
+
+ public Builder lastname(String lastname) {
+ instance.setLastname(lastname);
+ return this;
+ }
+
+ public Builder title(String title) {
+ instance.setTitle(title);
+ return this;
+ }
+
+ public Builder username(String username) {
+ instance.setUsername(username);
+ return this;
+ }
+
+ public Builder password(String password) {
+ instance.setPassword(password);
+ return this;
+ }
+
+ public User build() {
+ return instance;
+ }
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/query/Param.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/query/Param.java
new file mode 100644
index 0000000..a23998f
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/query/Param.java
@@ -0,0 +1,16 @@
+package marketing.heyday.hartmann.fotodocumentation.core.query;
+
+/**
+ *
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+
+public record Param(String name, Object value) {
+
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/query/QueryService.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/query/QueryService.java
new file mode 100644
index 0000000..01a1603
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/query/QueryService.java
@@ -0,0 +1,100 @@
+package marketing.heyday.hartmann.fotodocumentation.core.query;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Optional;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import jakarta.annotation.security.PermitAll;
+import jakarta.ejb.Stateless;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.NoResultException;
+import jakarta.persistence.PersistenceContext;
+import jakarta.persistence.Query;
+
+/**
+ *
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+@Stateless
+@SuppressWarnings("unchecked")
+@PermitAll
+public class QueryService {
+ private static final Log LOG = LogFactory.getLog(QueryService.class);
+
+ @PersistenceContext
+ private EntityManager eManager;
+
+ public int count(String namedQuery, Param... params) {
+ Query query = eManager.createNamedQuery(namedQuery);
+ for (Param param : params) {
+ query.setParameter(param.name(), param.value());
+ }
+ return ((Number) query.getSingleResult()).intValue();
+ }
+
+ public Optional callNamedQuerySingleResult(String namedQuery, Param... params) {
+ return singleResult(eManager.createNamedQuery(namedQuery), Arrays.asList(params));
+ }
+
+ private Optional singleResult(Query query, Collection params) {
+ try {
+ for (Param param : params) {
+ query.setParameter(param.name(), param.value());
+ }
+ return Optional.ofNullable((T) query.getSingleResult());
+ } catch (NoResultException nre) {
+ LOG.debug("No entity found for query " + query + " with params " + params);
+ LOG.trace("NoResultException", nre);
+ return Optional.empty();
+ }
+ }
+
+ public T callNamedQueryList(String namedQuery, Param... objects) {
+ Query query = eManager.createNamedQuery(namedQuery);
+ for (Param param : objects) {
+ query.setParameter(param.name(), param.value());
+ }
+ return (T) query.getResultList();
+ }
+
+ public Optional callQuerySingleResult(String sql, Param... objects) {
+ Query query = eManager.createQuery(sql);
+ for (Param param : objects) {
+ query.setParameter(param.name(), param.value());
+ }
+ return Optional.of((T) query.getSingleResult());
+ }
+
+ public T callNativeQuery(String sql, Param... objects) {
+ Query query = eManager.createNativeQuery(sql);
+ for (Param param : objects) {
+ query.setParameter(param.name(), param.value());
+ }
+ return (T) query.getResultList();
+ }
+
+ public int callNativeQueryUpdate(String sql, Param... objects) {
+ Query query = eManager.createNativeQuery(sql);
+ for (Param param : objects) {
+ query.setParameter(param.name(), param.value());
+ }
+ return query.executeUpdate();
+ }
+
+ public int callNamedQueryUpdate(String namedQuery, Param... objects) {
+ Query query = eManager.createNamedQuery(namedQuery);
+ for (Param param : objects) {
+ query.setParameter(param.name(), param.value());
+ }
+ return query.executeUpdate();
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/service/CustomerPictureService.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/service/CustomerPictureService.java
new file mode 100644
index 0000000..ab85205
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/service/CustomerPictureService.java
@@ -0,0 +1,48 @@
+package marketing.heyday.hartmann.fotodocumentation.core.service;
+
+import java.util.Optional;
+
+import jakarta.ejb.EJB;
+import jakarta.ejb.LocalBean;
+import jakarta.ejb.Stateless;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
+import marketing.heyday.hartmann.fotodocumentation.core.model.Customer;
+import marketing.heyday.hartmann.fotodocumentation.core.model.Picture;
+import marketing.heyday.hartmann.fotodocumentation.core.query.Param;
+import marketing.heyday.hartmann.fotodocumentation.core.query.QueryService;
+import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerPictureValue;
+
+/**
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+@Stateless
+@LocalBean
+public class CustomerPictureService {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ @EJB
+ private QueryService queryService;
+
+ public boolean addCustomerPicture(CustomerPictureValue customerPictureValue) {
+ Optional customerOpt = queryService.callNamedQuerySingleResult(Customer.FIND_BY_NUMBER, new Param(Customer.PARAM_NUMBER, customerPictureValue.customerNumber()));
+ Customer customer = customerOpt.orElseGet(() -> new Customer.Builder().customerNumber(customerPictureValue.customerNumber()).name(customerPictureValue.pharmacyName()).build());
+
+ Picture picture = new Picture.Builder().username(customerPictureValue.username()).comment(customerPictureValue.comment()).image(customerPictureValue.base64String()).pictureDate(customerPictureValue.date()).build();
+ customer.getPictures().add(picture);
+
+ entityManager.persist(picture);
+ entityManager.merge(customer);
+ entityManager.flush();
+ return true;
+ }
+
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureResource.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureResource.java
new file mode 100644
index 0000000..3a01182
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureResource.java
@@ -0,0 +1,49 @@
+package marketing.heyday.hartmann.fotodocumentation.rest;
+
+import org.jboss.resteasy.annotations.GZIP;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import jakarta.ejb.EJB;
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
+import marketing.heyday.hartmann.fotodocumentation.core.service.CustomerPictureService;
+import marketing.heyday.hartmann.fotodocumentation.rest.jackson.JsonSchemaValidate;
+import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerPictureValue;
+
+/**
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+@RequestScoped
+@Path("customer-picture")
+public class CustomerPictureResource {
+
+ @EJB
+ private CustomerPictureService customerPictureService;
+
+ @GZIP
+ @POST
+ @Path("")
+ //@Authenticate(shouldBeInAllRoles = RightUtils.ADMIN_RIGHT)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Operation(summary = "Add Customer Image to database")
+ @ApiResponse(responseCode = "200", description = "Add successfull")
+ public Response doAddCustomerPicture(@JsonSchemaValidate("schema/customer_picture_add.json") CustomerPictureValue customerPictureValue) {
+ boolean success = customerPictureService.addCustomerPicture(customerPictureValue);
+
+ return success ? Response.ok().build() : Response.status(Status.BAD_REQUEST).build();
+
+ }
+
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/MonitoringResource.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/MonitoringResource.java
new file mode 100644
index 0000000..d94812b
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/MonitoringResource.java
@@ -0,0 +1,36 @@
+package marketing.heyday.hartmann.fotodocumentation.rest;
+
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+
+/**
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 14 Nov 2024
+ */
+@Path("monitoring")
+@RequestScoped
+public class MonitoringResource {
+
+ @GET
+ @Path("check/{text}")
+ @Produces(MediaType.TEXT_PLAIN)
+ @Operation(summary = "Monitoring service for testing if the server is up and running.")
+ @ApiResponse(responseCode = "200", description = "ok with as body the given path param text. ")
+ public Response check(@PathParam("text") String text) {
+ return Response.status(Status.OK).entity(text).build();
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/ApplicationConfigApi.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/ApplicationConfigApi.java
new file mode 100644
index 0000000..0daffd7
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/ApplicationConfigApi.java
@@ -0,0 +1,51 @@
+package marketing.heyday.hartmann.fotodocumentation.rest.jackson;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
+import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+import io.swagger.v3.oas.annotations.info.Contact;
+import io.swagger.v3.oas.annotations.info.Info;
+import io.swagger.v3.oas.annotations.servers.Server;
+import jakarta.ws.rs.ApplicationPath;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import marketing.heyday.hartmann.fotodocumentation.rest.CustomerPictureResource;
+import marketing.heyday.hartmann.fotodocumentation.rest.MonitoringResource;
+
+/**
+ *
+ *
+ * Copyright: Copyright (c) 2017
+ * Company: heyday marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 18 Oct 2017
+ */
+@OpenAPIDefinition(info = @Info(title = "Hartmann Photo upload API", version = "1.0", description = "All available routes for the Hartmann Photo upload API", contact = @Contact(url = "https://localhost", name = "Patrick Verboom", email = "p.verboom@heyday.marketing")), servers = {
+ @Server(description = "development", url = "http://localhost"),
+ @Server(description = "integration", url = "http://localhost"),
+ @Server(description = "production", url = "http://localhost")
+})
+@ApplicationPath("/api")
+public class ApplicationConfigApi extends Application {
+ private static final Log LOG = LogFactory.getLog(ApplicationConfigApi.class);
+ public static final String JSON_OUT = MediaType.APPLICATION_JSON + "; charset=utf-8";
+
+ @Override
+ public Set> getClasses() {
+ Set> retVal = new HashSet<>();
+ retVal.add(OpenApiResource.class);
+ retVal.add(ValidatedMessageBodyReader.class);
+ //retVal.add(AuthenticateFilter.class);
+ retVal.add(MonitoringResource.class);
+ retVal.add(CustomerPictureResource.class);
+ LOG.info("returning rest api classes " + retVal);
+ return retVal;
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/JsonSchemaValidate.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/JsonSchemaValidate.java
new file mode 100644
index 0000000..d186afc
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/JsonSchemaValidate.java
@@ -0,0 +1,21 @@
+package marketing.heyday.hartmann.fotodocumentation.rest.jackson;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * Copyright: Copyright (c) 2017
+ * Company: heyday marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: Feb 10, 2017
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(value={ElementType.PARAMETER})
+public @interface JsonSchemaValidate {
+ String value();
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/SchemaValidated.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/SchemaValidated.java
new file mode 100644
index 0000000..4b1d440
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/SchemaValidated.java
@@ -0,0 +1,14 @@
+package marketing.heyday.hartmann.fotodocumentation.rest.jackson;
+
+/**
+ *
+ * Copyright: Copyright (c) 2017
+ * Company: heyday marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: Feb 10, 2017
+ */
+public interface SchemaValidated {
+
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/ValidatedMessageBodyReader.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/ValidatedMessageBodyReader.java
new file mode 100644
index 0000000..72e6849
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/ValidatedMessageBodyReader.java
@@ -0,0 +1,114 @@
+package marketing.heyday.hartmann.fotodocumentation.rest.jackson;
+
+import java.io.*;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedMap;
+import jakarta.ws.rs.ext.MessageBodyReader;
+import jakarta.ws.rs.ext.Provider;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.ValidationMessage;
+
+/**
+ *
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 6 Dec 2024
+ */
+@Provider
+@Consumes(value = {
+ MediaType.APPLICATION_JSON, "application/json; charset=utf-8"
+})
+public class ValidatedMessageBodyReader implements MessageBodyReader {
+ private static final Log LOG = LogFactory.getLog(ValidatedMessageBodyReader.class);
+
+ /* (non-Javadoc)
+ * @see jakarta.ws.rs.ext.MessageBodyReader#isReadable(java.lang.Class, java.lang.reflect.Type, java.lang.annotation.Annotation[], jakarta.ws.rs.core.MediaType)
+ */
+ @Override
+ public boolean isReadable(Class> classType, Type type, Annotation[] annotations, MediaType mediaType) {
+ if (mediaType.getType().contains("application/json")) {
+ return false;
+ }
+ return Arrays.stream(annotations).anyMatch(a -> a.annotationType() == JsonSchemaValidate.class);
+ }
+
+ /* (non-Javadoc)
+ * @see jakarta.ws.rs.ext.MessageBodyReader#readFrom(java.lang.Class, java.lang.reflect.Type, java.lang.annotation.Annotation[], jakarta.ws.rs.core.MediaType, jakarta.ws.rs.core.MultivaluedMap, java.io.InputStream)
+ */
+ @Override
+ public SchemaValidated readFrom(Class classType, Type type, Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap httpHeaders, InputStream input) throws IOException {
+ final String jsonData = read(input);
+
+ Optional annotation = Arrays.stream(annotations).filter(a -> a.annotationType() == JsonSchemaValidate.class).map(a -> (JsonSchemaValidate) a).findAny();
+ if (annotation.isPresent()) {
+ ValidationReply reply = validate(annotation.get(), jsonData);
+ if (!reply.success) {
+ throw new WebApplicationException(reply.getErrorResponse());
+ }
+ }
+
+ ObjectMapper objectMapper = new ObjectMapper();
+ return objectMapper.readValue(new StringReader(jsonData), classType);
+ }
+
+ /**
+ * @param jsonSchema
+ * @param jsonData
+ * @return
+ */
+ private ValidationReply validate(JsonSchemaValidate jsonSchema, String jsonData) {
+ String schemaPath = jsonSchema.value();
+ try {
+ JsonSchema schema = getJsonSchema(schemaPath);
+ JsonNode node = getJsonNode(jsonData);
+ Set errors = schema.validate(node);
+ if (!errors.isEmpty()) {
+ LOG.error("Failed to validate json to schema " + schemaPath);
+ errors.stream().forEach(LOG::error);
+ }
+ return new ValidationReply.Builder().success(errors.isEmpty()).errors(errors).build();
+ } catch (IOException e) {
+ LOG.error(e.getMessage(), e);
+ return new ValidationReply.Builder().success(false).build();
+ }
+ }
+
+ protected JsonSchema getJsonSchema(String name) throws IOException {
+ JsonSchemaFactory factory = new JsonSchemaFactory();
+ try (InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(name);) {
+ return factory.getSchema(input);
+ }
+ }
+
+ protected JsonNode getJsonNode(String content) throws IOException {
+ return new ObjectMapper().readTree(content);
+ }
+
+ private String read(InputStream input) throws IOException {
+ try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input, Charset.forName("UTF-8")))) {
+ return buffer.lines().collect(Collectors.joining("\n"));
+ }
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/ValidationReply.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/ValidationReply.java
new file mode 100644
index 0000000..0fb23b0
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/jackson/ValidationReply.java
@@ -0,0 +1,51 @@
+package marketing.heyday.hartmann.fotodocumentation.rest.jackson;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
+
+import com.networknt.schema.ValidationMessage;
+
+/**
+ *
+ * Copyright: Copyright (c) 2017
+ * Company: heyday marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: Feb 14, 2017
+ */
+public final class ValidationReply {
+ public final boolean success;
+ public final Set errors = new HashSet<>();
+
+ private ValidationReply(boolean success, Set errors) {
+ this.success = success;
+ this.errors.addAll(errors);
+ }
+
+ public Response getErrorResponse() {
+ return Response.status(Status.BAD_REQUEST).entity(errors).build();
+ }
+
+ public static final class Builder {
+ private boolean success;
+ private Set errors = new HashSet<>();
+
+ public Builder success(boolean success) {
+ this.success = success;
+ return this;
+ }
+
+ public Builder errors(Set errors) {
+ this.errors = errors;
+ return this;
+ }
+
+ public ValidationReply build() {
+ return new ValidationReply(success, errors);
+ }
+ }
+}
diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/vo/CustomerPictureValue.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/vo/CustomerPictureValue.java
new file mode 100644
index 0000000..0aa212e
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/rest/vo/CustomerPictureValue.java
@@ -0,0 +1,17 @@
+package marketing.heyday.hartmann.fotodocumentation.rest.vo;
+
+import java.util.Date;
+
+/**
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 19 Jan 2026
+ */
+
+public record CustomerPictureValue(String username, String pharmacyName, String customerNumber, Date date, String comment, String base64String) {
+
+}
diff --git a/hartmann-foto-documentation-app/src/main/resources/META-INF/beans.xml b/hartmann-foto-documentation-app/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..be79900
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,6 @@
+
+
+
\ No newline at end of file
diff --git a/hartmann-foto-documentation-app/src/main/resources/META-INF/persistence.xml b/hartmann-foto-documentation-app/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..06ffc00
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,24 @@
+
+
+
+
+ java:/jdbc/fotoDocumentationDS
+
+ marketing.heyday.hartmann.fotodocumentation.core.model.Right
+ marketing.heyday.hartmann.fotodocumentation.core.model.User
+ marketing.heyday.hartmann.fotodocumentation.core.model.Customer
+ marketing.heyday.hartmann.fotodocumentation.core.model.Picture
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hartmann-foto-documentation-app/src/main/resources/marketing/heyday/hartmann/fotodocumentation/core/db/migration/V1__init.sql b/hartmann-foto-documentation-app/src/main/resources/marketing/heyday/hartmann/fotodocumentation/core/db/migration/V1__init.sql
new file mode 100644
index 0000000..c64fd2c
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/resources/marketing/heyday/hartmann/fotodocumentation/core/db/migration/V1__init.sql
@@ -0,0 +1,76 @@
+
+-- Right
+
+create sequence right_seq start 25;
+
+create table x_right (
+ right_id bigint PRIMARY KEY,
+ code varchar(50) NOT NULL,
+ name varchar(150) NOT NULL,
+ jpa_active boolean NOT NULL,
+ jpa_created timestamp NOT NULL,
+ jpa_updated timestamp NOT NULL,
+ jpa_version integer NOT NULL,
+ CONSTRAINT unq_x_right_code UNIQUE(code)
+);
+
+
+-- user
+
+create sequence user_seq start 25;
+
+create table x_user (
+ user_id bigint PRIMARY KEY,
+ username varchar(150) NOT NULL,
+ password varchar(150) NOT NULL,
+ salt varchar(150) NOT NULL,
+ title varchar(15) ,
+ firstname varchar(150) NOT NULL,
+ lastname varchar(150) NOT NULL,
+ email varchar(150) NOT NULL,
+ jpa_active boolean NOT NULL,
+ jpa_created timestamp NOT NULL,
+ jpa_updated timestamp NOT NULL,
+ jpa_version integer NOT NULL,
+ CONSTRAINT unq_x_user_username UNIQUE(username)
+);
+
+create table user_to_right (
+ user_id_fk bigint REFERENCES x_user,
+ right_id_fk bigint REFERENCES x_right,
+ PRIMARY KEY(user_id_fk, right_id_fk)
+);
+
+
+-- customer
+
+create sequence customer_seq start 25;
+
+create table customer (
+ customer_id bigint PRIMARY KEY,
+ customer_number varchar(150) NOT NULL,
+ name varchar(150) NOT NULL,
+ jpa_active boolean NOT NULL,
+ jpa_created timestamp NOT NULL,
+ jpa_updated timestamp NOT NULL,
+ jpa_version integer NOT NULL,
+ CONSTRAINT unq_customer_number UNIQUE(customer_number)
+);
+
+
+-- picture
+
+create sequence picture_seq start 25;
+
+create table picture (
+ picture_id bigint PRIMARY KEY,
+ username varchar(150),
+ picture_date timestamp NOT NULL,
+ comment TEXT,
+ image TEXT,
+ jpa_active boolean NOT NULL,
+ jpa_created timestamp NOT NULL,
+ jpa_updated timestamp NOT NULL,
+ jpa_version integer NOT NULL,
+ customer_id_fk bigint REFERENCES customer
+);
\ No newline at end of file
diff --git a/hartmann-foto-documentation-app/src/main/resources/schema/customer_picture_add.json b/hartmann-foto-documentation-app/src/main/resources/schema/customer_picture_add.json
new file mode 100644
index 0000000..f75fa66
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/main/resources/schema/customer_picture_add.json
@@ -0,0 +1,40 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "Add Customer Picture",
+ "description": "Add a Customer Picture to the system",
+ "type": "object",
+ "properties": {
+ "username": {
+ "description": "The username from the user who uploads the picture",
+ "type": "string"
+ },
+ "pharmacyName": {
+ "description": "The Name from the pharmacy customer ",
+ "type": "string"
+ },
+ "customerNumber": {
+ "description": "The unique number from the pharmacy customer ",
+ "type": "string"
+ },
+ "date": {
+ "description": "The date when the picture is taken ",
+ "type": "string"
+ },
+ "comment": {
+ "description": "A free text comment field ",
+ "type": "string"
+ },
+ "base64String": {
+ "description": "The Picture content as base64 ",
+ "type": "string"
+ }
+ },
+ "required": [
+ "username",
+ "pharmacyName",
+ "customerNumber",
+ "date",
+ "comment",
+ "base64String"
+ ]
+}
\ No newline at end of file
diff --git a/hartmann-foto-documentation-app/src/site/securityDefinitions.json b/hartmann-foto-documentation-app/src/site/securityDefinitions.json
new file mode 100644
index 0000000..67858dc
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/site/securityDefinitions.json
@@ -0,0 +1,16 @@
+{
+ "api_key": {
+ "type": "apiKey",
+ "name": "api_key",
+ "in": "header"
+ },
+ "petstore_auth": {
+ "type": "oauth2",
+ "authorizationUrl": "http://swagger.io/api/oauth/dialog",
+ "flow": "implicit",
+ "scopes": {
+ "write:pets": "modify pets in your account",
+ "read:pets": "read your pets"
+ }
+ }
+}
\ No newline at end of file
diff --git a/hartmann-foto-documentation-app/src/site/templates/markdown.hbs b/hartmann-foto-documentation-app/src/site/templates/markdown.hbs
new file mode 100644
index 0000000..8c43e3b
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/site/templates/markdown.hbs
@@ -0,0 +1,107 @@
+#{{#info}}{{title}}
+
+
+## {{join schemes " | "}}://{{host}}{{basePath}}
+
+
+{{description}}
+
+{{#contact}}
+[**Contact the developer**](mailto:{{email}})
+{{/contact}}
+
+**Version** {{version}}
+
+[**Terms of Service**]({{termsOfService}})
+
+{{#license}}[**{{name}}**]({{url}}){{/license}}
+
+{{/info}}
+
+{{#if consumes}}**Consumes:** {{join consumes ", "}}{{/if}}
+
+{{#if produces}}**Produces:** {{join produces ", "}}{{/if}}
+
+{{#if securityDefinitions}}
+# Security Definitions
+{{/if}}
+{{> security}}
+
+# APIs
+
+{{#each paths}}
+## {{@key}}
+{{#this}}
+{{#get}}
+### GET
+{{> operation}}
+{{/get}}
+
+{{#put}}
+### PUT
+{{> operation}}
+{{/put}}
+
+{{#post}}
+### POST
+
+{{> operation}}
+
+{{/post}}
+
+{{#delete}}
+### DELETE
+{{> operation}}
+{{/delete}}
+
+{{#option}}
+### OPTION
+{{> operation}}
+{{/option}}
+
+{{#patch}}
+### PATCH
+{{> operation}}
+{{/patch}}
+
+{{#head}}
+### HEAD
+{{> operation}}
+{{/head}}
+
+{{/this}}
+{{/each}}
+
+# Definitions
+{{#each definitions}}
+## {{@key}}
+
+
+
+ | name |
+ type |
+ required |
+ description |
+ example |
+
+ {{#each this.properties}}
+
+ | {{@key}} |
+
+ {{#ifeq type "array"}}
+ {{#items.$ref}}
+ {{type}}[{{basename items.$ref}}]
+ {{/items.$ref}}
+ {{^items.$ref}}{{type}}[{{items.type}}]{{/items.$ref}}
+ {{else}}
+ {{#$ref}}{{basename $ref}}{{/$ref}}
+ {{^$ref}}{{type}}{{#format}} ({{format}}){{/format}}{{/$ref}}
+ {{/ifeq}}
+ |
+ {{#required}}required{{/required}}{{^required}}optional{{/required}} |
+ {{#description}}{{{description}}}{{/description}}{{^description}}-{{/description}} |
+ {{example}} |
+
+ {{/each}}
+
+{{/each}}
diff --git a/hartmann-foto-documentation-app/src/site/templates/operation.hbs b/hartmann-foto-documentation-app/src/site/templates/operation.hbs
new file mode 100644
index 0000000..b036fbf
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/site/templates/operation.hbs
@@ -0,0 +1,81 @@
+{{#deprecated}}-deprecated-{{/deprecated}}
+{{summary}}
+
+{{{description}}}
+
+{{#if externalDocs.url}}{{externalDocs.description}}. [See external documents for more details]({{externalDocs.url}})
+{{/if}}
+
+{{#if security}}
+#### Security
+{{/if}}
+
+{{#security}}
+{{#each this}}
+* {{@key}}
+{{#this}} * {{this}}
+{{/this}}
+{{/each}}
+{{/security}}
+
+#### Request
+
+{{#if consumes}}
+**Content-Type: ** {{join consumes ", "}}{{/if}}
+
+##### Parameters
+{{#if parameters}}
+
+
+ | Name |
+ Located in |
+ Required |
+ Description |
+ Default |
+ Schema |
+ Example |
+
+{{/if}}
+
+{{#parameters}}
+
+ | {{name}} |
+ {{in}} |
+ {{#if required}}yes{{else}}no{{/if}} |
+ {{description}}{{#if pattern}} (**Pattern**: `{{pattern}}`){{/if}} |
+ - |
+{{#ifeq in "body"}}
+
+ {{#ifeq schema.type "array"}}Array[{{basename schema.items.$ref}}]{{/ifeq}}
+ {{#schema.$ref}}{{basename schema.$ref}} {{/schema.$ref}}
+ |
+{{else}}
+ {{#ifeq type "array"}}
+ Array[{{items.type}}] ({{collectionFormat}}) |
+ {{else}}
+ {{type}} {{#format}}({{format}}){{/format}} |
+ {{/ifeq}}
+{{/ifeq}}
+
+{{#each examples}}
+ {{{this}}}
+{{/each}}
+ |
+
+
+
+{{/parameters}}
+{{#if parameters}}
+
+{{/if}}
+
+
+#### Response
+
+{{#if produces}}**Content-Type: ** {{join produces ", "}}{{/if}}
+
+
+| Status Code | Reason | Response Model |
+|-------------|-------------|----------------|
+{{#each responses}}| {{@key}} | {{description}} | {{#schema.$ref}}{{basename schema.$ref}}{{/schema.$ref}}{{^schema.$ref}}{{#ifeq schema.type "array"}}Array[{{basename schema.items.$ref}}]{{else}}{{schema.type}}{{/ifeq}}{{/schema.$ref}}{{^schema}} - {{/schema}}|
+{{/each}}
\ No newline at end of file
diff --git a/hartmann-foto-documentation-app/src/site/templates/security.hbs b/hartmann-foto-documentation-app/src/site/templates/security.hbs
new file mode 100644
index 0000000..04f86e8
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/site/templates/security.hbs
@@ -0,0 +1,88 @@
+{{#each securityDefinitions}}
+### {{@key}}
+{{#this}}
+{{#ifeq type "oauth2"}}
+
+
+ | type |
+ {{type}} |
+
+{{#if description}}
+
+ | description |
+ {{description}} |
+
+{{/if}}
+{{#if authorizationUrl}}
+
+ | authorizationUrl |
+ {{authorizationUrl}} |
+
+{{/if}}
+{{#if flow}}
+
+ | flow |
+ {{flow}} |
+
+{{/if}}
+{{#if tokenUrl}}
+
+ | tokenUrl |
+ {{tokenUrl}} |
+
+{{/if}}
+{{#if scopes}}
+
+ | scopes |
+{{#each scopes}}
+ {{@key}} |
+ {{this}} |
+
+
+{{/each}}
+
+{{/if}}
+
+{{/ifeq}}
+{{#ifeq type "apiKey"}}
+
+
+ | type |
+ {{type}} |
+
+{{#if description}}
+
+ | description |
+ {{description}} |
+
+{{/if}}
+{{#if name}}
+
+ | name |
+ {{name}} |
+
+{{/if}}
+{{#if in}}
+
+ | in |
+ {{in}} |
+
+{{/if}}
+
+{{/ifeq}}
+{{#ifeq type "basic"}}
+
+
+ | type |
+ {{type}} |
+
+{{#if description}}
+
+ | description |
+ {{description}} |
+
+{{/if}}
+
+{{/ifeq}}
+{{/this}}
+{{/each}}
\ No newline at end of file
diff --git a/hartmann-foto-documentation-app/src/site/templates/strapdown.html.hbs b/hartmann-foto-documentation-app/src/site/templates/strapdown.html.hbs
new file mode 100644
index 0000000..82e6970
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/site/templates/strapdown.html.hbs
@@ -0,0 +1,16 @@
+
+
+API Document
+
+
+{{>markdown}}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hartmann-foto-documentation-app/src/test/java/org/mockito/configuration/MockitoConfiguration.java b/hartmann-foto-documentation-app/src/test/java/org/mockito/configuration/MockitoConfiguration.java
new file mode 100644
index 0000000..818934c
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/test/java/org/mockito/configuration/MockitoConfiguration.java
@@ -0,0 +1,19 @@
+package org.mockito.configuration;
+
+/**
+ *
+ * Copyright: Copyright (c) 2016
+ * Company: heyday marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: Oct 14, 2016
+ */
+public class MockitoConfiguration extends DefaultMockitoConfiguration {
+
+ @Override
+ public boolean enableClassCache() {
+ return false;
+ }
+
+}
diff --git a/hartmann-foto-documentation-app/src/test/resources/log4j.xml b/hartmann-foto-documentation-app/src/test/resources/log4j.xml
new file mode 100644
index 0000000..95bea55
--- /dev/null
+++ b/hartmann-foto-documentation-app/src/test/resources/log4j.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hartmann-foto-documentation-docker/pom.xml b/hartmann-foto-documentation-docker/pom.xml
new file mode 100644
index 0000000..42ea3b0
--- /dev/null
+++ b/hartmann-foto-documentation-docker/pom.xml
@@ -0,0 +1,530 @@
+
+
+ 4.0.0
+
+ marketing.heyday.hartmann.fotodocumentation
+ hartmann-foto-documentation
+ 1.0.0
+ ../hartmann-foto-documentation/pom.xml
+
+ hartmann-foto-documentation-docker
+ 1.0.0-SNAPSHOT
+ hartmann-foto-documentation docker
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+
+ true
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M1
+
+ true
+ true
+
+
+
+
+
+ ${project.build.directory}/lib/*.jar
+ ${project.build.directory}/lib/*.jar
+
+
+
+ docker
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+ 3.1
+
+ true
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M1
+
+ true
+ true
+ true
+
+
+
+ integration-test
+
+ test
+
+ integration-test
+
+ false
+ false
+
+ once
+
+
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+ 0.45.1
+ true
+
+ true
+ false
+ true
+ true
+
+
+ hartmann_postgres
+ postgres:11
+
+
+ 5430:5432
+
+
+ hartmann_nw
+ hartmann_postgres
+
+
+ trust
+
+
+
+ ${basedir}/src/main/docker/sql:/docker-entrypoint-initdb.d
+
+
+
+ database system is ready to accept connections
+
+
+
+
+
+
+
+ ${project.artifactId}
+ hartmann
+
+ Dockerfile
+
+ maven
+
+
+ latest
+
+
+ hub.heyday.marketing
+
+
+ /srv/wait-for-it.sh
+ hartmann_postgres:5432" --
+ /srv/wildfly/bin/standalone-jacoco.sh
+ -b 0.0.0.0 -c test-standalone.xml
+
+
+ 8180:8080
+
+
+ hartmann_postgres
+
+
+
+ hartmann_nw
+ hartmann
+
+
+
+ ${basedir}/target:/srv/target
+
+
+
+
+
+ http://${docker.host.address}:8180/api/monitoring/check/hello
+
+
+
+
+
+
+
+
+
+ build
+ package
+
+ build
+
+
+
+
+ start
+ pre-integration-test
+
+ start
+
+
+
+ stop
+ post-integration-test
+
+ stop
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.0.2
+
+
+ copy-resources
+
+ validate
+
+ copy-resources
+
+
+ ${basedir}/src/main/docker
+
+
+ ${basedir}/../hartmann-foto-documentation-web/target/
+
+ *.war
+
+
+
+
+
+
+ copy-resources2
+
+ package
+
+ copy-resources
+
+
+ ${project.basedir}/target/classes
+
+
+ ${project.basedir}/../hartmann-foto-documentation-app/target/classes
+ false
+
+
+
+
+
+
+ copy-jacoco-app
+
+ verify
+
+ copy-resources
+
+
+ ${project.basedir}/../hartmann-foto-documentation-app/target
+
+
+ ${project.basedir}/target
+ false
+
+ jacoco-it.exec
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.seleniumhq.selenium
+ selenium-java
+ 3.141.59
+ test
+
+
+ org.seleniumhq.selenium
+ selenium-edge-driver
+
+
+ org.seleniumhq.selenium
+ selenium-firefox-driver
+
+
+ org.seleniumhq.selenium
+ selenium-ie-driver
+
+
+ org.seleniumhq.selenium
+ selenium-opera-driver
+
+
+ org.seleniumhq.selenium
+ selenium-safari-driver
+
+
+
+
+
+ org.seleniumhq.selenium
+ selenium-api
+ 3.141.59
+ test
+
+
+ org.seleniumhq.selenium
+ selenium-chrome-driver
+ 3.141.59
+ test
+
+
+
+ org.seleniumhq.selenium
+ selenium-remote-driver
+ 3.141.59
+ test
+
+
+ org.seleniumhq.selenium
+ selenium-support
+ 3.141.59
+ test
+
+
+
+ ${project.groupId}
+ hartmann-foto-documentation-web
+ ${project.version}
+ war
+
+
+
+ ${project.groupId}
+ hartmann-foto-documentation-app
+ ${project.version}
+
+
+
+
+
+ javax.websocket
+ javax.websocket-client-api
+ 1.0
+ test
+
+
+
+ org.glassfish.tyrus
+ tyrus-client
+ 1.1
+ test
+
+
+ org.glassfish.tyrus
+ tyrus-container-grizzly
+ 1.1
+ test
+
+
+ javax.json
+ javax.json-api
+ 1.0
+ test
+
+
+ org.glassfish
+ javax.json
+ 1.1
+ test
+
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${version.commons-lang3}
+ test
+
+
+
+ org.jacoco
+ org.jacoco.core
+ test
+
+
+
+ org.dbunit
+ dbunit
+ 2.7.0
+ test
+
+
+ junit
+ junit
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+
+ org.jboss.resteasy
+ resteasy-jackson2-provider
+ ${version.org.jboss.resteasy}
+ provided
+
+
+
+ org.codehaus.jackson
+ jackson-core-asl
+ 1.9.13
+ test
+
+
+
+ org.codehaus.jackson
+ jackson-mapper-asl
+ 1.9.13
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+
+ org.junit.platform
+ junit-platform-suite-engine
+ test
+
+
+
+ org.junit.platform
+ junit-platform-suite-api
+ test
+
+
+
+ org.junit.platform
+ junit-platform-commons
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.junit.platform
+ junit-platform-launcher
+ test
+
+
+
+ commons-io
+ commons-io
+ ${version.commons-io}
+ test
+
+
+ commons-logging
+ commons-logging
+ ${version.commons-logging}
+ test
+
+
+ org.apache.httpcomponents
+ fluent-hc
+ 4.5.1
+ test
+
+
+ org.apache.httpcomponents
+ httpmime
+ 4.5.1
+ test
+
+
+ log4j
+ log4j
+ 1.2.15
+ test
+
+
+ javax.mail
+ mail
+
+
+ javax.jms
+ jms
+
+
+ com.sun.jdmk
+ jmxtools
+
+
+ com.sun.jmx
+ jmxri
+
+
+
+
+
+ org.xmlunit
+ xmlunit-core
+ 2.8.2
+ test
+
+
+
+ org.skyscreamer
+ jsonassert
+ 1.2.2
+ test
+
+
+ org.postgresql
+ postgresql
+ test
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.5
+ test
+
+
+
diff --git a/hartmann-foto-documentation-docker/src/main/docker/Dockerfile b/hartmann-foto-documentation-docker/src/main/docker/Dockerfile
new file mode 100644
index 0000000..cac8acd
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/main/docker/Dockerfile
@@ -0,0 +1,52 @@
+############################################################
+# Dockerfile to build wildfly Hartmann Foto Documentation docker image
+# Based on jboss/wildfly
+############################################################
+
+# Pull base image.
+FROM hub.heyday.marketing/verboomp/wildfly:33.0.2-Final
+
+# File Author / Maintainer
+LABEL org.opencontainers.image.authors="Patrick Verboom "
+
+USER root
+
+RUN apt-get update && apt-get install -y unzip
+
+# install the postgres driver
+RUN wget 'https://nexus.heyday.marketing/repository/maven-releases/com/ayeqbenu/wildfly/database/modules/postgres-jdbc/1.1.0/postgres-jdbc-1.1.0.zip' -O /srv/postgres-jdbc.zip \
+ && unzip /srv/postgres-jdbc.zip -d ${JBOSS_HOME}/modules \
+ && rm -rf /srv/postgres-jdbc.zip
+
+
+# install the startup wait script to make sure postgres is booted before wildfly
+COPY wait-for-it.sh /srv/wait-for-it.sh
+
+RUN chmod 777 /srv/wait-for-it.sh
+
+# copy the jacoco agent for code coverage
+COPY jacoco/jacocoagent.jar /srv/jacocoagent.jar
+
+run chmod 777 /srv/jacocoagent.jar
+
+COPY standalone-jacoco.sh ${JBOSS_HOME}/bin/standalone-jacoco.sh
+
+RUN chmod 777 ${JBOSS_HOME}/bin/standalone-jacoco.sh
+
+
+# install the wildfly config xml file
+# COPY test-standalone.xml ${JBOSS_HOME}/standalone/configuration/test-standalone.xml
+COPY standalone-fotodocumentation.xml ${JBOSS_HOME}/standalone/configuration/test-standalone.xml
+
+
+# install the web war file
+COPY hartmann-foto-documentation-web-*.war ${JBOSS_HOME}/standalone/deployments
+
+# Expose the ports we're interested in
+EXPOSE 8080
+
+EXPOSE 9990
+
+# Set the default command to run on boot
+# This will boot WildFly in the standalone mode and bind to all interface
+CMD ["/srv/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-c", "test-standalone.xml"]
diff --git a/hartmann-foto-documentation-docker/src/main/docker/docker-compose.yml b/hartmann-foto-documentation-docker/src/main/docker/docker-compose.yml
new file mode 100644
index 0000000..afef13c
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/main/docker/docker-compose.yml
@@ -0,0 +1,40 @@
+
+services:
+
+ smtp-server:
+ image: andreptb/smtp-server-for-it:0.2.0
+ ports:
+ - "8280:8080"
+ networks:
+ - hartmann_nw
+ environment:
+ - smtp.port=26
+
+ hartmann_postgres:
+ image: postgres:11
+ volumes:
+ - ./sql:/docker-entrypoint-initdb.d
+ ports:
+ - "5430:5432"
+ networks:
+ - hartmann_nw
+ environment:
+ - POSTGRES_HOST_AUTH_METHOD=trust
+
+ hartmann:
+ build: .
+ ports:
+ - "8180:8080"
+ - "9990:9990"
+ depends_on:
+ - hartmann_postgres
+ networks:
+ - hartmann_nw
+ command: ["/srv/wait-for-it.sh", "cue_postgres:5432", "--", "/srv/wildfly/bin/standalone-jacoco.sh", "-b", "0.0.0.0", "-c", "test-standalone.xml"]
+ volumes:
+ - ./../../../target:/srv/target:z
+
+networks:
+ hartmann_nw:
+volumes:
+ jacoco:
diff --git a/hartmann-foto-documentation-docker/src/main/docker/hartmann-foto-documentation-web-1.0.0-SNAPSHOT.war b/hartmann-foto-documentation-docker/src/main/docker/hartmann-foto-documentation-web-1.0.0-SNAPSHOT.war
new file mode 100644
index 0000000..6b21898
Binary files /dev/null and b/hartmann-foto-documentation-docker/src/main/docker/hartmann-foto-documentation-web-1.0.0-SNAPSHOT.war differ
diff --git a/hartmann-foto-documentation-docker/src/main/docker/jacoco/jacocoagent.jar b/hartmann-foto-documentation-docker/src/main/docker/jacoco/jacocoagent.jar
new file mode 100644
index 0000000..e3c7d7d
Binary files /dev/null and b/hartmann-foto-documentation-docker/src/main/docker/jacoco/jacocoagent.jar differ
diff --git a/hartmann-foto-documentation-docker/src/main/docker/sql/init_test.sql b/hartmann-foto-documentation-docker/src/main/docker/sql/init_test.sql
new file mode 100644
index 0000000..7554e1b
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/main/docker/sql/init_test.sql
@@ -0,0 +1,80 @@
+--
+-- PostgreSQL database dump
+--
+
+-- Dumped from database version 9.4.5
+-- Dumped by pg_dump version 9.6.5
+
+SET statement_timeout = 0;
+SET lock_timeout = 0;
+SET idle_in_transaction_session_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = on;
+SET check_function_bodies = false;
+SET client_min_messages = warning;
+SET row_security = off;
+
+--
+-- Name: fotodocumentation; Type: DATABASE; Schema: -; Owner: postgres
+--
+
+CREATE ROLE fotodocumentation WITH
+ LOGIN
+ NOSUPERUSER
+ NOCREATEDB
+ NOCREATEROLE
+ INHERIT
+ NOREPLICATION
+ CONNECTION LIMIT -1
+ PASSWORD 'fotodocumentation';
+
+CREATE DATABASE fotodocumentation WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8';
+
+
+ALTER DATABASE fotodocumentation OWNER TO postgres;
+
+\connect fotodocumentation
+
+SET statement_timeout = 0;
+SET lock_timeout = 0;
+SET idle_in_transaction_session_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = on;
+SET check_function_bodies = false;
+SET client_min_messages = warning;
+SET row_security = off;
+
+--
+-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner:
+--
+
+CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
+
+
+--
+-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
+--
+
+COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
+
+
+SET search_path = public, pg_catalog;
+
+SET default_tablespace = '';
+
+SET default_with_oids = false;
+
+
+--
+-- Name: public; Type: ACL; Schema: -; Owner: postgres
+--
+
+REVOKE ALL ON SCHEMA public FROM PUBLIC;
+REVOKE ALL ON SCHEMA public FROM postgres;
+GRANT ALL ON SCHEMA public TO postgres;
+GRANT ALL ON SCHEMA public TO PUBLIC;
+
+
+--
+-- PostgreSQL database dump complete
+--
diff --git a/hartmann-foto-documentation-docker/src/main/docker/standalone-fotodocumentation.xml b/hartmann-foto-documentation-docker/src/main/docker/standalone-fotodocumentation.xml
new file mode 100644
index 0000000..80c65ef
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/main/docker/standalone-fotodocumentation.xml
@@ -0,0 +1,618 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ jdbc:postgresql://hartmann_postgres:5432/fotodocumentation
+ postgres
+ TRANSACTION_READ_COMMITTED
+
+ 1
+ 10
+
+
+
+
+
+
+
+ 32
+ true
+
+
+
+ jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=${wildfly.h2.compatibility.mode:REGULAR}
+ h2
+
+
+
+
+ org.postgresql.xa.PGXADataSource
+
+
+ org.h2.jdbcx.JdbcDataSource
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${jboss.bind.address:127.0.0.1}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hartmann-foto-documentation-docker/src/main/docker/standalone-jacoco.sh b/hartmann-foto-documentation-docker/src/main/docker/standalone-jacoco.sh
new file mode 100644
index 0000000..83b66cb
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/main/docker/standalone-jacoco.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+export JAVA_OPTS="$JAVA_OPTS -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -javaagent:/srv/jacocoagent.jar=destfile=/srv/target/jacoco-it.exec,includes=marketing/heyday/hartmann/fotodocumentation/*,output=file "
+exec `dirname $0`/standalone.sh $@
\ No newline at end of file
diff --git a/hartmann-foto-documentation-docker/src/main/docker/wait-for-it.sh b/hartmann-foto-documentation-docker/src/main/docker/wait-for-it.sh
new file mode 100644
index 0000000..5b3a6f9
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/main/docker/wait-for-it.sh
@@ -0,0 +1,177 @@
+#!/usr/bin/env bash
+# Use this script to test if a given TCP host/port are available
+
+cmdname=$(basename $0)
+
+echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
+
+usage()
+{
+ cat << USAGE >&2
+Usage:
+ $cmdname host:port [-s] [-t timeout] [-- command args]
+ -h HOST | --host=HOST Host or IP under test
+ -p PORT | --port=PORT TCP port under test
+ Alternatively, you specify the host and port as host:port
+ -s | --strict Only execute subcommand if the test succeeds
+ -q | --quiet Don't output any status messages
+ -t TIMEOUT | --timeout=TIMEOUT
+ Timeout in seconds, zero for no timeout
+ -- COMMAND ARGS Execute command with args after the test finishes
+USAGE
+ exit 1
+}
+
+wait_for()
+{
+ if [[ $TIMEOUT -gt 0 ]]; then
+ echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT"
+ else
+ echoerr "$cmdname: waiting for $HOST:$PORT without a timeout"
+ fi
+ start_ts=$(date +%s)
+ while :
+ do
+ if [[ $ISBUSY -eq 1 ]]; then
+ nc -z $HOST $PORT
+ result=$?
+ else
+ (echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1
+ result=$?
+ fi
+ if [[ $result -eq 0 ]]; then
+ end_ts=$(date +%s)
+ echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds"
+ break
+ fi
+ sleep 1
+ done
+ return $result
+}
+
+wait_for_wrapper()
+{
+ # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
+ if [[ $QUIET -eq 1 ]]; then
+ timeout $BUSYTIMEFLAG $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
+ else
+ timeout $BUSYTIMEFLAG $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
+ fi
+ PID=$!
+ trap "kill -INT -$PID" INT
+ wait $PID
+ RESULT=$?
+ if [[ $RESULT -ne 0 ]]; then
+ echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT"
+ fi
+ return $RESULT
+}
+
+# process arguments
+while [[ $# -gt 0 ]]
+do
+ case "$1" in
+ *:* )
+ hostport=(${1//:/ })
+ HOST=${hostport[0]}
+ PORT=${hostport[1]}
+ shift 1
+ ;;
+ --child)
+ CHILD=1
+ shift 1
+ ;;
+ -q | --quiet)
+ QUIET=1
+ shift 1
+ ;;
+ -s | --strict)
+ STRICT=1
+ shift 1
+ ;;
+ -h)
+ HOST="$2"
+ if [[ $HOST == "" ]]; then break; fi
+ shift 2
+ ;;
+ --host=*)
+ HOST="${1#*=}"
+ shift 1
+ ;;
+ -p)
+ PORT="$2"
+ if [[ $PORT == "" ]]; then break; fi
+ shift 2
+ ;;
+ --port=*)
+ PORT="${1#*=}"
+ shift 1
+ ;;
+ -t)
+ TIMEOUT="$2"
+ if [[ $TIMEOUT == "" ]]; then break; fi
+ shift 2
+ ;;
+ --timeout=*)
+ TIMEOUT="${1#*=}"
+ shift 1
+ ;;
+ --)
+ shift
+ CLI=("$@")
+ break
+ ;;
+ --help)
+ usage
+ ;;
+ *)
+ echoerr "Unknown argument: $1"
+ usage
+ ;;
+ esac
+done
+
+if [[ "$HOST" == "" || "$PORT" == "" ]]; then
+ echoerr "Error: you need to provide a host and port to test."
+ usage
+fi
+
+TIMEOUT=${TIMEOUT:-15}
+STRICT=${STRICT:-0}
+CHILD=${CHILD:-0}
+QUIET=${QUIET:-0}
+
+# check to see if timeout is from busybox?
+# check to see if timeout is from busybox?
+TIMEOUT_PATH=$(realpath $(which timeout))
+if [[ $TIMEOUT_PATH =~ "busybox" ]]; then
+ ISBUSY=1
+ BUSYTIMEFLAG="-t"
+else
+ ISBUSY=0
+ BUSYTIMEFLAG=""
+fi
+
+if [[ $CHILD -gt 0 ]]; then
+ wait_for
+ RESULT=$?
+ exit $RESULT
+else
+ if [[ $TIMEOUT -gt 0 ]]; then
+ wait_for_wrapper
+ RESULT=$?
+ else
+ wait_for
+ RESULT=$?
+ fi
+fi
+
+if [[ $CLI != "" ]]; then
+ if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then
+ echoerr "$cmdname: strict mode, refusing to execute subprocess"
+ exit $RESULT
+ fi
+ exec "${CLI[@]}"
+else
+ exit $RESULT
+fi
\ No newline at end of file
diff --git a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AAAResourceTestSuite.java b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AAAResourceTestSuite.java
new file mode 100644
index 0000000..8802ff4
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AAAResourceTestSuite.java
@@ -0,0 +1,23 @@
+package marketing.heyday.hartmann.fotodocumentation.rest;
+
+import org.junit.platform.suite.api.SelectPackages;
+import org.junit.platform.suite.api.Suite;
+import org.junit.platform.suite.api.SuiteDisplayName;
+
+/**
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 22 Nov 2024
+ */
+
+
+@Suite
+@SuiteDisplayName("Rest Resource Suite")
+@SelectPackages("marketing.heyday.hartmann.fotodocumentation.rest")
+public class AAAResourceTestSuite {
+
+}
diff --git a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AbstractRestTest.java b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AbstractRestTest.java
new file mode 100644
index 0000000..23f2df4
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AbstractRestTest.java
@@ -0,0 +1,95 @@
+package marketing.heyday.hartmann.fotodocumentation.rest;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.*;
+import java.util.Base64;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import javax.json.Json;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.fluent.Executor;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.impl.client.BasicCookieStore;
+
+/**
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 13 Nov 2024
+ */
+public abstract class AbstractRestTest extends AbstractTest {
+ private static final Log LOG = LogFactory.getLog(AbstractRestTest.class);
+ public static final String TEXT_PLAIN = "text/plain";
+
+ public HttpResponse executeRequest(Request request) throws IOException {
+ var executor = Executor.newInstance();
+ return executor.use(new BasicCookieStore()).execute(request).returnResponse();
+ }
+
+ protected String getAuthorization() {
+ return getAuthorization(username, password);
+ }
+
+ protected String getAuthorization(String user, String pass) {
+ String auth = user + ":" + pass;
+ String encoded = Base64.getEncoder().encodeToString(auth.getBytes());
+ String authorization = "Basic " + encoded;
+ return authorization;
+ }
+
+ protected String getBearerToken(String authorization) {
+ try {
+ String path = deploymentURL + "api/login";
+ Request request = Request.Get(path).addHeader("Accept", "application/json; charset=utf-8")
+ .addHeader("Authorization", authorization);
+
+ HttpResponse httpResponse = executeRequest(request);
+ int code = httpResponse.getStatusLine().getStatusCode();
+ assertEquals(200, code);
+
+ String bearerStr = getResponseText(httpResponse);
+
+ try (var reader = Json.createReader(new StringReader(bearerStr));) {
+ return reader.readObject().getString("accessToken");
+ }
+ } catch (IOException e) {
+ LOG.error("Failed to get bearer token " + e.getMessage(), e);
+ return "";
+ }
+ }
+
+ protected byte[] getResponse(HttpResponse httpResponse) throws IOException {
+ try (var output = new ByteArrayOutputStream(); var input = httpResponse.getEntity().getContent()) {
+ input.transferTo(output);
+ output.flush();
+ return output.toByteArray();
+ }
+ }
+
+ protected String getResponseText(HttpResponse httpResponse) throws IOException {
+ String text;
+ try (BufferedReader input = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()))) {
+ text = input.lines().collect(Collectors.joining("\n"));
+ }
+ return text;
+ }
+
+ protected String getResponseText(HttpResponse httpResponse, Supplier supplier) throws IOException {
+ String text = getResponseText(httpResponse);
+ writeJsonFile(text, supplier.get());
+ return text;
+ }
+
+ protected String getResponseText(HttpResponse httpResponse, String name) throws IOException {
+ String className = this.getClass().getName();
+ return getResponseText(httpResponse, () -> className + "-" + name + ".json");
+ }
+}
diff --git a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AbstractTest.java b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AbstractTest.java
new file mode 100644
index 0000000..4fc7de3
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/AbstractTest.java
@@ -0,0 +1,189 @@
+package marketing.heyday.hartmann.fotodocumentation.rest;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.sql.*;
+import java.util.Date;
+import java.util.Properties;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.time.DateUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.dbunit.database.DatabaseConfig;
+import org.dbunit.database.IDatabaseConnection;
+import org.dbunit.dataset.IDataSet;
+import org.dbunit.dataset.ReplacementDataSet;
+import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
+import org.dbunit.ext.postgresql.PostgresqlDataTypeFactory;
+import org.dbunit.operation.DatabaseOperation;
+import org.json.JSONException;
+import org.skyscreamer.jsonassert.JSONAssert;
+
+/**
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 13 Nov 2024
+ */
+
+public class AbstractTest {
+ private static final Log LOG = LogFactory.getLog(AbstractTest.class);
+
+ public String deploymentURL = "http://localhost:8180/";
+ public String username = "admin";
+ public String password = "test";
+
+ private static Properties props = null;
+
+ public static Connection getConnection() throws SQLException {
+ if (props == null) {
+ try (FileReader reader = new FileReader(new File("src/test/resources/junit.properties"))) {
+ Class.forName("org.postgresql.Driver");
+ props = new Properties();
+ props.load(reader);
+ } catch (Exception e) {
+ throw new SQLException(e.getMessage());
+ }
+ }
+ return DriverManager.getConnection(props.getProperty("connection"), props.getProperty("username"), props.getProperty("password"));
+ }
+
+ public static void initDB() {
+ initDB("dataset.xml");
+ }
+
+ public static void initDB(String xmlFile) {
+ try (Connection jdbcConnection = getConnection();) {
+ initDb(jdbcConnection, xmlFile);
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void initDb(Connection jdbcConnection, String xmlFile) {
+ // using manual DBUnit since the persistence plugin has performance problems.
+ // https://community.jboss.org/thread/235511
+ // https://issues.jboss.org/browse/ARQ-1440
+ try {
+ System.out.println("start dbunit " + new Date(System.currentTimeMillis()));
+ IDatabaseConnection conn = new org.dbunit.database.DatabaseConnection(jdbcConnection);
+ DatabaseConfig config = conn.getConfig();
+ config.setProperty("http://www.dbunit.org/features/datatypeWarning", Boolean.FALSE);
+ config.setProperty(DatabaseConfig.FEATURE_ALLOW_EMPTY_FIELDS, Boolean.TRUE);
+ config.setProperty(DatabaseConfig.FEATURE_BATCHED_STATEMENTS, Boolean.TRUE);
+ config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new PostgresqlDataTypeFactory());
+
+ // initialize your dataset here
+ IDataSet dataSet = new FlatXmlDataSetBuilder().build(new File("src/test/resources/datasets/" + xmlFile));
+ ReplacementDataSet repDataSet = new ReplacementDataSet(dataSet);
+ repDataSet.addReplacementObject("EXP_DATE", DateUtils.addDays(new Date(), 1));
+ repDataSet.addReplacementObject("NULL_DATE", null);
+ repDataSet.addReplacementObject("NULL_NUMBER", null);
+ repDataSet.addReplacementObject("NULL_STRING", null);
+ try {
+ DatabaseOperation.CLEAN_INSERT.execute(conn, repDataSet);
+ } finally {
+ conn.close();
+ }
+ } catch (java.sql.BatchUpdateException e) {
+ e.printStackTrace();
+ e.getNextException().printStackTrace();
+ throw new RuntimeException(e.getMessage(), e);
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ System.out.println("End dbunit " + new Date(System.currentTimeMillis()));
+ }
+
+ public String fileToString(final String fileName) {
+ try (FileInputStream input = new FileInputStream("src/test/resources/" + fileName)) {
+ return IOUtils.toString(input, Charset.forName("utf-8"));
+ } catch (IOException e) {
+ return "";
+ }
+ }
+
+ public static void writeJsonFile(final String content, final String fileName) {
+ if (content.isEmpty()) {
+ return;
+ }
+ File file = new File("target/test/output/");
+ file.mkdirs();
+ try (FileOutputStream out = new FileOutputStream(new File(file, fileName))) {
+ ObjectMapper mapper = new ObjectMapper();
+ Object json = mapper.readValue(content, Object.class);
+ String formattedContent = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json);
+ System.out.println(formattedContent);
+ IOUtils.write(formattedContent, out, Charset.forName("utf-8"));
+ } catch (Exception e) {
+ LOG.error("Error storing json file", e);
+ }
+ }
+
+ public static void writeFile(byte[] content, String fileName) {
+ File file = new File("target/test/output/");
+ file.mkdirs();
+
+ try (FileOutputStream out = new FileOutputStream(new File(file, fileName))) {
+ IOUtils.write(content, out);
+ } catch (Exception e) {
+ LOG.error("Error storing binary file", e);
+ }
+ }
+
+ public static byte[] readFile(String fileName) {
+ try (FileInputStream in = new FileInputStream("src/test/resources/" + fileName)) {
+ return IOUtils.toByteArray(in);
+ } catch (Exception e) {
+ throw new RuntimeException("Error reading file src/test/resources/" + fileName);
+ }
+ }
+
+ public void writeFile(InputStream content, String fileName) {
+ File file = new File("target/test/output/");
+ file.mkdirs();
+
+ try (FileOutputStream out = new FileOutputStream(new File(file, fileName))) {
+ IOUtils.copy(content, out);
+ } catch (Exception e) {
+ LOG.error("Error storing binary file", e);
+ }
+ }
+
+ public static int getCount(String sql) {
+ try (Connection conn = getConnection(); PreparedStatement statement = conn.prepareStatement(sql); ResultSet resultSet = statement.executeQuery();) {
+ if (resultSet.next()) {
+ return resultSet.getInt(1);
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ LOG.error("no result found");
+ return 0;
+ }
+
+ protected void jsonAssert(String expected, String content) {
+ jsonAssert(expected, content, false);
+ }
+
+ protected void jsonAssert(String expected, String content, boolean strict) {
+ try {
+ JSONAssert.assertEquals(expected, content, strict);
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /*
+ public static void main(String[] args) throws SQLException {
+ try (var connection = DriverManager.getConnection("jdbc:postgresql://localhost:5432/skillmatrix", "skillmatrix", "skillmatrix")) {
+ initDb(connection, "dataset.xml");
+ }
+ }*/
+}
diff --git a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureTest.java b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureTest.java
new file mode 100644
index 0000000..f5b1ca2
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/CustomerPictureTest.java
@@ -0,0 +1,53 @@
+package marketing.heyday.hartmann.fotodocumentation.rest;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.entity.ContentType;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+/**
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 14 Nov 2024
+ */
+@TestMethodOrder(OrderAnnotation.class)
+public class CustomerPictureTest extends AbstractRestTest {
+ private static final Log LOG = LogFactory.getLog(CustomerPictureTest.class);
+ private static final String PATH = "api/customer-picture";
+ private static final String BASE_UPLOAD = "src/test/resources/upload/";
+
+ @BeforeAll
+ public static void init() {
+ initDB();
+ }
+
+ @Test
+ @Order(1)
+ public void doAddCustomerPicture() throws IOException {
+ LOG.info("doAddCustomerPicture");
+
+ String path = deploymentURL + PATH;
+ Request request = Request.Post(path).addHeader("Accept", "application/json; charset=utf-8")
+ .addHeader("Authorization", getAuthorization())
+ .bodyFile(new File(BASE_UPLOAD + "add.json"), ContentType.APPLICATION_JSON);
+
+ HttpResponse httpResponse = executeRequest(request);
+ int code = httpResponse.getStatusLine().getStatusCode();
+ assertEquals(200, code);
+ }
+}
diff --git a/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/MonitoringResourceTest.java b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/MonitoringResourceTest.java
new file mode 100644
index 0000000..8675a6c
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/test/java/marketing/heyday/hartmann/fotodocumentation/rest/MonitoringResourceTest.java
@@ -0,0 +1,34 @@
+package marketing.heyday.hartmann.fotodocumentation.rest;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.fluent.Request;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ * Copyright: Copyright (c) 2024
+ * Company: heyday Marketing GmbH
+ * @author Patrick Verboom
+ * @version 1.0
+ *
+ * created: 13 Nov 2024
+ */
+
+public class MonitoringResourceTest extends AbstractRestTest {
+
+ @Test
+ public void check() throws IOException {
+ String path = deploymentURL + "api/monitoring/check/hello";
+
+ Request request = Request.Get(path).addHeader("Accept", TEXT_PLAIN);
+
+ HttpResponse httpResponse = executeRequest(request);
+ int code = httpResponse.getStatusLine().getStatusCode();
+ assertEquals(200, code);
+ }
+}
+
diff --git a/hartmann-foto-documentation-docker/src/test/resources/datasets/dataset.xml b/hartmann-foto-documentation-docker/src/test/resources/datasets/dataset.xml
new file mode 100644
index 0000000..2f2aad4
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/test/resources/datasets/dataset.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hartmann-foto-documentation-docker/src/test/resources/junit.properties b/hartmann-foto-documentation-docker/src/test/resources/junit.properties
new file mode 100644
index 0000000..1bd6fc1
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/test/resources/junit.properties
@@ -0,0 +1,3 @@
+connection=jdbc:postgresql://localhost:5430/fotodocumentation
+username=fotodocumentation
+password=fotodocumentation
diff --git a/hartmann-foto-documentation-docker/src/test/resources/log4j.xml b/hartmann-foto-documentation-docker/src/test/resources/log4j.xml
new file mode 100644
index 0000000..92e24ab
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/test/resources/log4j.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hartmann-foto-documentation-docker/src/test/resources/upload/add.json b/hartmann-foto-documentation-docker/src/test/resources/upload/add.json
new file mode 100644
index 0000000..4b6509d
--- /dev/null
+++ b/hartmann-foto-documentation-docker/src/test/resources/upload/add.json
@@ -0,0 +1,8 @@
+{
+ "username": "verboomp",
+ "pharmacyName": "Müller Apotheke",
+ "customerNumber": "1234",
+ "date": "2026-01-20T11:06:00+01:00",
+ "comment": "Some long text for Müller Pharmacy",
+ "base64String": "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"
+}
diff --git a/hartmann-foto-documentation-web/pom.xml b/hartmann-foto-documentation-web/pom.xml
new file mode 100644
index 0000000..cf9c87c
--- /dev/null
+++ b/hartmann-foto-documentation-web/pom.xml
@@ -0,0 +1,70 @@
+
+ 4.0.0
+
+ marketing.heyday.hartmann.fotodocumentation
+ hartmann-foto-documentation
+ 1.0.0
+ ../hartmann-foto-documentation/pom.xml
+
+ hartmann-foto-documentation-web
+ 1.0.0-SNAPSHOT
+ hartmann-foto-documentation web
+ war
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+
+ true
+ true
+
+
+
+
+
+
+
+
+ ${project.groupId}
+ hartmann-foto-documentation-app
+ ${project.version}
+
+
+
diff --git a/hartmann-foto-documentation-web/src/main/webapp/.FSync b/hartmann-foto-documentation-web/src/main/webapp/.FSync
new file mode 100644
index 0000000..e69de29
diff --git a/hartmann-foto-documentation-web/src/main/webapp/.last_build_id b/hartmann-foto-documentation-web/src/main/webapp/.last_build_id
new file mode 100644
index 0000000..7cf2aca
--- /dev/null
+++ b/hartmann-foto-documentation-web/src/main/webapp/.last_build_id
@@ -0,0 +1 @@
+20d9c068b272d7a2718d0d8e3e04271e
\ No newline at end of file
diff --git a/hartmann-foto-documentation-web/src/main/webapp/.sass-cache/eb7439011024ea2090517b484fb24cab7ef65b8d/styles.scssc b/hartmann-foto-documentation-web/src/main/webapp/.sass-cache/eb7439011024ea2090517b484fb24cab7ef65b8d/styles.scssc
new file mode 100644
index 0000000..e4a7b6a
Binary files /dev/null and b/hartmann-foto-documentation-web/src/main/webapp/.sass-cache/eb7439011024ea2090517b484fb24cab7ef65b8d/styles.scssc differ
diff --git a/hartmann-foto-documentation-web/src/main/webapp/META-INF/MANIFEST.MF b/hartmann-foto-documentation-web/src/main/webapp/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5be8a35
--- /dev/null
+++ b/hartmann-foto-documentation-web/src/main/webapp/META-INF/MANIFEST.MF
@@ -0,0 +1,24 @@
+Manifest-Version: 1.0
+Class-Path: commons-logging-1.1.1.jar cue-platform-storage-3.0.1-SN
+ APSHOT.jar commons-lang3-3.5.jar javaslang-2.0.2.jar javaslang-match-2.
+ 0.2.jar commons-io-2.4.jar org.eclipse.jgit-4.2.0.201601211800-r.jar js
+ ch-0.1.53.jar JavaEWAH-0.7.9.jar slf4j-api-1.7.2.jar cue-platform-
+ app-3.0.1-SNAPSHOT.jar cue-platform-push-3.0.1-SNAPSHOT.jar pushy-
+ 0.13.5.jar netty-codec-http2-4.1.25.Final.jar netty-codec-http-4.1.25.F
+ inal.jar netty-codec-4.1.25.Final.jar netty-transport-4.1.25.Final.jar
+ netty-buffer-4.1.25.Final.jar netty-common-4.1.25.Final.jar netty-resol
+ ver-4.1.25.Final.jar netty-handler-4.1.25.Final.jar netty-handler-proxy
+ -4.1.25.Final.jar netty-codec-socks-4.1.25.Final.jar netty-resolver-dns
+ -4.1.25.Final.jar netty-codec-dns-4.1.25.Final.jar fast-uuid-0.1.jar gs
+ on-2.2.4.jar primefaces-6.1.jar primefaces-extensions-6.1.1.jar compile
+ r-0.9.5.jar antlr4-4.5.jar antlr4-runtime-4.5.jar org.abego.treelayout.
+ core-1.0.1.jar antlr-runtime-3.5.2.jar ST4-4.0.8.jar json-schema-valida
+ tor-0.1.2.jar slf4j-ext-1.7.21.jar cal10n-api-0.8.1.jar commons-lang-2.
+ 6.jar omnifaces-2.6.2.jar flyway-core-3.1.jar commons-fileupload-1.3.ja
+ r poi-4.1.0.jar commons-codec-1.12.jar commons-collections4-4.3.jar com
+ mons-math3-3.6.1.jar poi-ooxml-4.1.0.jar poi-ooxml-schemas-4.1.0.jar xm
+ lbeans-3.1.0.jar commons-compress-1.18.jar curvesapi-1.06.jar cue-
+ platform-module-detailing-3.0.1-SNAPSHOT.jar cue-platform-module-c
+ ontent-3.0.1-SNAPSHOT.jar cue-platform-module-poll-3.0.1-SNAPSHOT.
+ jar resources-codemirror-6.1.1.jar
+
diff --git a/hartmann-foto-documentation-web/src/main/webapp/META-INF/context.xml b/hartmann-foto-documentation-web/src/main/webapp/META-INF/context.xml
new file mode 100644
index 0000000..9b7ba60
--- /dev/null
+++ b/hartmann-foto-documentation-web/src/main/webapp/META-INF/context.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/beans.xml b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..be79900
--- /dev/null
+++ b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,6 @@
+
+
+
\ No newline at end of file
diff --git a/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
new file mode 100644
index 0000000..df1c5bb
--- /dev/null
+++ b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/jboss-ejb3.xml b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/jboss-ejb3.xml
new file mode 100644
index 0000000..39de0d9
--- /dev/null
+++ b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/jboss-ejb3.xml
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/jboss-web.xml b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/jboss-web.xml
new file mode 100644
index 0000000..37e8ba3
--- /dev/null
+++ b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/jboss-web.xml
@@ -0,0 +1,5 @@
+
+
+ fotoDocumentationSecurity
+ /
+
\ No newline at end of file
diff --git a/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/openapi-configuration.yaml b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/openapi-configuration.yaml
new file mode 100644
index 0000000..efaa60d
--- /dev/null
+++ b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/openapi-configuration.yaml
@@ -0,0 +1,5 @@
+prettyPrint: true
+servers:
+ - url: https://localhost/api
+ description: Production server
+ -
\ No newline at end of file
diff --git a/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/web.xml b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..48df189
--- /dev/null
+++ b/hartmann-foto-documentation-web/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Secure
+ /api/login
+
+ GET
+ POST
+ PUT
+ DELETE
+
+
+ **
+
+
+
+
+ BASIC
+ fotoDocumentationRealm
+
+
+
diff --git a/hartmann-foto-documentation/pom.xml b/hartmann-foto-documentation/pom.xml
new file mode 100644
index 0000000..28cddba
--- /dev/null
+++ b/hartmann-foto-documentation/pom.xml
@@ -0,0 +1,506 @@
+
+ 4.0.0
+
+ marketing.heyday.maven
+ parentpom
+ 16
+
+ marketing.heyday.hartmann.fotodocumentation
+ hartmann-foto-documentation
+ pom
+ 1.0.0
+ hartmann-foto-documentation
+
+ scm:git:git@git.heyday.marketing:heyday-marketing-gmbh/hartmann-foto_documentation.git
+ https://git.heyday.marketing/?p=heyday-marketing-gmbh/hartmann-foto_documentation.git
+ HEAD
+
+
+ install
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.5.0
+
+
+ enforce-maven
+
+ enforce
+
+
+
+
+ 3.0
+
+
+
+
+
+
+
+ maven-site-plugin
+
+ true
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 3.2.2
+
+
+
+ true
+
+
+ ${buildNumber}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 21
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M1
+
+ ${surefireArgLine}
+ --add-opens java.base/java.lang=ALL-UNNAMED
+ --add-opens java.base/java.lang.reflect=ALL-UNNAMED
+ --add-opens java.base/java.util.concurrent=ALL-UNNAMED
+ --add-opens java.base/java.util=ALL-UNNAMED
+
+ true
+
+
+ **/test/helper/*.*
+ **/AllTest.*
+
+ 1
+
+ **/*Test.java
+ **/*IT.java
+
+
+
+ listener
+ org.sonar.java.jacoco.JUnitListener
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ marketing/heyday/hartmann/fotodocumentation/**/*
+
+
+
+
+ test
+ initialize
+
+ prepare-agent
+
+
+
+
+
+ pre-unit-test
+
+ prepare-agent
+
+
+
+ surefireArgLine
+
+
+
+
+ post-unit-test
+ verify
+
+ report
+
+
+
+
+
+ pre-integration-test
+ pre-integration-test
+
+ prepare-agent-integration
+
+
+ true
+
+ failsafeArgLine
+
+
+
+
+ post-integration-test
+
+ verify
+
+ report
+ report-aggregate
+
+
+
+ XML
+ CSV
+
+
+ **/jacoco.exec
+ **/jacoco-it.exec
+
+
+ ${project.reporting.outputDirectory}/jacoco-aggregate
+
+
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${version.jacoco}
+
+
+
+ org.codehaus.mojo
+ buildnumber-maven-plugin
+ 3.0.0
+
+
+ validate
+
+ create
+
+
+
+
+ false
+ false
+ 7
+ true
+
+
+
+
+
+
+
+ true
+
+
+ jacoco
+ reuseReports
+
+
+ ${project.basedir}/target/site/jacoco-aggregate/jacoco.xml
+
+ 0.8.12
+
+ 0.8.12
+ 1.1.11.Final
+ 2.2.0.Final
+ 6.2.5.Final
+
+ 1.2
+ 2.7
+ 2.6
+ 3.5
+ 1.2
+ 1.3
+ 2.0.2
+
+ 4.3
+ 13.0.3
+ 13.0.3
+
+ 0.12.5
+
+ 2.2.19
+
+
+
+
+
+
+ org.wildfly.bom
+ wildfly
+ 26.1.3.Final
+ pom
+
+
+
+ jakarta.platform
+ jakarta.jakartaee-api
+ 10.0.0
+ provided
+
+
+
+ org.hibernate.orm
+ hibernate-core
+ 6.2.13.Final
+ provided
+
+
+
+
+ org.eclipse.parsson
+ parsson
+ 1.1.5
+ provided
+
+
+
+ jakarta.json
+ jakarta.json-api
+ 2.1.2
+ provided
+
+
+
+
+ org.apache.poi
+ poi
+ 5.1.0
+
+
+ org.apache.poi
+ poi-ooxml
+ 5.1.0
+
+
+ dom4j
+ dom4j
+
+
+ stax
+ stax-api
+
+
+
+
+
+
+
+
+
+ io.swagger.core.v3
+ swagger-jaxrs2-jakarta
+ compile
+ ${swagger-version}
+
+
+ java.xml.bind
+ jaxb-api
+
+
+ com.fasterxml.jackson.jaxrs
+ jackson-jaxrs-json-provider
+
+
+
+ com.fasterxml.jackson.core
+ jackson-dataformat
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+
+
+ io.swagger.core.v3
+ swagger-jaxrs2-servlet-initializer-jakarta
+ ${swagger-version}
+
+
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.15.0
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.15.0
+
+
+
+
+
+ org.jboss.resteasy
+ resteasy-core
+ ${version.org.jboss.resteasy}
+
+
+
+ org.jboss.resteasy
+ resteasy-core-spi
+ ${version.org.jboss.resteasy}
+
+
+
+ org.jboss.resteasy
+ resteasy-multipart-provider
+ ${version.org.jboss.resteasy}
+
+
+
+ org.jboss.resteasy
+ resteasy-jackson2-provider
+ ${version.org.jboss.resteasy}
+ provided
+
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ ${version.jsonwebtoken}
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${version.jsonwebtoken}
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ ${version.jsonwebtoken}
+ runtime
+
+
+
+
+
+ org.postgresql
+ postgresql
+ 9.4-1201-jdbc41
+ test
+
+
+ org.slf4j
+ slf4j-log4j12
+ 1.7.5
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 5.7.0
+ test
+
+
+ org.mockito
+ mockito-core
+ 5.7.0
+ test
+
+
+ net.bytebuddy
+ byte-buddy
+ 1.14.9
+ test
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.6.0
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ 5.6.0
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.6.0
+ test
+
+
+
+ org.junit.platform
+ junit-platform-suite-engine
+ 1.9.2
+ test
+
+
+
+ org.junit.platform
+ junit-platform-suite-api
+ 1.9.2
+ test
+
+
+
+ org.junit.platform
+ junit-platform-launcher
+ 1.9.2
+ test
+
+
+
+ org.junit.platform
+ junit-platform-commons
+ 1.9.2
+ test
+
+
+
+ org.jacoco
+ org.jacoco.core
+ ${version.jacoco}
+ test
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..6d61c43
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,27 @@
+
+ 4.0.0
+
+ marketing.heyday.hartmann.fotodocumentation
+ hartman-foto-documentation
+ 1.0.0
+ hartmann-foto-documentation/pom.xml
+
+
+ marketing.heyday.hartmann.fotodocumentation
+ hartmann-foto-documentation-root
+ 1.0.0-SNAPSHOT
+ pom
+ hartman-foto-documentation-root
+
+ scm:git:git@git.heyday.marketing:heyday-marketing-gmbh/hartmann-foto_documentation.git
+ https://git.heyday.marketing/?p=heyday-marketing-gmbh/hartmann-foto_documentation.git
+ HEAD
+
+
+ hartmann-foto-documentation-app
+ hartmann-foto-documentation-web
+ hartmann-foto-documentation-docker
+
+
+
+