Compare commits
46 Commits
071b1f7f95
...
cb5d0de5b2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb5d0de5b2 | ||
|
|
bf02f143dd | ||
|
|
51a05ea372 | ||
|
|
c7ad119f35 | ||
|
|
2e120291c7 | ||
|
|
3a1b9a3af6 | ||
|
|
935999bb5a | ||
|
|
1296658645 | ||
|
|
4cd5e12d72 | ||
|
|
c78560ac3a | ||
|
|
e3d7716133 | ||
|
|
508b1b4380 | ||
|
|
6cb7c768e0 | ||
|
|
54b6237188 | ||
|
|
5f1d2d8610 | ||
|
|
f9ca668b39 | ||
|
|
e0279a4d26 | ||
|
|
c7afc52abc | ||
|
|
cbd53ac9a5 | ||
|
|
5f2a54a5bc | ||
|
|
8d329501e4 | ||
|
|
33ee33d55c | ||
|
|
94c6becf9f | ||
|
|
d9d64d2daa | ||
|
|
71100353fa | ||
|
|
0918185a6b | ||
|
|
2587a9c3c8 | ||
|
|
e062b4c688 | ||
|
|
38979c99e5 | ||
|
|
ca514eea67 | ||
|
|
98764dc51e | ||
|
|
e4b2dd0462 | ||
|
|
3d456128b1 | ||
|
|
f48bfe2107 | ||
|
|
b3de3eec8c | ||
|
|
d2e6f5164a | ||
|
|
47ee7c3c25 | ||
|
|
2c14caa6ca | ||
|
|
934a5eec51 | ||
|
|
39580438c2 | ||
|
|
8ccd98755b | ||
|
|
db46303a88 | ||
|
|
f3ba2758af | ||
|
|
4b4353f133 | ||
|
|
05b0ced8dd | ||
|
|
9d36ea7780 |
32
.gitignore
vendored
32
.gitignore
vendored
@@ -42,9 +42,9 @@ target/
|
||||
|
||||
*/.dart_tool
|
||||
*/build
|
||||
skillmatrix-frontend/build
|
||||
skillmatrix-frontend/coverage/
|
||||
skillmatrix-frontend/pubspec.lock
|
||||
hartmann-foto-documentation-frontend/build
|
||||
hartmann-foto-documentation-frontend/coverage/
|
||||
hartmann-foto-documentation-frontend/pubspec.lock
|
||||
|
||||
# Avoid committing generated Javascript files:
|
||||
*.dart.js
|
||||
@@ -59,17 +59,17 @@ skillmatrix-frontend/pubspec.lock
|
||||
.flutter-plugins-dependencies
|
||||
|
||||
|
||||
|
||||
skillmatrix-docker/src/main/docker/skillmatrix-web-*.war
|
||||
skillmatrix-web/src/main/webapp/.last_build_id
|
||||
skillmatrix-web/src/main/webapp/assets/
|
||||
skillmatrix-web/src/main/webapp/canvaskit/
|
||||
skillmatrix-web/src/main/webapp/favicon.png
|
||||
skillmatrix-web/src/main/webapp/flutter.js
|
||||
skillmatrix-web/src/main/webapp/flutter_bootstrap.js
|
||||
skillmatrix-web/src/main/webapp/flutter_service_worker.js
|
||||
skillmatrix-web/src/main/webapp/icons/
|
||||
skillmatrix-web/src/main/webapp/index.html
|
||||
skillmatrix-web/src/main/webapp/manifest.json
|
||||
skillmatrix-web/src/main/webapp/version.json
|
||||
hartmann-foto-documentation-docker/src/main/docker/hartmann-foto-documentation-web-*.war
|
||||
hartmann-foto-documentation-docker/src/main/docker/hartmann-foto-documentation-web-*.war
|
||||
hartmann-foto-documentation-web/src/main/webapp/.last_build_id
|
||||
hartmann-foto-documentation-web/src/main/webapp/assets/
|
||||
hartmann-foto-documentation-web/src/main/webapp/canvaskit/
|
||||
hartmann-foto-documentation-web/src/main/webapp/favicon.png
|
||||
hartmann-foto-documentation-web/src/main/webapp/flutter.js
|
||||
hartmann-foto-documentation-web/src/main/webapp/flutter_bootstrap.js
|
||||
hartmann-foto-documentation-web/src/main/webapp/flutter_service_worker.js
|
||||
hartmann-foto-documentation-web/src/main/webapp/icons/
|
||||
hartmann-foto-documentation-web/src/main/webapp/index.html
|
||||
hartmann-foto-documentation-web/src/main/webapp/manifest.json
|
||||
hartmann-foto-documentation-web/src/main/webapp/version.json
|
||||
|
||||
|
||||
17
.project
Normal file
17
.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>hartmann-foto_documentation</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
2
.settings/org.eclipse.core.resources.prefs
Normal file
2
.settings/org.eclipse.core.resources.prefs
Normal file
@@ -0,0 +1,2 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
4
.settings/org.eclipse.m2e.core.prefs
Normal file
4
.settings/org.eclipse.m2e.core.prefs
Normal file
@@ -0,0 +1,4 @@
|
||||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
||||
99
CLAUDE.md
Normal file
99
CLAUDE.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Build Commands
|
||||
|
||||
### Backend (Maven)
|
||||
```bash
|
||||
# Full build and deploy
|
||||
mvn deploy -U -f pom.xml
|
||||
|
||||
# Run integration tests with Docker
|
||||
mvn deploy -P docker -f hartmann-foto-documentation-docker/pom.xml
|
||||
```
|
||||
|
||||
### Frontend (Flutter)
|
||||
```bash
|
||||
cd hartmann-foto-documentation-frontend
|
||||
|
||||
# Install dependencies
|
||||
flutter pub get
|
||||
|
||||
# Build for web
|
||||
flutter build web --no-tree-shake-icons
|
||||
|
||||
# Run tests with coverage
|
||||
flutter test --coverage --reporter=json
|
||||
|
||||
# Run a single test file
|
||||
flutter test test/path/to/test_file.dart
|
||||
|
||||
# Static analysis
|
||||
flutter analyze
|
||||
|
||||
# Generate localization files
|
||||
flutter gen-l10n
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Multi-Module Maven Project
|
||||
- **hartmann-foto-documentation-app** - Backend REST API (jar)
|
||||
- **hartmann-foto-documentation-web** - WAR deployment package
|
||||
- **hartmann-foto-documentation-docker** - Docker/integration tests
|
||||
- **hartmann-foto-documentation-frontend** - Flutter mobile/web app
|
||||
|
||||
### Technology Stack
|
||||
- **Backend:** Java 21, WildFly 26.1.3, Jakarta EE 10, Hibernate 6.2, PostgreSQL 11, JWT auth
|
||||
- **Frontend:** Flutter 3.3.0+, Provider for state management, go_router for navigation
|
||||
- **API Docs:** Swagger/OpenAPI v3
|
||||
|
||||
### Backend Layered Architecture
|
||||
```
|
||||
REST Resources → Services (@Stateless EJBs) → QueryService → JPA Entities
|
||||
```
|
||||
|
||||
Key packages in `hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/`:
|
||||
- `core/model/` - JPA entities (User, Right, Customer, Picture, JwtRefreshToken)
|
||||
- `core/service/` - Business logic with AbstractService base class
|
||||
- `core/query/` - QueryService for database operations
|
||||
- `rest/` - JAX-RS resources and value objects
|
||||
|
||||
### Frontend Structure
|
||||
```
|
||||
lib/
|
||||
├── main.dart # Entry point (MaterialApp.router)
|
||||
├── controller/ # Business logic controllers
|
||||
├── pages/ # Feature-based UI (login/, customer/)
|
||||
├── dto/ # Data Transfer Objects
|
||||
├── utils/ # DI container, theme, routing
|
||||
└── l10n/ # Localization (German supported)
|
||||
```
|
||||
|
||||
### Authentication
|
||||
- JWT token-based authentication with refresh tokens
|
||||
- WildFly Elytron security realm
|
||||
- Bearer token auth for REST endpoints
|
||||
- User passwords stored with salt-based hashing
|
||||
|
||||
### Database Entities
|
||||
- User ↔ Right (many-to-many via user_to_right)
|
||||
- Customer → Picture (one-to-many)
|
||||
- JwtRefreshToken (device/IP tracking)
|
||||
|
||||
Named queries pattern: `@NamedQuery` on entities (e.g., `User.BY_USERNAME`)
|
||||
|
||||
## Local Development
|
||||
|
||||
Docker Compose provides PostgreSQL and WildFly:
|
||||
```bash
|
||||
cd hartmann-foto-documentation-docker
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
Port mappings: WildFly HTTP (8180), Management (9990), SMTP (8280)
|
||||
|
||||
## CI/CD
|
||||
|
||||
Jenkins pipeline with SonarQube integration. Build runs on macOS agent with JDK 21.
|
||||
243
Jenkinsfile
vendored
Normal file
243
Jenkinsfile
vendored
Normal file
@@ -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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
365
hartmann-foto-documentation-app/pom.xml
Normal file
365
hartmann-foto-documentation-app/pom.xml
Normal file
@@ -0,0 +1,365 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>marketing.heyday.hartmann.fotodocumentation</groupId>
|
||||
<artifactId>hartmann-foto-documentation</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<relativePath>../hartmann-foto-documentation/pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>hartmann-foto-documentation-app</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>hartmann-foto-documentation app</name>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<skipDeploy>true</skipDeploy>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.pdfbox</groupId>
|
||||
<artifactId>pdfbox</artifactId>
|
||||
<version>3.0.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Elytron secrity used for username/password login -->
|
||||
<dependency>
|
||||
<groupId>org.wildfly.security</groupId>
|
||||
<artifactId>wildfly-elytron</artifactId>
|
||||
<version>2.5.2.Final</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.wildfly.security</groupId>
|
||||
<artifactId>wildfly-elytron-credential</artifactId>
|
||||
<version>2.5.2.Final</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT Library -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT Library -->
|
||||
|
||||
<!-- Apache POI -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
</dependency>
|
||||
<!-- Apache POI-->
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>fluent-hc</artifactId>
|
||||
<version>4.5.1</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-jaxrs2-jakarta</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>swagger-ui</artifactId>
|
||||
<version>3.22.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.spullara.mustache.java</groupId>
|
||||
<artifactId>compiler</artifactId>
|
||||
<version>0.9.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.networknt</groupId>
|
||||
<artifactId>json-schema-validator</artifactId>
|
||||
<version>0.1.2</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>${version.commons-logging}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${version.commons-lang3}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.javaslang</groupId>
|
||||
<artifactId>javaslang</artifactId>
|
||||
<version>${version.javaslang}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
<version>3.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.ejb3</groupId>
|
||||
<artifactId>jboss-ejb3-ext-api</artifactId>
|
||||
<version>${version.org.jboss.ejb3.ext-api}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.platform</groupId>
|
||||
<artifactId>jakarta.jakartaee-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Resteasy -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-core-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jackson2-provider</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-multipart-provider</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Apache Commons -->
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
<version>${version.commons-fileupload}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${version.commons-io}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache POI -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate.orm</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Logging -->
|
||||
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.15</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>mail</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>jms</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jdmk</groupId>
|
||||
<artifactId>jmxtools</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jmx</groupId>
|
||||
<artifactId>jmxri</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>17.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- to fix the java.lang.NoClassDefFoundError:
|
||||
org/w3c/dom/ElementTraversal -->
|
||||
<dependency>
|
||||
<groupId>xml-apis</groupId>
|
||||
<artifactId>xml-apis</artifactId>
|
||||
<version>1.4.01</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.subethamail</groupId>
|
||||
<artifactId>subethasmtp-smtp</artifactId>
|
||||
<version>${subethamail.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.subethamail</groupId>
|
||||
<artifactId>subethasmtp-wiser</artifactId>
|
||||
<version>${subethamail.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.subethamail</groupId>
|
||||
<artifactId>subethasmtp-smtp</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-suite-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-commons</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-launcher</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- for jacoco -->
|
||||
<dependency>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>org.jacoco.core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 19 Jan 2026
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class AbstractEntity implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
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.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 19 Jan 2026
|
||||
*/
|
||||
|
||||
@Entity
|
||||
@Table(name = "customer")
|
||||
@NamedQuery(name = Customer.FIND_ALL, query = "select c from Customer c order by c.name")
|
||||
@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_ALL = "Customer.findAll";
|
||||
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;
|
||||
|
||||
@Column(name = "city")
|
||||
private String city;
|
||||
|
||||
@Column(name = "zip")
|
||||
private String zip;
|
||||
|
||||
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private Set<Picture> 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 String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getZip() {
|
||||
return zip;
|
||||
}
|
||||
|
||||
public void setZip(String zip) {
|
||||
this.zip = zip;
|
||||
}
|
||||
|
||||
public Set<Picture> getPictures() {
|
||||
return pictures;
|
||||
}
|
||||
|
||||
public void setPictures(Set<Picture> 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 Builder city(String city) {
|
||||
instance.setCity(city);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder zip(String zip) {
|
||||
instance.setZip(zip);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Customer build() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 21 Jan 2026
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "jwt_refresh_token")
|
||||
@NamedQuery(name = JwtRefreshToken.FIND_BY_HASH_REVOKE_NULL, query = "SELECT t FROM JwtRefreshToken t WHERE t.tokenHash = :hash AND t.revokedAt IS NULL")
|
||||
@NamedQuery(name = JwtRefreshToken.FIND_BY_HASH, query = "SELECT t FROM JwtRefreshToken t WHERE t.tokenHash = :hash")
|
||||
@NamedQuery(name = JwtRefreshToken.REVOKE_ALL_USER, query = "UPDATE JwtRefreshToken t SET t.revokedAt = :date WHERE t.user.userId = :userId AND t.revokedAt IS NULL")
|
||||
public class JwtRefreshToken extends AbstractEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public static final String SEQUENCE = "jwt_refresh_token_seq";
|
||||
public static final String FIND_BY_HASH_REVOKE_NULL = "JwtRefreshToken.forHashRevokeNull";
|
||||
public static final String FIND_BY_HASH = "JwtRefreshToken.forHash";
|
||||
public static final String REVOKE_ALL_USER = "JwtRefreshToken.revokeAllUser";
|
||||
public static final String PARAM_HASH = "hash";
|
||||
public static final String PARAM_USER_ID = "userId";
|
||||
public static final String PARAM_DATE = "date";
|
||||
|
||||
@Id
|
||||
@Column(name = "jwt_refresh_token_id", length = 22)
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
|
||||
@SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE, allocationSize = 1)
|
||||
private Long jwsRefreshTokenId;
|
||||
|
||||
@Column(name = "token_hash", nullable = false)
|
||||
private String tokenHash;
|
||||
|
||||
@Column(name = "device_info", nullable = false)
|
||||
private String deviceInfo;
|
||||
|
||||
@Column(name = "ip_address", nullable = false)
|
||||
private String ipAddress;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "issued_at", nullable = false)
|
||||
@JsonIgnore
|
||||
private Date issuedAt;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "expires_at", nullable = false)
|
||||
@JsonIgnore
|
||||
private Date expiresAt;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "revoked_at", nullable = false)
|
||||
@JsonIgnore
|
||||
private Date revokedAt;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name = "last_used_at", nullable = false)
|
||||
@JsonIgnore
|
||||
private Date lastUsedAt;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "user_id_fk", nullable = true)
|
||||
private User user;
|
||||
|
||||
public Long getJwsRefreshTokenId() {
|
||||
return jwsRefreshTokenId;
|
||||
}
|
||||
|
||||
public void setJwsRefreshTokenId(Long jwsRefreshTokenId) {
|
||||
this.jwsRefreshTokenId = jwsRefreshTokenId;
|
||||
}
|
||||
|
||||
public String getTokenHash() {
|
||||
return tokenHash;
|
||||
}
|
||||
|
||||
public void setTokenHash(String tokenHash) {
|
||||
this.tokenHash = tokenHash;
|
||||
}
|
||||
|
||||
public String getDeviceInfo() {
|
||||
return deviceInfo;
|
||||
}
|
||||
|
||||
public void setDeviceInfo(String deviceInfo) {
|
||||
this.deviceInfo = deviceInfo;
|
||||
}
|
||||
|
||||
public String getIpAddress() {
|
||||
return ipAddress;
|
||||
}
|
||||
|
||||
public void setIpAddress(String ipAddress) {
|
||||
this.ipAddress = ipAddress;
|
||||
}
|
||||
|
||||
public Date getIssuedAt() {
|
||||
return issuedAt;
|
||||
}
|
||||
|
||||
public void setIssuedAt(Date issuedAt) {
|
||||
this.issuedAt = issuedAt;
|
||||
}
|
||||
|
||||
public Date getExpiresAt() {
|
||||
return expiresAt;
|
||||
}
|
||||
|
||||
public void setExpiresAt(Date expiresAt) {
|
||||
this.expiresAt = expiresAt;
|
||||
}
|
||||
|
||||
public Date getRevokedAt() {
|
||||
return revokedAt;
|
||||
}
|
||||
|
||||
public void setRevokedAt(Date revokedAt) {
|
||||
this.revokedAt = revokedAt;
|
||||
}
|
||||
|
||||
public Date getLastUsedAt() {
|
||||
return lastUsedAt;
|
||||
}
|
||||
|
||||
public void setLastUsedAt(Date lastUsedAt) {
|
||||
this.lastUsedAt = lastUsedAt;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private JwtRefreshToken instance = new JwtRefreshToken();
|
||||
|
||||
public Builder tokenHash(String tokenHash) {
|
||||
instance.setTokenHash(tokenHash);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder deviceInfo(String deviceInfo) {
|
||||
instance.setDeviceInfo(deviceInfo);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder ipAddress(String ipAddress) {
|
||||
instance.setIpAddress(ipAddress);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder expiresAt(Date expiresAt) {
|
||||
instance.setExpiresAt(expiresAt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder issuedAt(Date issuedAt) {
|
||||
instance.setIssuedAt(issuedAt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder revokedAt(Date revokedAt) {
|
||||
instance.setRevokedAt(revokedAt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder lastUsedAt(Date lastUsedAt) {
|
||||
instance.setLastUsedAt(lastUsedAt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder user(User user) {
|
||||
instance.setUser(user);
|
||||
return this;
|
||||
}
|
||||
|
||||
public JwtRefreshToken build() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @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;
|
||||
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String comment;
|
||||
|
||||
@Column(name = "evaluation")
|
||||
private Integer evaluation;
|
||||
|
||||
@Column
|
||||
private String category;
|
||||
|
||||
@Column(name = "image")
|
||||
@Basic(fetch = FetchType.LAZY)
|
||||
private String image;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "customer_id_fk")
|
||||
private Customer customer;
|
||||
|
||||
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 Integer getEvaluation() {
|
||||
return evaluation;
|
||||
}
|
||||
|
||||
public void setEvaluation(Integer evaluation) {
|
||||
this.evaluation = evaluation;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void setImage(String image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public void setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
}
|
||||
|
||||
@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(){
|
||||
instance.evaluation = 0;
|
||||
}
|
||||
|
||||
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 category(String category) {
|
||||
instance.setCategory(category);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder image(String image) {
|
||||
instance.setImage(image);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder evaluation(Integer evaluation) {
|
||||
instance.setEvaluation(evaluation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder customer(Customer customer) {
|
||||
instance.setCustomer(customer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Picture build() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @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<User> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
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.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 19 Jan 2026
|
||||
*/
|
||||
|
||||
@Entity
|
||||
@Table(name = "x_user")
|
||||
@NamedQuery(name = User.BY_USERNAME, query = "select u from User u where u.username like :username")
|
||||
public class User extends AbstractDateEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public static final String SEQUENCE = "user_seq";
|
||||
public static final String BY_USERNAME = "user.byUsername";
|
||||
public static final String PARAM_USERNAME = "username";
|
||||
|
||||
@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<Right> 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<Right> getRights() {
|
||||
return rights;
|
||||
}
|
||||
|
||||
public void setRights(Set<Right> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.query;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 19 Jan 2026
|
||||
*/
|
||||
|
||||
public record Param(String name, Object value) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @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 <T> Optional<T> callNamedQuerySingleResult(String namedQuery, Param... params) {
|
||||
return singleResult(eManager.createNamedQuery(namedQuery), Arrays.asList(params));
|
||||
}
|
||||
|
||||
private <T> Optional<T> singleResult(Query query, Collection<Param> 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 int callNamedQueryUpdate(String namedQuery, Param... objects) {
|
||||
Query query = eManager.createNamedQuery(namedQuery);
|
||||
for (Param param : objects) {
|
||||
query.setParameter(param.name(), param.value());
|
||||
}
|
||||
return query.executeUpdate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.service;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.ejb.EJB;
|
||||
import jakarta.ejb.EJBContext;
|
||||
import jakarta.ejb.SessionContext;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.query.QueryService;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.StorageUtils.StorageState;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 21 Jan 2026
|
||||
*/
|
||||
|
||||
public abstract class AbstractService {
|
||||
private static final Log LOG = LogFactory.getLog(AbstractService.class);
|
||||
|
||||
@Resource
|
||||
protected EJBContext ejbContext;
|
||||
|
||||
@PersistenceContext
|
||||
protected EntityManager entityManager;
|
||||
|
||||
@Resource
|
||||
protected SessionContext sessionContext;
|
||||
|
||||
@EJB
|
||||
protected QueryService queryService;
|
||||
|
||||
protected <T> StorageState delete(Class<T> type, Long id) {
|
||||
try {
|
||||
T entity = entityManager.getReference(type, id);
|
||||
entityManager.remove(entity);
|
||||
entityManager.flush();
|
||||
return StorageState.OK;
|
||||
} catch (EntityNotFoundException e) {
|
||||
LOG.warn("Failed to delete entity " + type + " not found " + id, e);
|
||||
ejbContext.setRollbackOnly();
|
||||
return StorageState.NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.service;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.ejb.LocalBean;
|
||||
import jakarta.ejb.Stateless;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.persistence.criteria.*;
|
||||
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.utils.CalendarUtil;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.PdfUtils;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerListValue;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerPictureValue;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerValue;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 19 Jan 2026
|
||||
*/
|
||||
@Stateless
|
||||
@LocalBean
|
||||
@PermitAll
|
||||
public class CustomerPictureService extends AbstractService {
|
||||
|
||||
@Inject
|
||||
private PdfUtils pdfUtils;
|
||||
|
||||
@Inject
|
||||
private CalendarUtil calendarUtil;
|
||||
|
||||
public boolean addCustomerPicture(CustomerPictureValue customerPictureValue) {
|
||||
Optional<Customer> 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())
|
||||
.city(customerPictureValue.city()).zip(customerPictureValue.zip())
|
||||
.build());
|
||||
customer = entityManager.merge(customer);
|
||||
|
||||
Picture picture = new Picture.Builder().customer(customer).username(customerPictureValue.username())
|
||||
.category(customerPictureValue.category())
|
||||
.comment(customerPictureValue.comment())
|
||||
.image(customerPictureValue.base64String())
|
||||
.pictureDate(customerPictureValue.date()).build();
|
||||
customer.getPictures().add(picture);
|
||||
|
||||
entityManager.persist(picture);
|
||||
entityManager.flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
// query = search for name, number and date
|
||||
public List<CustomerListValue> getAll(String queryStr, String startsWith) {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Customer> criteriaQuery = builder.createQuery(Customer.class);
|
||||
Root<Customer> customerRoot = criteriaQuery.from(Customer.class);
|
||||
|
||||
criteriaQuery = criteriaQuery.select(customerRoot).distinct(true);
|
||||
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
if (StringUtils.isNotBlank(startsWith)) {
|
||||
String param = startsWith.toLowerCase() + "%";
|
||||
var pred = builder.like(builder.lower(customerRoot.get("name")), param);
|
||||
predicates.add(pred);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(queryStr)) {
|
||||
|
||||
// check if it contains a date
|
||||
Date date = calendarUtil.parse(queryStr);
|
||||
if (date != null) {
|
||||
Date startOfDay = calendarUtil.getStartOfDay(date);
|
||||
Date endOfDay = calendarUtil.getEndOfDay(date);
|
||||
|
||||
Fetch<Customer, Picture> picturesFetch = customerRoot.fetch("pictures", JoinType.LEFT);
|
||||
@SuppressWarnings("unchecked")
|
||||
Join<Customer, Picture> pictures = (Join<Customer, Picture>) picturesFetch;
|
||||
|
||||
var predicateDate = builder.between(pictures.get("pictureDate"), startOfDay, endOfDay);
|
||||
predicates.add(predicateDate);
|
||||
|
||||
} else {
|
||||
String param = "%" + StringUtils.trimToEmpty(queryStr).toLowerCase() + "%";
|
||||
var predicateName = builder.like(builder.lower(customerRoot.get("name")), param);
|
||||
var predicateNr = builder.like(builder.lower(customerRoot.get("customerNumber")), param);
|
||||
|
||||
var pred = builder.or(predicateName, predicateNr);
|
||||
predicates.add(pred);
|
||||
}
|
||||
}
|
||||
|
||||
if (predicates.size() == 1) {
|
||||
criteriaQuery = criteriaQuery.where(predicates.getFirst());
|
||||
} else if (predicates.size() > 1) {
|
||||
criteriaQuery = criteriaQuery.where(builder.and(predicates.toArray(new Predicate[0])));
|
||||
}
|
||||
|
||||
TypedQuery<Customer> typedQuery = entityManager.createQuery(criteriaQuery);
|
||||
List<Customer> customers = typedQuery.getResultList();
|
||||
customers.forEach(c -> c.getPictures().size());
|
||||
return customers.parallelStream().map(CustomerListValue::builder).toList();
|
||||
}
|
||||
|
||||
public CustomerValue get(Long id, String baseUrl) {
|
||||
Customer customer = entityManager.find(Customer.class, id);
|
||||
if (customer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CustomerValue.builder(customer, baseUrl);
|
||||
}
|
||||
|
||||
public byte[] getExport(Long id, Long pictureId) {
|
||||
Customer customer = entityManager.find(Customer.class, id);
|
||||
if (customer == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
List<Picture> pictures = customer.getPictures().stream().sorted((x, y) -> x.getPictureDate().compareTo(y.getPictureDate())).toList();
|
||||
|
||||
if (pictureId != null) {
|
||||
Optional<Picture> pictureOpt = customer.getPictures().stream().filter(p -> p.getPictureId().equals(pictureId)).findFirst();
|
||||
pictures = pictureOpt.map(Arrays::asList).orElse(pictures);
|
||||
}
|
||||
|
||||
return pdfUtils.createPdf(customer, pictures);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.service;
|
||||
|
||||
import static marketing.heyday.hartmann.fotodocumentation.core.model.JwtRefreshToken.*;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import jakarta.inject.Inject;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.JwtRefreshToken;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Right;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.User;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.query.Param;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.JwtTokenUtil;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.vo.TokenPairValue;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 21 Jan 2026
|
||||
*/
|
||||
|
||||
public class JwtTokenService extends AbstractService {
|
||||
private static final Long EXPIRED_DELTA = 30L * 24 * 60 * 60 * 1000;
|
||||
|
||||
@Inject
|
||||
private JwtTokenUtil jwtTokenUtil;
|
||||
|
||||
public TokenPairValue generateTokenPair(User user, String deviceInfo, String ipAddress) {
|
||||
String accessToken = jwtTokenUtil.generateAccessToken(
|
||||
user.getUserId(),
|
||||
user.getUsername(),
|
||||
user.getRights().stream().map(Right::getCode).toList());
|
||||
|
||||
String refreshToken = jwtTokenUtil.generateRefreshToken(user.getUserId());
|
||||
|
||||
// Store refresh token (optional - for revocation support)
|
||||
var refreshTokenEntity = new JwtRefreshToken.Builder()
|
||||
.user(user)
|
||||
.tokenHash(hashToken(refreshToken))
|
||||
.deviceInfo(deviceInfo)
|
||||
.ipAddress(ipAddress)
|
||||
.issuedAt(new Date())
|
||||
.expiresAt(new Date(System.currentTimeMillis() + EXPIRED_DELTA)).build();
|
||||
entityManager.persist(refreshTokenEntity);
|
||||
|
||||
return new TokenPairValue(accessToken, refreshToken);
|
||||
}
|
||||
|
||||
public String refreshAccessToken(String refreshToken) {
|
||||
Claims claims = jwtTokenUtil.validateAndExtractClaims(refreshToken);
|
||||
Long userId = Long.parseLong(claims.getSubject());
|
||||
|
||||
// Verify refresh token exists and not revoked
|
||||
String tokenHash = hashToken(refreshToken);
|
||||
|
||||
Optional<JwtRefreshToken> tokenEntityOpt = queryService.callNamedQuerySingleResult(FIND_BY_HASH_REVOKE_NULL, new Param(PARAM_HASH, tokenHash));
|
||||
if (tokenEntityOpt.isEmpty()) {
|
||||
// FIXME: do error handling
|
||||
}
|
||||
|
||||
var tokenEntity = tokenEntityOpt.get();
|
||||
tokenEntity.setLastUsedAt(new Date());
|
||||
entityManager.merge(tokenEntity);
|
||||
|
||||
User user = entityManager.find(User.class, userId);
|
||||
return jwtTokenUtil.generateAccessToken(user.getUserId(), user.getUsername(), user.getRights().stream().map(Right::getCode).toList());
|
||||
}
|
||||
|
||||
public void revokeRefreshToken(String refreshToken) {
|
||||
String tokenHash = hashToken(refreshToken);
|
||||
Optional<JwtRefreshToken> tokenEntityOpt = queryService.callNamedQuerySingleResult(FIND_BY_HASH, new Param(PARAM_HASH, tokenHash));
|
||||
if (tokenEntityOpt.isPresent()) {
|
||||
JwtRefreshToken tokenEntity = tokenEntityOpt.get();
|
||||
tokenEntity.setRevokedAt(new Date());
|
||||
entityManager.merge(tokenEntity);
|
||||
}
|
||||
}
|
||||
|
||||
public void revokeAllUserTokens(Long userId) {
|
||||
queryService.callNamedQueryUpdate(REVOKE_ALL_USER, new Param(PARAM_DATE, new Date()), new Param(PARAM_USER_ID, userId));
|
||||
}
|
||||
|
||||
private String hashToken(String token) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = digest.digest(token.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.getEncoder().encodeToString(hash);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.ejb.LocalBean;
|
||||
import jakarta.ejb.Stateless;
|
||||
import jakarta.inject.Inject;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.User;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.query.Param;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.vo.TokenPairValue;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 21 Jan 2026
|
||||
*/
|
||||
@Stateless
|
||||
@LocalBean
|
||||
public class LoginService extends AbstractService{
|
||||
|
||||
@Inject
|
||||
private JwtTokenService jwtTokenService;
|
||||
|
||||
public TokenPairValue authenticateUser(String username, String deviceInfo, String ipAddress) {
|
||||
// Get logged-in user from database
|
||||
Optional<User> userOpt = queryService.callNamedQuerySingleResult(User.BY_USERNAME, new Param(User.PARAM_USERNAME, username));
|
||||
if (userOpt.isEmpty()) {
|
||||
// Should never happen
|
||||
return null;
|
||||
}
|
||||
|
||||
User user = userOpt.get();
|
||||
return jwtTokenService.generateTokenPair(user, deviceInfo, ipAddress);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.service;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.ejb.LocalBean;
|
||||
import jakarta.ejb.Stateless;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Picture;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.ImageUtil;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.StorageUtils.StorageState;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 19 Jan 2026
|
||||
*/
|
||||
@Stateless
|
||||
@LocalBean
|
||||
@PermitAll
|
||||
public class PictureService extends AbstractService {
|
||||
private static final Log LOG = LogFactory.getLog(PictureService.class);
|
||||
|
||||
@Inject
|
||||
private ImageUtil imageUtil;
|
||||
|
||||
public StorageState delete(Long id) {
|
||||
return super.delete(Picture.class, id);
|
||||
}
|
||||
|
||||
public StorageState updateEvaluationStatus(Long id, Integer value) {
|
||||
try {
|
||||
Picture entity = entityManager.getReference(Picture.class, id);
|
||||
entity.setEvaluation(value);
|
||||
entityManager.flush();
|
||||
return StorageState.OK;
|
||||
} catch (EntityNotFoundException e) {
|
||||
LOG.warn("Failed to update evaluation value not found " + id, e);
|
||||
ejbContext.setRollbackOnly();
|
||||
return StorageState.NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getImage(Long id, int size) {
|
||||
try {
|
||||
Picture entity = entityManager.getReference(Picture.class, id);
|
||||
String base64 = entity.getImage();
|
||||
return imageUtil.getImage(base64, size);
|
||||
} catch (EntityNotFoundException e) {
|
||||
LOG.warn("Failed to get image for id " + id, e);
|
||||
ejbContext.setRollbackOnly();
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 3 Feb 2026
|
||||
*/
|
||||
|
||||
public class CalendarUtil {
|
||||
private static final Log LOG = LogFactory.getLog(CalendarUtil.class);
|
||||
private static final int HOUR_OF_DAY = 23;
|
||||
private static final int MINUTE = 59;
|
||||
private static final int SECOND = 59;
|
||||
private static final int MILLISECOND = 999;
|
||||
|
||||
public Date parse(String query) {
|
||||
try {
|
||||
return DateUtils.parseDate(query, Locale.GERMAN, "dd.MM.yyyy", "d.M.yyyy", "dd.MM.yy", "d. MMMM yyyy", "dd MMMM yyyy", "dd-MM-yyyy");
|
||||
} catch (ParseException e) {
|
||||
LOG.trace("Failed to find date in queryStr " + query);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Date getStartOfDay(Date date) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
public Date getEndOfDay(Date date) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
cal.set(Calendar.HOUR_OF_DAY, HOUR_OF_DAY);
|
||||
|
||||
cal.set(Calendar.MINUTE, MINUTE);
|
||||
cal.set(Calendar.SECOND, SECOND);
|
||||
cal.set(Calendar.MILLISECOND, MILLISECOND);
|
||||
return cal.getTime();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 3 Feb 2026
|
||||
*/
|
||||
|
||||
public class EvaluationUtil {
|
||||
private static final int MIN_VALUE = 1;
|
||||
private static final int MAX_VALUE = 3;
|
||||
|
||||
public boolean isInValid(Integer value) {
|
||||
return (value == null || value < MIN_VALUE || value > MAX_VALUE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.ImageWriter;
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 2 Feb 2026
|
||||
*/
|
||||
|
||||
public class ImageUtil {
|
||||
private static final Log LOG = LogFactory.getLog(ImageUtil.class);
|
||||
|
||||
private static final int NORMAL_MAX_WIDTH = 1200;
|
||||
private static final float NORMAL_QUALITY = 0.75F;
|
||||
|
||||
private static final int THUMBNAIL_MAX_WIDTH = 200;
|
||||
private static final float THUMBNAIL_QUALITY = 0.6F;
|
||||
|
||||
/**
|
||||
* size 1 is original
|
||||
* size 2 is normal (web)
|
||||
* size 3 is thumbnail
|
||||
*/
|
||||
public byte[] getImage(String base64, int size) {
|
||||
byte[] original = Base64.getDecoder().decode(base64);
|
||||
return switch (size) {
|
||||
case 1 -> original;
|
||||
case 2 -> normal(original);
|
||||
case 3 -> thumbnail(original);
|
||||
default -> original;
|
||||
};
|
||||
}
|
||||
|
||||
private byte[] normal(byte[] original) {
|
||||
return resize(original, NORMAL_MAX_WIDTH, NORMAL_QUALITY);
|
||||
}
|
||||
|
||||
private byte[] thumbnail(byte[] original) {
|
||||
return resize(original, THUMBNAIL_MAX_WIDTH, THUMBNAIL_QUALITY);
|
||||
}
|
||||
|
||||
private byte[] resize(byte[] original, int maxWidth, float quality) {
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(original));
|
||||
if (image == null) {
|
||||
LOG.error("Failed to read image from byte array");
|
||||
return original;
|
||||
}
|
||||
|
||||
int originalWidth = image.getWidth();
|
||||
|
||||
if (originalWidth <= maxWidth) {
|
||||
return original;
|
||||
}
|
||||
|
||||
double scale = (double) maxWidth / originalWidth;
|
||||
int targetWidth = maxWidth;
|
||||
int originalHeight = image.getHeight();
|
||||
int targetHeight = (int) (originalHeight * scale);
|
||||
|
||||
BufferedImage resized = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2d = resized.createGraphics();
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||
g2d.drawImage(image, 0, 0, targetWidth, targetHeight, null);
|
||||
g2d.dispose();
|
||||
|
||||
return writeJpeg(resized, quality);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to resize image", e);
|
||||
return original;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] writeJpeg(BufferedImage image, float quality) throws IOException {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
|
||||
ImageWriteParam param = writer.getDefaultWriteParam();
|
||||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
param.setCompressionQuality(quality);
|
||||
|
||||
try (ImageOutputStream ios = ImageIO.createImageOutputStream(output)) {
|
||||
writer.setOutput(ios);
|
||||
writer.write(null, new IIOImage(image, null, null), param);
|
||||
} finally {
|
||||
writer.dispose();
|
||||
}
|
||||
return output.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.ejb.LocalBean;
|
||||
import jakarta.ejb.Stateless;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 21 Jan 2026
|
||||
*/
|
||||
@Stateless
|
||||
@LocalBean
|
||||
public class JwtTokenUtil {
|
||||
private static final Log LOG = LogFactory.getLog(JwtTokenUtil.class);
|
||||
|
||||
private static final long ACCESS_TOKEN_VALIDITY = 60 * 60 * 1000L; // 1 hour
|
||||
private static final long REFRESH_TOKEN_VALIDITY = 30 * 24 * 60 * 60 * 1000L; // 30 days
|
||||
private static final long TEMP_2FA_TOKEN_VALIDITY = 5 * 60 * 1000L; // 5 minutes
|
||||
|
||||
private static final String ISSUER = "foto-jwt-issuer";
|
||||
private static final String AUDIENCE = "foto-api";
|
||||
|
||||
private PrivateKey privateKey;
|
||||
private PublicKey publicKey;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// Load key from wildfly system property
|
||||
try {
|
||||
String pem = System.getProperty("jwt.secret.key");
|
||||
pem = pem.replace("-----BEGIN PRIVATE KEY-----", "")
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
.replaceAll("\\s", "");
|
||||
byte[] decoded = Base64.getDecoder().decode(pem);
|
||||
|
||||
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
|
||||
privateKey = KeyFactory.getInstance("RSA").generatePrivate(spec);
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
LOG.error("Failed to load JWT PrivateKey " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public String generateAccessToken(Long userId, String username, List<String> groups) {
|
||||
return Jwts.builder()
|
||||
.issuer(ISSUER)
|
||||
.audience().add(AUDIENCE).and()
|
||||
.subject(userId.toString())
|
||||
.claim("username", username)
|
||||
.claim("type", "access")
|
||||
.claim("groups", groups)
|
||||
.issuedAt(new Date())
|
||||
.expiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_VALIDITY))
|
||||
.signWith(privateKey, Jwts.SIG.RS256)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public String generateRefreshToken(Long userId) {
|
||||
return Jwts.builder()
|
||||
.issuer(ISSUER)
|
||||
.audience().add(AUDIENCE).and()
|
||||
.subject(userId.toString())
|
||||
.claim("type", "refresh")
|
||||
.issuedAt(new Date())
|
||||
.expiration(new Date(System.currentTimeMillis() + REFRESH_TOKEN_VALIDITY))
|
||||
.signWith(privateKey, Jwts.SIG.RS256)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public String generateTemp2FAToken(Long userId) {
|
||||
return Jwts.builder()
|
||||
.issuer(ISSUER)
|
||||
.audience().add(AUDIENCE).and()
|
||||
.subject(userId.toString())
|
||||
.claim("type", "temp_2fa")
|
||||
.issuedAt(new Date())
|
||||
.expiration(new Date(System.currentTimeMillis() + TEMP_2FA_TOKEN_VALIDITY))
|
||||
.signWith(privateKey, Jwts.SIG.RS256)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public Claims validateAndExtractClaims(String token) {
|
||||
return Jwts.parser()
|
||||
.verifyWith(publicKey) // FIXME: not working need public key that we currently didn't load
|
||||
.build()
|
||||
.parseUnsecuredClaims(token)
|
||||
.getPayload();
|
||||
}
|
||||
|
||||
public Long extractUserId(String token) {
|
||||
Claims claims = validateAndExtractClaims(token);
|
||||
return Long.parseLong(claims.getSubject());
|
||||
}
|
||||
|
||||
public boolean isTokenExpired(String token) {
|
||||
try {
|
||||
Claims claims = validateAndExtractClaims(token);
|
||||
return claims.getExpiration().before(new Date());
|
||||
} catch (JwtException e) {
|
||||
LOG.warn("Failed to get expiration date from token " + e.getMessage());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.Principal;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.wildfly.security.auth.principal.NamePrincipal;
|
||||
import org.wildfly.security.auth.server.RealmUnavailableException;
|
||||
import org.wildfly.security.auth.server.SecurityDomain;
|
||||
import org.wildfly.security.auth.server.SecurityIdentity;
|
||||
import org.wildfly.security.evidence.PasswordGuessEvidence;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import javaslang.Tuple;
|
||||
import javaslang.Tuple2;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 20 Jan 2026
|
||||
*/
|
||||
|
||||
public class LoginUtils {
|
||||
private static final Log LOG = LogFactory.getLog(LoginUtils.class);
|
||||
private static final int BASIC_HEADER_SIZE = 6;
|
||||
private static final int USERPASS_LENGTH = 1;
|
||||
|
||||
public Optional<SecurityIdentity> authenticate(HttpServletRequest httpServletRequest) {
|
||||
Tuple2<String, String> userPass = extractUsernamePassword(httpServletRequest);
|
||||
if (userPass._1.isBlank() || userPass._2.isBlank()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return authenticate(userPass._1, userPass._2);
|
||||
}
|
||||
|
||||
private Optional<SecurityIdentity> authenticate(String username, String password) {
|
||||
try {
|
||||
LOG.debug("Login with username & password " + username);
|
||||
Principal principal = new NamePrincipal(username);
|
||||
PasswordGuessEvidence evidence = new PasswordGuessEvidence(password.toCharArray());
|
||||
|
||||
SecurityDomain sd = SecurityDomain.getCurrent();
|
||||
SecurityIdentity identity = sd.authenticate(principal, evidence);
|
||||
LOG.debug("Login identity: " + identity);
|
||||
return Optional.ofNullable(identity);
|
||||
} catch (RealmUnavailableException | SecurityException e) {
|
||||
LOG.warn("Failed to authenticate user " + e.getMessage(), e);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private Tuple2<String, String> extractUsernamePassword(HttpServletRequest httpServletRequest) {
|
||||
Optional<String[]> userPassOptional = extractAuthHeader(httpServletRequest);
|
||||
|
||||
if (userPassOptional.isPresent() && userPassOptional.get().length >= USERPASS_LENGTH) {
|
||||
String[] userpass = userPassOptional.get();
|
||||
String username = userpass[0];
|
||||
String password = userpass.length > USERPASS_LENGTH ? userpass[1] : "";
|
||||
|
||||
return Tuple.of(username, password);
|
||||
}
|
||||
return Tuple.of("", "");
|
||||
}
|
||||
|
||||
private Optional<String[]> extractAuthHeader(HttpServletRequest httpServletRequest) {
|
||||
Optional<String[]> retVal = Optional.empty();
|
||||
|
||||
String authorization = httpServletRequest.getHeader("Authorization");
|
||||
if (authorization != null && StringUtils.length(authorization) > BASIC_HEADER_SIZE) {
|
||||
authorization = authorization.substring(BASIC_HEADER_SIZE);
|
||||
String decoded = StringUtils.toEncodedString(Base64.decodeBase64(authorization), Charset.forName("utf-8"));
|
||||
retVal = Optional.of(decoded.split(":"));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract device information from User-Agent header
|
||||
*
|
||||
* @param request HTTP servlet request
|
||||
* @return Device/browser information
|
||||
*/
|
||||
public String extractDeviceInfo(HttpServletRequest request) {
|
||||
String userAgent = request.getHeader("User-Agent");
|
||||
return userAgent != null ? userAgent : "Unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract client IP address, considering proxies
|
||||
*
|
||||
* @param request HTTP servlet request
|
||||
* @return Client IP address
|
||||
*/
|
||||
public String extractIpAddress(HttpServletRequest request) {
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
|
||||
// X-Forwarded-For can contain multiple IPs, take the first one
|
||||
return xForwardedFor.split(",")[0].trim();
|
||||
}
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||
import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
|
||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Customer;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Picture;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 2 Feb 2026
|
||||
*/
|
||||
@SuppressWarnings({ "java:S818", "squid:S818", "squid:S109" })
|
||||
public class PdfUtils {
|
||||
private static final Log LOG = LogFactory.getLog(PdfUtils.class);
|
||||
|
||||
private static final String FONT_PANTON_REGULAR = "fonts/Panton-Regular.ttf";
|
||||
private static final String FONT_PANTON_BOLD = "fonts/Panton-Bold.ttf";
|
||||
|
||||
private static final Color COLOR_CUSTOMER_NAME = new Color(0x00, 0x45, 0xFF);
|
||||
private static final Color COLOR_DATE = new Color(0x00, 0x16, 0x89);
|
||||
|
||||
private static final Color COLOR_TEXT_GRAY = new Color(0x2F, 0x2F, 0x2F);
|
||||
|
||||
private static final Color COLOR_GREEN = new Color(76, 175, 80);
|
||||
private static final Color COLOR_YELLOW = new Color(255, 193, 7);
|
||||
private static final Color COLOR_RED = new Color(244, 67, 54);
|
||||
private static final Color COLOR_HIGHLIGHT = new Color(41, 98, 175);
|
||||
|
||||
private static final float PAGE_MARGIN = 40F;
|
||||
private static final float CIRCLE_RADIUS = 8F;
|
||||
private static final float HIGHLIGHT_RADIUS = 12F;
|
||||
private static final float CIRCLE_SPACING = 30F;
|
||||
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.GERMAN);
|
||||
|
||||
public byte[] createPdf(Customer customer, List<Picture> pictures) {
|
||||
try (PDDocument document = new PDDocument()) {
|
||||
PDFont fontBold = loadFont(document, FONT_PANTON_BOLD, Standard14Fonts.FontName.HELVETICA_BOLD);
|
||||
PDFont fontRegular = loadFont(document, FONT_PANTON_REGULAR, Standard14Fonts.FontName.HELVETICA);
|
||||
boolean firstPage = true;
|
||||
|
||||
for (Picture picture : pictures) {
|
||||
PDPage page = new PDPage(new PDRectangle(PDRectangle.A4.getHeight(), PDRectangle.A4.getWidth()));
|
||||
document.addPage(page);
|
||||
|
||||
float pageWidth = page.getMediaBox().getWidth();
|
||||
float pageHeight = page.getMediaBox().getHeight();
|
||||
float contentWidth = pageWidth - 2 * PAGE_MARGIN;
|
||||
float halfWidth = contentWidth / 2F;
|
||||
|
||||
try (PDPageContentStream cs = new PDPageContentStream(document, page)) {
|
||||
float yPosition = pageHeight - 50F;
|
||||
|
||||
// Customer name on the first page
|
||||
if (firstPage) {
|
||||
cs.setFont(fontBold, 50);
|
||||
cs.setNonStrokingColor(COLOR_CUSTOMER_NAME);
|
||||
cs.beginText();
|
||||
cs.newLineAtOffset(PAGE_MARGIN, yPosition);
|
||||
cs.showText(nullSafe(customer.getName()));
|
||||
cs.endText();
|
||||
yPosition -= 60F;
|
||||
firstPage = false;
|
||||
}
|
||||
|
||||
// Left side: image (50% of content width)
|
||||
float imageX = PAGE_MARGIN;
|
||||
float imageY = yPosition;
|
||||
float imageMaxWidth = halfWidth - 10F;
|
||||
float imageMaxHeight = pageHeight - 2 * PAGE_MARGIN - 40F;
|
||||
|
||||
if (picture.getImage() != null) {
|
||||
try {
|
||||
byte[] imageBytes = Base64.getDecoder().decode(picture.getImage());
|
||||
PDImageXObject pdImage = PDImageXObject.createFromByteArray(document, imageBytes, "picture");
|
||||
|
||||
float scale = Math.min(imageMaxWidth / pdImage.getWidth(), imageMaxHeight / pdImage.getHeight());
|
||||
float drawWidth = pdImage.getWidth() * scale;
|
||||
float drawHeight = pdImage.getHeight() * scale;
|
||||
|
||||
cs.drawImage(pdImage, imageX, imageY - drawHeight, drawWidth, drawHeight);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to embed image in PDF", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Right side: metadata (top-aligned with image)
|
||||
float rightX = PAGE_MARGIN + halfWidth + 10F;
|
||||
float rightY = imageY - 32F;
|
||||
|
||||
// Date (no label, bold, size 44)
|
||||
String dateStr = picture.getPictureDate() != null ? (DATE_FORMAT.format(picture.getPictureDate()) + " UHR") : "";
|
||||
cs.setFont(fontBold, 32);
|
||||
cs.setNonStrokingColor(COLOR_DATE);
|
||||
cs.beginText();
|
||||
cs.newLineAtOffset(rightX, rightY);
|
||||
cs.showText(dateStr);
|
||||
cs.endText();
|
||||
rightY -= 54F;
|
||||
|
||||
// Customer number
|
||||
float kundenNummerY = rightY;
|
||||
rightY = drawLabel(cs, fontBold, "KUNDENNUMMER", rightX, rightY);
|
||||
rightY = drawValue(cs, fontRegular, nullSafe(customer.getCustomerNumber()), rightX, rightY);
|
||||
rightY -= 10F;
|
||||
|
||||
// Evaluation card with circles
|
||||
float circlesX = rightX + 140F;
|
||||
drawEvaluationCard(cs, fontBold, circlesX, kundenNummerY, picture.getEvaluation());
|
||||
|
||||
// ZIP
|
||||
rightY = drawLabel(cs, fontBold, "PLZ", rightX, rightY);
|
||||
rightY = drawValue(cs, fontRegular, nullSafe(customer.getZip()), rightX, rightY);
|
||||
rightY -= 10F;
|
||||
|
||||
// City
|
||||
rightY = drawLabel(cs, fontBold, "ORT", rightX, rightY);
|
||||
rightY = drawValue(cs, fontRegular, nullSafe(customer.getCity()), rightX, rightY);
|
||||
rightY -= 10F;
|
||||
|
||||
// Comment
|
||||
rightY = drawLabel(cs, fontBold, "KOMMENTAR", rightX, rightY);
|
||||
drawWrappedText(cs, fontRegular, nullSafe(picture.getComment()), rightX, rightY, halfWidth - 20f);
|
||||
}
|
||||
}
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
document.save(output);
|
||||
return output.toByteArray();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to create PDF", e);
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
private PDFont loadFont(PDDocument document, String resourcePath, Standard14Fonts.FontName fallback) {
|
||||
try (InputStream fontStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourcePath)) {
|
||||
if (fontStream != null) {
|
||||
return PDType0Font.load(document, fontStream);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to load font " + resourcePath + ", using fallback", e);
|
||||
}
|
||||
LOG.info("Font " + resourcePath + " not found, using fallback " + fallback.getName());
|
||||
return new PDType1Font(fallback);
|
||||
}
|
||||
|
||||
private float drawLabel(PDPageContentStream cs, PDFont font, String label, float x, float y) throws IOException {
|
||||
cs.setFont(font, 10);
|
||||
cs.setNonStrokingColor(COLOR_CUSTOMER_NAME);
|
||||
cs.beginText();
|
||||
cs.newLineAtOffset(x, y);
|
||||
cs.showText(label);
|
||||
cs.endText();
|
||||
return y - 14F;
|
||||
}
|
||||
|
||||
private float drawValue(PDPageContentStream cs, PDFont font, String value, float x, float y) throws IOException {
|
||||
cs.setFont(font, 10);
|
||||
cs.setNonStrokingColor(COLOR_TEXT_GRAY);
|
||||
cs.beginText();
|
||||
cs.newLineAtOffset(x, y);
|
||||
cs.showText(value);
|
||||
cs.endText();
|
||||
return y - 14F;
|
||||
}
|
||||
|
||||
private void drawWrappedText(PDPageContentStream cs, PDFont font, String text, float x, float y, float maxWidth) throws IOException {
|
||||
if (text == null || text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
cs.setFont(font, 10);
|
||||
cs.setNonStrokingColor(COLOR_TEXT_GRAY);
|
||||
|
||||
String[] words = text.split("\\s+");
|
||||
StringBuilder line = new StringBuilder();
|
||||
float currentY = y;
|
||||
|
||||
for (String word : words) {
|
||||
String testLine = line.isEmpty() ? word : (line + " " + word);
|
||||
float textWidth = font.getStringWidth(testLine) / 1000F * 10F;
|
||||
if (textWidth > maxWidth && !line.isEmpty()) {
|
||||
cs.beginText();
|
||||
cs.newLineAtOffset(x, currentY);
|
||||
cs.showText(line.toString());
|
||||
cs.endText();
|
||||
currentY -= 14F;
|
||||
line = new StringBuilder(word);
|
||||
} else {
|
||||
line = new StringBuilder(testLine);
|
||||
}
|
||||
}
|
||||
if (!line.isEmpty()) {
|
||||
cs.beginText();
|
||||
cs.newLineAtOffset(x, currentY);
|
||||
cs.showText(line.toString());
|
||||
cs.endText();
|
||||
}
|
||||
}
|
||||
|
||||
private void drawEvaluationCard(PDPageContentStream cs, PDFont fontBold, float x, float y, Integer evaluation) throws IOException {
|
||||
int eval = evaluation != null ? evaluation : 0;
|
||||
Color[] colors = { COLOR_GREEN, COLOR_YELLOW, COLOR_RED };
|
||||
|
||||
float cardPadding = 10F;
|
||||
float cardWidth = 2 * CIRCLE_SPACING + 2 * HIGHLIGHT_RADIUS + 2 * cardPadding;
|
||||
float labelHeight = 14F;
|
||||
float cardHeight = labelHeight + 2 * HIGHLIGHT_RADIUS + 2 * cardPadding + 4F;
|
||||
float cardX = x - HIGHLIGHT_RADIUS - cardPadding;
|
||||
float cardY = y - cardHeight + cardPadding;
|
||||
|
||||
// Draw card background (rounded rectangle)
|
||||
cs.setStrokingColor(new Color(0xDD, 0xDD, 0xDD));
|
||||
cs.setNonStrokingColor(new Color(0xF8, 0xF8, 0xF8));
|
||||
cs.setLineWidth(1f);
|
||||
drawRoundedRect(cs, cardX, cardY, cardWidth, cardHeight, 6F);
|
||||
cs.fillAndStroke();
|
||||
|
||||
// Draw "BEWERTUNG" label above circles
|
||||
float labelX = x;
|
||||
float labelY = y - labelHeight;
|
||||
cs.setFont(fontBold, 9);
|
||||
cs.setNonStrokingColor(COLOR_CUSTOMER_NAME);
|
||||
cs.beginText();
|
||||
cs.newLineAtOffset(labelX, labelY);
|
||||
cs.showText("BEWERTUNG");
|
||||
cs.endText();
|
||||
|
||||
// Draw circles below the label
|
||||
float circleY = labelY - cardPadding - HIGHLIGHT_RADIUS - 2F;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
float cx = x + i * CIRCLE_SPACING;
|
||||
|
||||
// Highlight circle if this matches the evaluation (1=green, 2=yellow, 3=red)
|
||||
if (eval == i + 1) {
|
||||
cs.setStrokingColor(COLOR_HIGHLIGHT);
|
||||
cs.setLineWidth(2F);
|
||||
drawCircle(cs, cx, circleY, HIGHLIGHT_RADIUS);
|
||||
cs.stroke();
|
||||
}
|
||||
|
||||
// Filled color circle
|
||||
cs.setNonStrokingColor(colors[i]);
|
||||
drawCircle(cs, cx, circleY, CIRCLE_RADIUS);
|
||||
cs.fill();
|
||||
}
|
||||
}
|
||||
|
||||
private void drawRoundedRect(PDPageContentStream cs, float x, float y, float w, float h, float r) throws IOException {
|
||||
cs.moveTo(x + r, y);
|
||||
cs.lineTo(x + w - r, y);
|
||||
cs.curveTo(x + w, y, x + w, y, x + w, y + r);
|
||||
cs.lineTo(x + w, y + h - r);
|
||||
cs.curveTo(x + w, y + h, x + w, y + h, x + w - r, y + h);
|
||||
cs.lineTo(x + r, y + h);
|
||||
cs.curveTo(x, y + h, x, y + h, x, y + h - r);
|
||||
cs.lineTo(x, y + r);
|
||||
cs.curveTo(x, y, x, y, x + r, y);
|
||||
cs.closePath();
|
||||
}
|
||||
|
||||
private void drawCircle(PDPageContentStream cs, float cx, float cy, float r) throws IOException {
|
||||
float k = 0.5523f; // Bezier approximation for circle
|
||||
cs.moveTo(cx - r, cy);
|
||||
cs.curveTo(cx - r, cy + r * k, cx - r * k, cy + r, cx, cy + r);
|
||||
cs.curveTo(cx + r * k, cy + r, cx + r, cy + r * k, cx + r, cy);
|
||||
cs.curveTo(cx + r, cy - r * k, cx + r * k, cy - r, cx, cy - r);
|
||||
cs.curveTo(cx - r * k, cy - r, cx - r, cy - r * k, cx - r, cy);
|
||||
cs.closePath();
|
||||
}
|
||||
|
||||
private String nullSafe(String value) {
|
||||
return value != null ? value : "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 27 Jan 2026
|
||||
*/
|
||||
|
||||
public class StorageUtils {
|
||||
|
||||
public enum StorageState {
|
||||
OK,
|
||||
DUPLICATE,
|
||||
FORBIDDEN,
|
||||
NOT_FOUND,
|
||||
ERROR,
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 19 Jan 2026
|
||||
*/
|
||||
@RequestScoped
|
||||
@Path("customer-picture")
|
||||
public class CustomerPictureResource {
|
||||
|
||||
@EJB
|
||||
private CustomerPictureService customerPictureService;
|
||||
|
||||
@GZIP
|
||||
@POST
|
||||
@Path("")
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest;
|
||||
|
||||
import static marketing.heyday.hartmann.fotodocumentation.rest.jackson.ApplicationConfigApi.JSON_OUT;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jboss.resteasy.annotations.GZIP;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import jakarta.ejb.EJB;
|
||||
import jakarta.enterprise.context.RequestScoped;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.StreamingOutput;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.service.CustomerPictureService;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerListValue;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerValue;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 21 Jan 2026
|
||||
*/
|
||||
@RequestScoped
|
||||
@Path("customer")
|
||||
public class CustomerResource {
|
||||
private static final Log LOG = LogFactory.getLog(CustomerResource.class);
|
||||
|
||||
@Context
|
||||
private UriInfo uriInfo;
|
||||
|
||||
@EJB
|
||||
private CustomerPictureService customerPictureService;
|
||||
|
||||
@GZIP
|
||||
@GET
|
||||
@Path("")
|
||||
@Produces(JSON_OUT)
|
||||
@Operation(summary = "Get customer list")
|
||||
@ApiResponse(responseCode = "200", description = "Successfully retrieved customer list", content = @Content(mediaType = JSON_OUT, array = @ArraySchema(schema = @Schema(implementation = CustomerListValue.class))))
|
||||
public Response doGetCustomerList(@QueryParam("query") String query, @QueryParam("startsWith") String startsWith) {
|
||||
LOG.debug("Query customers for query " + query + " startsWith: " + startsWith);
|
||||
var retVal = customerPictureService.getAll(query, startsWith);
|
||||
return Response.ok().entity(retVal).build();
|
||||
}
|
||||
|
||||
@GZIP
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces(JSON_OUT)
|
||||
@Operation(summary = "Get customer value")
|
||||
@ApiResponse(responseCode = "200", description = "Successfully retrieved customer value", content = @Content(mediaType = JSON_OUT, array = @ArraySchema(schema = @Schema(implementation = CustomerValue.class))))
|
||||
public Response doGetDetailCustomer(@PathParam("id") Long id) {
|
||||
LOG.debug("Get Customer details for id " + id);
|
||||
String baseUrl = uriInfo.getBaseUri().toString();
|
||||
|
||||
var retVal = customerPictureService.get(id, baseUrl);
|
||||
return Response.ok().entity(retVal).build();
|
||||
}
|
||||
|
||||
@GZIP
|
||||
@GET
|
||||
@Path("export/{id}")
|
||||
@Produces("application/pdf")
|
||||
@Operation(summary = "Get Export")
|
||||
@ApiResponse(responseCode = "200", description = "Successfully retrieved export")
|
||||
public Response doExport(@PathParam("id") Long id, @QueryParam("picture") Long pictureId) {
|
||||
LOG.debug("Create export for customer " + id + " with optional param " + pictureId);
|
||||
byte[] pdf = customerPictureService.getExport(id, pictureId);
|
||||
|
||||
if (pdf.length == 0) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
StreamingOutput streamingOutput = (OutputStream output) -> {
|
||||
LOG.debug("Start writing content to OutputStream available bytes");
|
||||
output.write(pdf);
|
||||
};
|
||||
return Response.status(Status.OK).entity(streamingOutput).build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest;
|
||||
|
||||
import static marketing.heyday.hartmann.fotodocumentation.rest.jackson.ApplicationConfigApi.JSON_OUT;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.wildfly.security.auth.server.SecurityIdentity;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.ejb.EJB;
|
||||
import jakarta.enterprise.context.RequestScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.service.LoginService;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.LoginUtils;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.vo.TokenPairValue;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 21 Jan 2026
|
||||
*/
|
||||
|
||||
@RequestScoped
|
||||
@Path("login")
|
||||
@PermitAll
|
||||
public class LoginResource {
|
||||
private static final Log LOG = LogFactory.getLog(LoginResource.class);
|
||||
|
||||
@EJB
|
||||
private LoginService loginService;
|
||||
|
||||
@Inject
|
||||
private LoginUtils loginUtils;
|
||||
|
||||
@GET
|
||||
@Path("/")
|
||||
@Produces(JSON_OUT)
|
||||
@Operation(summary = "Get logged in user")
|
||||
@ApiResponse(responseCode = "200", description = "Successfully retrieved logged in user", content = @Content(mediaType = JSON_OUT, schema = @Schema(implementation = TokenPairValue.class)))
|
||||
@ApiResponse(responseCode = "500", description = "Internal server error")
|
||||
public Response doLogin(@Context HttpServletRequest httpServletRequest) {
|
||||
Optional<SecurityIdentity> identity = loginUtils.authenticate(httpServletRequest);
|
||||
if (identity.isEmpty()) {
|
||||
LOG.debug("identity empty login invalid");
|
||||
return Response.status(Status.UNAUTHORIZED).build();
|
||||
}
|
||||
String username = identity.get().getPrincipal().getName();
|
||||
|
||||
LOG.debug("Login valid returning jwt");
|
||||
String deviceInfo = loginUtils.extractDeviceInfo(httpServletRequest);
|
||||
String ipAddress = loginUtils.extractIpAddress(httpServletRequest);
|
||||
|
||||
var tokenPairValue = loginService.authenticateUser(username, deviceInfo, ipAddress);
|
||||
return Response.ok(tokenPairValue).build();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
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.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.Response.ResponseBuilder;
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
import jakarta.ws.rs.core.StreamingOutput;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.service.PictureService;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.EvaluationUtil;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.utils.StorageUtils.StorageState;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 21 Jan 2026
|
||||
*/
|
||||
@RequestScoped
|
||||
@Path("picture")
|
||||
public class PictureResource {
|
||||
private static final Log LOG = LogFactory.getLog(PictureResource.class);
|
||||
|
||||
@EJB
|
||||
private PictureService pictureService;
|
||||
|
||||
@Inject
|
||||
private EvaluationUtil evaluationUtil;
|
||||
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Operation(summary = "Delete picture from database")
|
||||
@ApiResponse(responseCode = "200", description = "Task successfully deleted")
|
||||
@ApiResponse(responseCode = "404", description = "Task not found")
|
||||
@ApiResponse(responseCode = "403", description = "Insufficient permissions")
|
||||
public Response doDelete(@PathParam("id") Long id) {
|
||||
LOG.debug("Delete picture with id " + id);
|
||||
var state = pictureService.delete(id);
|
||||
return deleteResponse(state).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("evaluation/{id}")
|
||||
@Operation(summary = "Update evaluation for picture data to database")
|
||||
@ApiResponse(responseCode = "200", description = "Task successfully updated")
|
||||
public Response doUpdateEvaluation(@PathParam("id") Long id, @QueryParam("evaluation") Integer value) {
|
||||
if (evaluationUtil.isInValid(value)) {
|
||||
return Response.status(Status.BAD_REQUEST).build();
|
||||
}
|
||||
StorageState state = pictureService.updateEvaluationStatus(id, value);
|
||||
return deleteResponse(state).build();
|
||||
}
|
||||
|
||||
@GZIP
|
||||
@GET
|
||||
@Path("image/{id}")
|
||||
@Produces({ "image/png", "image/jpg" })
|
||||
@Operation(summary = "Get picture")
|
||||
@ApiResponse(responseCode = "200", description = "Successfully retrieved picture")
|
||||
public Response doGetPictureImage(@PathParam("id") Long id, @QueryParam("size") int size) {
|
||||
LOG.debug("Get Picture for id " + id + " with size " + size);
|
||||
byte[] retVal = pictureService.getImage(id, size);
|
||||
if (retVal.length == 0) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
StreamingOutput streamingOutput = (OutputStream output) -> {
|
||||
LOG.debug("Start writing content to OutputStream available bytes");
|
||||
output.write(retVal);
|
||||
};
|
||||
return Response.status(Status.OK).entity(streamingOutput).build();
|
||||
}
|
||||
|
||||
protected ResponseBuilder deleteResponse(StorageState state) {
|
||||
return switch (state) {
|
||||
case OK -> Response.status(Status.OK);
|
||||
case NOT_FOUND -> Response.status(Status.NOT_FOUND);
|
||||
default -> Response.status(Status.INTERNAL_SERVER_ERROR);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
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.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2017</p>
|
||||
* <p>Company: heyday marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @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<Class<?>> getClasses() {
|
||||
Set<Class<?>> retVal = new HashSet<>();
|
||||
retVal.add(OpenApiResource.class);
|
||||
retVal.add(ValidatedMessageBodyReader.class);
|
||||
retVal.add(LoginResource.class);
|
||||
retVal.add(MonitoringResource.class);
|
||||
retVal.add(CustomerPictureResource.class);
|
||||
retVal.add(CustomerResource.class);
|
||||
retVal.add(PictureResource.class);
|
||||
LOG.info("returning rest api classes " + retVal);
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2017</p>
|
||||
* <p>Company: heyday marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: Feb 10, 2017
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value={ElementType.PARAMETER})
|
||||
public @interface JsonSchemaValidate {
|
||||
String value();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest.jackson;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2017</p>
|
||||
* <p>Company: heyday marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: Feb 10, 2017
|
||||
*/
|
||||
public interface SchemaValidated {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
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.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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;
|
||||
|
||||
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.core.Response;
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
import jakarta.ws.rs.ext.MessageBodyReader;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 6 Dec 2024
|
||||
*/
|
||||
@Provider
|
||||
@Consumes(value = {
|
||||
MediaType.APPLICATION_JSON, "application/json",
|
||||
MediaType.APPLICATION_JSON, "application/json; charset=utf-8",
|
||||
})
|
||||
public class ValidatedMessageBodyReader implements MessageBodyReader<SchemaValidated> {
|
||||
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<SchemaValidated> classType, Type type, Annotation[] annotations, MediaType mediaType,
|
||||
MultivaluedMap<String, String> httpHeaders, InputStream input) throws IOException {
|
||||
final String jsonData = read(input);
|
||||
|
||||
Optional<JsonSchemaValidate> 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()) {
|
||||
var response = Response.status(Status.BAD_REQUEST).entity(reply.errors()).build();
|
||||
throw new WebApplicationException(response);
|
||||
}
|
||||
}
|
||||
|
||||
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<ValidationMessage> errors = schema.validate(node);
|
||||
if (!errors.isEmpty()) {
|
||||
LOG.error("Failed to validate json to schema " + schemaPath);
|
||||
errors.forEach(LOG::error);
|
||||
}
|
||||
return new ValidationReply(errors.isEmpty(), errors);
|
||||
} catch (IOException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
return new ValidationReply(false, new HashSet<>());
|
||||
}
|
||||
}
|
||||
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest.jackson;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.networknt.schema.ValidationMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2017</p>
|
||||
* <p>Company: heyday marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: Feb 14, 2017
|
||||
*/
|
||||
public record ValidationReply(boolean success, Set<ValidationMessage> errors) {
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest.vo;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Customer;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Picture;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 19 Jan 2026
|
||||
*/
|
||||
|
||||
@Schema(name = "CustomerList")
|
||||
public record CustomerListValue(Long id, String name, String customerNumber, Date lastUpdateDate) {
|
||||
|
||||
public static CustomerListValue builder(Customer customer) {
|
||||
if (customer == null) {
|
||||
return null;
|
||||
}
|
||||
Date date = customer.getPictures().stream().map(Picture::getPictureDate).sorted((p1, p2) -> p2.compareTo(p1)).findFirst().orElse(null);
|
||||
return new CustomerListValue(customer.getCustomerId(), customer.getName(), customer.getCustomerNumber(), date);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest.vo;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import marketing.heyday.hartmann.fotodocumentation.rest.jackson.SchemaValidated;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 19 Jan 2026
|
||||
*/
|
||||
@Schema(name = "CustomerPictureUpload")
|
||||
public record CustomerPictureValue(String username, String pharmacyName, String customerNumber, Date date, String zip, String city, String comment, String category, String base64String) implements SchemaValidated {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest.vo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Customer;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 22 Jan 2026
|
||||
*/
|
||||
@Schema(name = "Customer")
|
||||
public record CustomerValue(Long id, String name, String customerNumber, String city, String zip, List<PictureValue> pictures) {
|
||||
|
||||
public static CustomerValue builder(Customer customer, String baseUrl) {
|
||||
if (customer == null) {
|
||||
return null;
|
||||
}
|
||||
List<PictureValue> pictures = customer.getPictures().parallelStream().map(p -> PictureValue.builder(p, baseUrl)).filter(p -> p != null).toList();
|
||||
return new CustomerValue(customer.getCustomerId(), customer.getName(), customer.getCustomerNumber(), customer.getCity(), customer.getZip(), pictures);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest.vo;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Picture;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 22 Jan 2026
|
||||
*/
|
||||
|
||||
@Schema(name = "Picture")
|
||||
public record PictureValue(Long id, String comment, String category, Date pictureDate, String username, Integer evaluation, String imageUrl, String normalSizeUrl, String thumbnailSizeUrl) {
|
||||
|
||||
public static PictureValue builder(Picture picture, String baseUrl) {
|
||||
if (picture == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String sizeUrl = baseUrl;
|
||||
// we need to rewrite the url for dev/integ/prod since the Wildfly doesn't know we are running the nginx on https. Without the https the images are not shown
|
||||
if(baseUrl.startsWith("http://") && !baseUrl.startsWith("http://localhost")){
|
||||
sizeUrl = "https://" + baseUrl.substring(7);
|
||||
}
|
||||
|
||||
sizeUrl = sizeUrl + "picture/image/" + picture.getPictureId() + "?size=";
|
||||
|
||||
String imageUrl = sizeUrl + "1";
|
||||
String normalSizeUrl = sizeUrl + "2";
|
||||
String thumbnailSizeUrl = sizeUrl + "3";
|
||||
return new PictureValue(picture.getPictureId(), picture.getComment(), picture.getCategory(), picture.getPictureDate(), picture.getUsername(), picture.getEvaluation(), imageUrl, normalSizeUrl, thumbnailSizeUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 21 Jan 2026
|
||||
*/
|
||||
|
||||
@Schema(name = "TokenPair")
|
||||
public record TokenPairValue(String accessToken, String refreshToken) {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TokenPair{" + "accessToken='" + (accessToken != null ? "[REDACTED]" : "null") + '\'' + ", refreshToken='" + (refreshToken != null ? "[REDACTED]" : "null") + '\'' + '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
|
||||
version="4.0" bean-discovery-mode="all">
|
||||
</beans>
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
<persistence version="3.0" xmlns="https://jakarta.ee/xml/ns/persistence"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence
|
||||
https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
|
||||
|
||||
<persistence-unit name="auth" transaction-type="JTA">
|
||||
<jta-data-source>java:/jdbc/fotoDocumentationDS</jta-data-source>
|
||||
|
||||
<class>marketing.heyday.hartmann.fotodocumentation.core.model.Right</class>
|
||||
<class>marketing.heyday.hartmann.fotodocumentation.core.model.User</class>
|
||||
<class>marketing.heyday.hartmann.fotodocumentation.core.model.Customer</class>
|
||||
<class>marketing.heyday.hartmann.fotodocumentation.core.model.Picture</class>
|
||||
<class>marketing.heyday.hartmann.fotodocumentation.core.model.JwtRefreshToken</class>
|
||||
|
||||
<properties>
|
||||
<property name="hibernate.format_sql" value="false" />
|
||||
<property name="hibernate.show_sql" value="false" />
|
||||
<!-- <property name="hibernate.archive.autodetection" value="class" /> -->
|
||||
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
|
||||
<property name="hibernate.hbm2ddl.auto" value="none" />
|
||||
<property name="hibernate.jpa.compliance.query" value="false" />
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
||||
@@ -0,0 +1,120 @@
|
||||
|
||||
-- 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)
|
||||
);
|
||||
|
||||
|
||||
-- jwt_refresh_token
|
||||
|
||||
CREATE SEQUENCE jwt_refresh_token_seq START 1 INCREMENT 1;
|
||||
|
||||
CREATE TABLE jwt_refresh_token (
|
||||
jwt_refresh_token_id BIGINT PRIMARY KEY,
|
||||
token_hash VARCHAR(255) NOT NULL, -- SHA-256 hash of refresh token
|
||||
device_info VARCHAR(255), -- Browser/device identifier
|
||||
ip_address VARCHAR(45),
|
||||
issued_at TIMESTAMP NOT NULL,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
revoked_at TIMESTAMP,
|
||||
last_used_at TIMESTAMP,
|
||||
user_id_fk BIGINT NOT NULL REFERENCES x_user(user_id),
|
||||
UNIQUE (token_hash)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_jwt_refresh_token_user ON jwt_refresh_token(user_id_fk);
|
||||
CREATE INDEX idx_jwt_refresh_token_expires ON jwt_refresh_token(expires_at);
|
||||
|
||||
|
||||
|
||||
-- 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
|
||||
);
|
||||
|
||||
|
||||
-- initial users
|
||||
|
||||
insert into x_right (right_id, code, name,jpa_active,jpa_created,jpa_updated,jpa_version) VALUES
|
||||
(1, 'ADMIN', 'Admin Right', true,TIMESTAMP '2026-01-20 10:09:30.009',TIMESTAMP '2026-01-20 10:09:30.009',0),
|
||||
(2, 'USER', 'User Right', true,TIMESTAMP '2026-01-20 10:09:52.797',TIMESTAMP '2026-01-20 10:09:52.797',0)
|
||||
;
|
||||
|
||||
-- nvlev4YnTi
|
||||
-- x1t0e7Pb49
|
||||
|
||||
INSERT INTO x_user (user_id,username,password,salt,title,firstname,lastname,email,jpa_active,jpa_created,jpa_updated,jpa_version)
|
||||
VALUES
|
||||
(1,'hartmann','vPsg/G5xQWoJTOA0r9b9HPTEAzMktKg7fKCrnmHYcyQ=', '9bARmw6zzbXPg4qdbj5RAe2OlJ9mz0Lpq3ZKJlg8Iug=','Herr','Hartmann','Admin','admin@heyday.marketing',true,TIMESTAMP '2026-01-20 10:09:52.000',TIMESTAMP '2026-01-20 10:09:52.000',0),
|
||||
(2,'adm','eXlSEtLDfqos/w0DqPQiVoJHVEQaqLwD7qeDx74Onmk=','vajK924ZRXNWmt9GkcK/BO/Oc1bYp582MJ47HzsXyzA=','Herr','Hartmann','adm','adm@heyday.marketing',true,TIMESTAMP '2026-01-20 10:09:52.000',TIMESTAMP '2026-01-20 10:09:52.000',0);
|
||||
|
||||
|
||||
INSERT INTO user_to_right (user_id_fk,right_id_fk)
|
||||
VALUES
|
||||
(1,1),
|
||||
(2,2);
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
-- picture
|
||||
|
||||
alter table picture add column category varchar(250);
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
-- picture
|
||||
|
||||
alter table picture add column evaluation bigint;
|
||||
|
||||
update picture set evaluation = 0;
|
||||
|
||||
alter table picture alter column evaluation set not null;
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
-- customer
|
||||
|
||||
alter table customer add column zip varchar(150);
|
||||
|
||||
alter table customer add column city varchar(150);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"$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"
|
||||
},
|
||||
"zip": {
|
||||
"description": "The zip from the customer",
|
||||
"type": "string"
|
||||
},
|
||||
"city": {
|
||||
"description": "The city from the customer",
|
||||
"type": "string"
|
||||
},
|
||||
"base64String": {
|
||||
"description": "The Picture content as base64 ",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"username",
|
||||
"pharmacyName",
|
||||
"customerNumber",
|
||||
"date",
|
||||
"comment",
|
||||
"base64String"
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
107
hartmann-foto-documentation-app/src/site/templates/markdown.hbs
Normal file
107
hartmann-foto-documentation-app/src/site/templates/markdown.hbs
Normal file
@@ -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}}
|
||||
## <a name="/definitions/{{key}}">{{@key}}</a>
|
||||
|
||||
<table border="1">
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>type</th>
|
||||
<th>required</th>
|
||||
<th>description</th>
|
||||
<th>example</th>
|
||||
</tr>
|
||||
{{#each this.properties}}
|
||||
<tr>
|
||||
<td>{{@key}}</td>
|
||||
<td>
|
||||
{{#ifeq type "array"}}
|
||||
{{#items.$ref}}
|
||||
{{type}}[<a href="{{items.$ref}}">{{basename items.$ref}}</a>]
|
||||
{{/items.$ref}}
|
||||
{{^items.$ref}}{{type}}[{{items.type}}]{{/items.$ref}}
|
||||
{{else}}
|
||||
{{#$ref}}<a href="{{$ref}}">{{basename $ref}}</a>{{/$ref}}
|
||||
{{^$ref}}{{type}}{{#format}} ({{format}}){{/format}}{{/$ref}}
|
||||
{{/ifeq}}
|
||||
</td>
|
||||
<td>{{#required}}required{{/required}}{{^required}}optional{{/required}}</td>
|
||||
<td>{{#description}}{{{description}}}{{/description}}{{^description}}-{{/description}}</td>
|
||||
<td>{{example}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/each}}
|
||||
@@ -0,0 +1,81 @@
|
||||
{{#deprecated}}-deprecated-{{/deprecated}}
|
||||
<a id="{{operationId}}">{{summary}}</a>
|
||||
|
||||
{{{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}}
|
||||
<table border="1">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Located in</th>
|
||||
<th>Required</th>
|
||||
<th>Description</th>
|
||||
<th>Default</th>
|
||||
<th>Schema</th>
|
||||
<th>Example</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
|
||||
{{#parameters}}
|
||||
<tr>
|
||||
<th>{{name}}</th>
|
||||
<td>{{in}}</td>
|
||||
<td>{{#if required}}yes{{else}}no{{/if}}</td>
|
||||
<td>{{description}}{{#if pattern}} (**Pattern**: `{{pattern}}`){{/if}}</td>
|
||||
<td> - </td>
|
||||
{{#ifeq in "body"}}
|
||||
<td>
|
||||
{{#ifeq schema.type "array"}}Array[<a href="{{schema.items.$ref}}">{{basename schema.items.$ref}}</a>]{{/ifeq}}
|
||||
{{#schema.$ref}}<a href="{{schema.$ref}}">{{basename schema.$ref}}</a> {{/schema.$ref}}
|
||||
</td>
|
||||
{{else}}
|
||||
{{#ifeq type "array"}}
|
||||
<td>Array[{{items.type}}] ({{collectionFormat}})</td>
|
||||
{{else}}
|
||||
<td>{{type}} {{#format}}({{format}}){{/format}}</td>
|
||||
{{/ifeq}}
|
||||
{{/ifeq}}
|
||||
<td>
|
||||
{{#each examples}}
|
||||
{{{this}}}
|
||||
{{/each}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
{{/parameters}}
|
||||
{{#if parameters}}
|
||||
</table>
|
||||
{{/if}}
|
||||
|
||||
|
||||
#### Response
|
||||
|
||||
{{#if produces}}**Content-Type: ** {{join produces ", "}}{{/if}}
|
||||
|
||||
|
||||
| Status Code | Reason | Response Model |
|
||||
|-------------|-------------|----------------|
|
||||
{{#each responses}}| {{@key}} | {{description}} | {{#schema.$ref}}<a href="{{schema.$ref}}">{{basename schema.$ref}}</a>{{/schema.$ref}}{{^schema.$ref}}{{#ifeq schema.type "array"}}Array[<a href="{{schema.items.$ref}}">{{basename schema.items.$ref}}</a>]{{else}}{{schema.type}}{{/ifeq}}{{/schema.$ref}}{{^schema}} - {{/schema}}|
|
||||
{{/each}}
|
||||
@@ -0,0 +1,88 @@
|
||||
{{#each securityDefinitions}}
|
||||
### {{@key}}
|
||||
{{#this}}
|
||||
{{#ifeq type "oauth2"}}
|
||||
<table>
|
||||
<tr>
|
||||
<th>type</th>
|
||||
<th colspan="2">{{type}}</th>
|
||||
</tr>
|
||||
{{#if description}}
|
||||
<tr>
|
||||
<th>description</th>
|
||||
<th colspan="2">{{description}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if authorizationUrl}}
|
||||
<tr>
|
||||
<th>authorizationUrl</th>
|
||||
<th colspan="2">{{authorizationUrl}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if flow}}
|
||||
<tr>
|
||||
<th>flow</th>
|
||||
<th colspan="2">{{flow}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if tokenUrl}}
|
||||
<tr>
|
||||
<th>tokenUrl</th>
|
||||
<th colspan="2">{{tokenUrl}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if scopes}}
|
||||
<tr>
|
||||
<td rowspan="3">scopes</td>
|
||||
{{#each scopes}}
|
||||
<td>{{@key}}</td>
|
||||
<td>{{this}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{/each}}
|
||||
</tr>
|
||||
{{/if}}
|
||||
</table>
|
||||
{{/ifeq}}
|
||||
{{#ifeq type "apiKey"}}
|
||||
<table>
|
||||
<tr>
|
||||
<th>type</th>
|
||||
<th colspan="2">{{type}}</th>
|
||||
</tr>
|
||||
{{#if description}}
|
||||
<tr>
|
||||
<th>description</th>
|
||||
<th colspan="2">{{description}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if name}}
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th colspan="2">{{name}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if in}}
|
||||
<tr>
|
||||
<th>in</th>
|
||||
<th colspan="2">{{in}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
</table>
|
||||
{{/ifeq}}
|
||||
{{#ifeq type "basic"}}
|
||||
<table>
|
||||
<tr>
|
||||
<th>type</th>
|
||||
<th colspan="2">{{type}}</th>
|
||||
</tr>
|
||||
{{#if description}}
|
||||
<tr>
|
||||
<th>description</th>
|
||||
<th colspan="2">{{description}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
</table>
|
||||
{{/ifeq}}
|
||||
{{/this}}
|
||||
{{/each}}
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>API Document</title>
|
||||
|
||||
<xmp theme="united" style="display:none;">
|
||||
{{>markdown}}
|
||||
</xmp>
|
||||
|
||||
<script src="http://strapdownjs.com/v/0.2/strapdown.js"></script>
|
||||
|
||||
<!-- code for TOC (jquery plugin) (see http://projects.jga.me/toc/#toc0) -->
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||
<script src="https://rawgit.com/jgallen23/toc/master/dist/toc.min.js"></script>
|
||||
<script src="https://rawgit.com/zipizap/strapdown_template/master/js/init_TOC.js"></script>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,72 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
import java.util.Base64.Encoder;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 20 Jan 2026
|
||||
*/
|
||||
|
||||
public class SecurityGenerator {
|
||||
private static final int GENERATE_LENGTH = 10;
|
||||
private static final int SALT_LENGTH = 32;
|
||||
|
||||
@java.lang.SuppressWarnings("java:S2245")
|
||||
public String generatePassword() {
|
||||
return RandomStringUtils.randomAlphanumeric(GENERATE_LENGTH);
|
||||
}
|
||||
|
||||
public byte[] createSalt() {
|
||||
byte[] salt = new byte[SALT_LENGTH];
|
||||
SecureRandom random = new SecureRandom();
|
||||
random.nextBytes(salt);
|
||||
return salt;
|
||||
|
||||
}
|
||||
|
||||
public byte[] createPassword(String password, byte[] salt) throws NoSuchAlgorithmException {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] passwordBytes = password.getBytes(Charset.forName("utf-8"));
|
||||
md.update(passwordBytes);
|
||||
md.update(salt);
|
||||
|
||||
return md.digest();
|
||||
}
|
||||
|
||||
public String encode(byte[] hash) {
|
||||
Encoder encoder = Base64.getEncoder();
|
||||
return encoder.encodeToString(hash);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws NoSuchAlgorithmException {
|
||||
String password = generatePassword();
|
||||
assertNotNull(password);
|
||||
|
||||
byte[] salt = createSalt();
|
||||
String saltHash = encode(salt);
|
||||
|
||||
byte[] digest = createPassword(password, salt);
|
||||
String passwordHash = encode(digest);
|
||||
|
||||
System.out.println("Password " + password);
|
||||
System.out.println("PasswordHash " + passwordHash);
|
||||
System.out.println("saltHash " + saltHash);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 3 Feb 2026
|
||||
*/
|
||||
class CalendarUtilTest {
|
||||
|
||||
private CalendarUtil calendarUtil;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
calendarUtil = new CalendarUtil();
|
||||
}
|
||||
|
||||
// --- parse ---
|
||||
|
||||
@Test
|
||||
void parse_ddMMyyyyDot_returnsDate() {
|
||||
Date result = calendarUtil.parse("15.03.2026");
|
||||
|
||||
assertNotNull(result);
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(15, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(Calendar.MARCH, cal.get(Calendar.MONTH));
|
||||
assertEquals(2026, cal.get(Calendar.YEAR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_dMyyyyDot_returnsDate() {
|
||||
Date result = calendarUtil.parse("5.3.2026");
|
||||
|
||||
assertNotNull(result);
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(5, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(Calendar.MARCH, cal.get(Calendar.MONTH));
|
||||
assertEquals(2026, cal.get(Calendar.YEAR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_ddMMyy_returnsDate() {
|
||||
Date result = calendarUtil.parse("15.03.26");
|
||||
|
||||
assertNotNull(result);
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(15, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(Calendar.MARCH, cal.get(Calendar.MONTH));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_ddMMyyyyDash_returnsDate() {
|
||||
Date result = calendarUtil.parse("15-03-2026");
|
||||
|
||||
assertNotNull(result);
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(15, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(Calendar.MARCH, cal.get(Calendar.MONTH));
|
||||
assertEquals(2026, cal.get(Calendar.YEAR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_germanMonthName_dMMMMyyyy_returnsDate() {
|
||||
Date result = calendarUtil.parse("5. Januar 2026");
|
||||
|
||||
assertNotNull(result);
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(5, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(Calendar.JANUARY, cal.get(Calendar.MONTH));
|
||||
assertEquals(2026, cal.get(Calendar.YEAR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_germanMonthName_ddMMMMyyyy_returnsDate() {
|
||||
Date result = calendarUtil.parse("15 Dezember 2025");
|
||||
|
||||
assertNotNull(result);
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(15, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(Calendar.DECEMBER, cal.get(Calendar.MONTH));
|
||||
assertEquals(2025, cal.get(Calendar.YEAR));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_invalidString_returnsNull() {
|
||||
assertNull(calendarUtil.parse("not a date"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_emptyString_returnsNull() {
|
||||
assertNull(calendarUtil.parse(""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parse_null_throwsException() {
|
||||
assertThrows(IllegalArgumentException.class, () -> calendarUtil.parse(null));
|
||||
}
|
||||
|
||||
// --- getStartOfDay ---
|
||||
|
||||
@Test
|
||||
void getStartOfDay_returnsDateAtMidnight() {
|
||||
Calendar input = Calendar.getInstance();
|
||||
input.set(2026, Calendar.MARCH, 15, 14, 30, 45);
|
||||
input.set(Calendar.MILLISECOND, 500);
|
||||
|
||||
Date result = calendarUtil.getStartOfDay(input.getTime());
|
||||
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(2026, cal.get(Calendar.YEAR));
|
||||
assertEquals(Calendar.MARCH, cal.get(Calendar.MONTH));
|
||||
assertEquals(15, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(0, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(0, cal.get(Calendar.MINUTE));
|
||||
assertEquals(0, cal.get(Calendar.SECOND));
|
||||
assertEquals(0, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getStartOfDay_alreadyMidnight_returnsSameTime() {
|
||||
Calendar input = Calendar.getInstance();
|
||||
input.set(2026, Calendar.JANUARY, 1, 0, 0, 0);
|
||||
input.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
Date result = calendarUtil.getStartOfDay(input.getTime());
|
||||
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(0, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(0, cal.get(Calendar.MINUTE));
|
||||
assertEquals(0, cal.get(Calendar.SECOND));
|
||||
assertEquals(0, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getStartOfDay_endOfDay_returnsStartOfSameDay() {
|
||||
Calendar input = Calendar.getInstance();
|
||||
input.set(2026, Calendar.JUNE, 20, 23, 59, 59);
|
||||
input.set(Calendar.MILLISECOND, 999);
|
||||
|
||||
Date result = calendarUtil.getStartOfDay(input.getTime());
|
||||
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(20, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(0, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(0, cal.get(Calendar.MINUTE));
|
||||
assertEquals(0, cal.get(Calendar.SECOND));
|
||||
assertEquals(0, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
// --- getEndOfDay ---
|
||||
|
||||
@Test
|
||||
void getEndOfDay_returnsDateAt235959999() {
|
||||
Calendar input = Calendar.getInstance();
|
||||
input.set(2026, Calendar.MARCH, 15, 10, 0, 0);
|
||||
input.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
Date result = calendarUtil.getEndOfDay(input.getTime());
|
||||
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(2026, cal.get(Calendar.YEAR));
|
||||
assertEquals(Calendar.MARCH, cal.get(Calendar.MONTH));
|
||||
assertEquals(15, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(23, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(59, cal.get(Calendar.MINUTE));
|
||||
assertEquals(59, cal.get(Calendar.SECOND));
|
||||
assertEquals(999, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEndOfDay_fromMidnight_returnsEndOfSameDay() {
|
||||
Calendar input = Calendar.getInstance();
|
||||
input.set(2026, Calendar.JANUARY, 1, 0, 0, 0);
|
||||
input.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
Date result = calendarUtil.getEndOfDay(input.getTime());
|
||||
|
||||
Calendar cal = toCalendar(result);
|
||||
assertEquals(1, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(23, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(59, cal.get(Calendar.MINUTE));
|
||||
assertEquals(59, cal.get(Calendar.SECOND));
|
||||
assertEquals(999, cal.get(Calendar.MILLISECOND));
|
||||
}
|
||||
|
||||
// --- startOfDay / endOfDay consistency ---
|
||||
|
||||
@Test
|
||||
void startOfDay_isBeforeEndOfDay() {
|
||||
Date now = new Date();
|
||||
|
||||
Date start = calendarUtil.getStartOfDay(now);
|
||||
Date end = calendarUtil.getEndOfDay(now);
|
||||
|
||||
assertTrue(start.before(end));
|
||||
}
|
||||
|
||||
@Test
|
||||
void startAndEnd_preserveSameDay() {
|
||||
Calendar input = Calendar.getInstance();
|
||||
input.set(2026, Calendar.JULY, 4, 12, 0, 0);
|
||||
|
||||
Date start = calendarUtil.getStartOfDay(input.getTime());
|
||||
Date end = calendarUtil.getEndOfDay(input.getTime());
|
||||
|
||||
Calendar startCal = toCalendar(start);
|
||||
Calendar endCal = toCalendar(end);
|
||||
assertEquals(startCal.get(Calendar.YEAR), endCal.get(Calendar.YEAR));
|
||||
assertEquals(startCal.get(Calendar.MONTH), endCal.get(Calendar.MONTH));
|
||||
assertEquals(startCal.get(Calendar.DAY_OF_MONTH), endCal.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
|
||||
private Calendar toCalendar(Date date) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(date);
|
||||
return cal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 3 Feb 2026
|
||||
*/
|
||||
class EvaluationUtilTest {
|
||||
|
||||
private EvaluationUtil evaluationUtil;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
evaluationUtil = new EvaluationUtil();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInValid_null_returnsTrue() {
|
||||
assertTrue(evaluationUtil.isInValid(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInValid_zero_returnsTrue() {
|
||||
assertTrue(evaluationUtil.isInValid(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInValid_negativeValue_returnsTrue() {
|
||||
assertTrue(evaluationUtil.isInValid(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInValid_one_returnsFalse() {
|
||||
assertFalse(evaluationUtil.isInValid(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInValid_two_returnsFlse() {
|
||||
assertFalse(evaluationUtil.isInValid(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInValid_three_returnsFalse() {
|
||||
assertFalse(evaluationUtil.isInValid(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInValid_largeValue_returnsTrue() {
|
||||
assertTrue(evaluationUtil.isInValid(100));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 3 Feb 2026
|
||||
*/
|
||||
class ImageUtilTest {
|
||||
|
||||
private ImageUtil imageUtil;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
imageUtil = new ImageUtil();
|
||||
}
|
||||
|
||||
// --- Size 1: original ---
|
||||
|
||||
@Test
|
||||
void getImage_size1_returnsOriginalBytes() {
|
||||
String base64 = createTestImageBase64(800, 600);
|
||||
byte[] original = Base64.getDecoder().decode(base64);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 1);
|
||||
|
||||
assertArrayEquals(original, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImage_size1_largeImage_returnsOriginalBytes() {
|
||||
String base64 = createTestImageBase64(2000, 1500);
|
||||
byte[] original = Base64.getDecoder().decode(base64);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 1);
|
||||
|
||||
assertArrayEquals(original, result);
|
||||
}
|
||||
|
||||
// --- Size 2: normal (web, max 1200px) ---
|
||||
|
||||
@Test
|
||||
void getImage_size2_largeImage_resizesToMaxWidth1200() throws IOException {
|
||||
String base64 = createTestImageBase64(2400, 1600);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 2);
|
||||
|
||||
BufferedImage resized = ImageIO.read(new ByteArrayInputStream(result));
|
||||
assertEquals(1200, resized.getWidth());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImage_size2_largeImage_preservesAspectRatio() throws IOException {
|
||||
String base64 = createTestImageBase64(2400, 1600);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 2);
|
||||
|
||||
BufferedImage resized = ImageIO.read(new ByteArrayInputStream(result));
|
||||
assertEquals(1200, resized.getWidth());
|
||||
assertEquals(800, resized.getHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImage_size2_largeImage_resultIsSmallerThanOriginal() {
|
||||
String base64 = createNoisyImageBase64(2400, 1600);
|
||||
byte[] original = Base64.getDecoder().decode(base64);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 2);
|
||||
|
||||
assertTrue(result.length < original.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImage_size2_smallImage_returnsOriginal() {
|
||||
String base64 = createTestImageBase64(800, 600);
|
||||
byte[] original = Base64.getDecoder().decode(base64);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 2);
|
||||
|
||||
assertArrayEquals(original, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImage_size2_exactMaxWidth_returnsOriginal() {
|
||||
String base64 = createTestImageBase64(1200, 900);
|
||||
byte[] original = Base64.getDecoder().decode(base64);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 2);
|
||||
|
||||
assertArrayEquals(original, result);
|
||||
}
|
||||
|
||||
// --- Size 3: thumbnail (max 200px) ---
|
||||
|
||||
@Test
|
||||
void getImage_size3_largeImage_resizesToMaxWidth200() throws IOException {
|
||||
String base64 = createTestImageBase64(2400, 1600);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 3);
|
||||
|
||||
BufferedImage resized = ImageIO.read(new ByteArrayInputStream(result));
|
||||
assertEquals(200, resized.getWidth());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImage_size3_largeImage_preservesAspectRatio() throws IOException {
|
||||
String base64 = createTestImageBase64(2400, 1600);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 3);
|
||||
|
||||
BufferedImage resized = ImageIO.read(new ByteArrayInputStream(result));
|
||||
assertEquals(200, resized.getWidth());
|
||||
assertEquals(133, resized.getHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImage_size3_largeImage_resultIsSmallerThanNormal() {
|
||||
String base64 = createTestImageBase64(2400, 1600);
|
||||
|
||||
byte[] normal = imageUtil.getImage(base64, 2);
|
||||
byte[] thumbnail = imageUtil.getImage(base64, 3);
|
||||
|
||||
assertTrue(thumbnail.length < normal.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImage_size3_smallImage_returnsOriginal() {
|
||||
String base64 = createTestImageBase64(150, 100);
|
||||
byte[] original = Base64.getDecoder().decode(base64);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 3);
|
||||
|
||||
assertArrayEquals(original, result);
|
||||
}
|
||||
|
||||
// --- Default size ---
|
||||
|
||||
@Test
|
||||
void getImage_unknownSize_returnsOriginalBytes() {
|
||||
String base64 = createTestImageBase64(800, 600);
|
||||
byte[] original = Base64.getDecoder().decode(base64);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 99);
|
||||
|
||||
assertArrayEquals(original, result);
|
||||
}
|
||||
|
||||
// --- Output format ---
|
||||
|
||||
@Test
|
||||
void getImage_size2_outputIsJpeg() {
|
||||
String base64 = createTestImageBase64(2400, 1600);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 2);
|
||||
|
||||
// JPEG files start with FF D8 FF
|
||||
assertEquals((byte) 0xFF, result[0]);
|
||||
assertEquals((byte) 0xD8, result[1]);
|
||||
assertEquals((byte) 0xFF, result[2]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImage_size3_outputIsJpeg() {
|
||||
String base64 = createTestImageBase64(2400, 1600);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 3);
|
||||
|
||||
assertEquals((byte) 0xFF, result[0]);
|
||||
assertEquals((byte) 0xD8, result[1]);
|
||||
assertEquals((byte) 0xFF, result[2]);
|
||||
}
|
||||
|
||||
// --- Edge cases ---
|
||||
|
||||
@Test
|
||||
void getImage_size2_squareImage_preservesAspectRatio() throws IOException {
|
||||
String base64 = createTestImageBase64(2000, 2000);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 2);
|
||||
|
||||
BufferedImage resized = ImageIO.read(new ByteArrayInputStream(result));
|
||||
assertEquals(1200, resized.getWidth());
|
||||
assertEquals(1200, resized.getHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getImage_size2_veryWideImage_preservesAspectRatio() throws IOException {
|
||||
String base64 = createTestImageBase64(4000, 500);
|
||||
|
||||
byte[] result = imageUtil.getImage(base64, 2);
|
||||
|
||||
BufferedImage resized = ImageIO.read(new ByteArrayInputStream(result));
|
||||
assertEquals(1200, resized.getWidth());
|
||||
assertEquals(150, resized.getHeight());
|
||||
}
|
||||
|
||||
private String createTestImageBase64(int width, int height) {
|
||||
try {
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "png", baos);
|
||||
return Base64.getEncoder().encodeToString(baos.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String createNoisyImageBase64(int width, int height) {
|
||||
try {
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
java.util.Random random = new java.util.Random(42);
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
image.setRGB(x, y, random.nextInt(0xFFFFFF));
|
||||
}
|
||||
}
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "png", baos);
|
||||
return Base64.getEncoder().encodeToString(baos.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.core.utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.text.PDFTextStripper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Customer;
|
||||
import marketing.heyday.hartmann.fotodocumentation.core.model.Picture;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 2 Feb 2026
|
||||
*/
|
||||
class PdfUtilsTest {
|
||||
private static final Log LOG = LogFactory.getLog(PdfUtilsTest.class);
|
||||
|
||||
private PdfUtils pdfUtils;
|
||||
private Customer customer;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
pdfUtils = new PdfUtils();
|
||||
customer = new Customer.Builder()
|
||||
.name("Apotheke Musterstadt")
|
||||
.customerNumber("KD-12345")
|
||||
.zip("50667")
|
||||
.city("Köln")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_singlePicture_returnsValidPdf() throws IOException {
|
||||
Picture picture = createPicture(new Date(), "Schaufenster Dekoration", 1);
|
||||
List<Picture> pictures = List.of(picture);
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, pictures);
|
||||
|
||||
assertNotNull(pdfBytes);
|
||||
assertTrue(pdfBytes.length > 0);
|
||||
|
||||
try (PDDocument document = Loader.loadPDF(pdfBytes)) {
|
||||
assertEquals(1, document.getNumberOfPages());
|
||||
}
|
||||
writeToFile(pdfBytes, "createPdf_singlePicture_returnsValidPdf.pdf");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_multiplePictures_createsOnPagePerPicture() throws IOException {
|
||||
List<Picture> pictures = List.of(
|
||||
createPicture(new Date(), "Bild 1", 1),
|
||||
createPicture(new Date(), longComment, 2),
|
||||
createPicture(new Date(), "Bild 3", 3));
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, pictures);
|
||||
|
||||
try (PDDocument document = Loader.loadPDF(pdfBytes)) {
|
||||
assertEquals(3, document.getNumberOfPages());
|
||||
}
|
||||
writeToFile(pdfBytes, "createPdf_multiplePictures_createsOnPagePerPicture.pdf");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_containsCustomerName() throws IOException {
|
||||
Picture picture = createPicture(new Date(), "Test", 1);
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, List.of(picture));
|
||||
|
||||
String text = extractText(pdfBytes);
|
||||
assertTrue(text.contains("Apotheke Musterstadt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_containsCustomerNumber() throws IOException {
|
||||
Picture picture = createPicture(new Date(), "Test", 1);
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, List.of(picture));
|
||||
|
||||
String text = extractText(pdfBytes);
|
||||
assertTrue(text.contains("KD-12345"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_containsLabels() throws IOException {
|
||||
Picture picture = createPicture(new Date(), "Test Kommentar", 2);
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, List.of(picture));
|
||||
|
||||
String text = extractText(pdfBytes);
|
||||
assertTrue(text.contains("KUNDENNUMMER"));
|
||||
assertTrue(text.contains("PLZ"));
|
||||
assertTrue(text.contains("ORT"));
|
||||
assertTrue(text.contains("KOMMENTAR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_containsZipAndCity() throws IOException {
|
||||
Picture picture = createPicture(new Date(), "Test", 1);
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, List.of(picture));
|
||||
|
||||
String text = extractText(pdfBytes);
|
||||
assertTrue(text.contains("50667"));
|
||||
assertTrue(text.contains("Köln"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_containsComment() throws IOException {
|
||||
Picture picture = createPicture(new Date(), "Wichtiger Kommentar zum Bild", 1);
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, List.of(picture));
|
||||
|
||||
String text = extractText(pdfBytes);
|
||||
assertTrue(text.contains("Wichtiger Kommentar zum Bild"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_emptyPictureList_returnsValidEmptyPdf() throws IOException {
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, Collections.emptyList());
|
||||
|
||||
assertNotNull(pdfBytes);
|
||||
assertTrue(pdfBytes.length > 0);
|
||||
|
||||
try (PDDocument document = Loader.loadPDF(pdfBytes)) {
|
||||
assertEquals(0, document.getNumberOfPages());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_nullImage_doesNotThrow() throws IOException {
|
||||
Picture picture = new Picture.Builder()
|
||||
.pictureDate(new Date())
|
||||
.comment("Ohne Bild")
|
||||
.customer(customer)
|
||||
.build();
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, List.of(picture));
|
||||
|
||||
assertNotNull(pdfBytes);
|
||||
try (PDDocument document = Loader.loadPDF(pdfBytes)) {
|
||||
assertEquals(1, document.getNumberOfPages());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_nullComment_doesNotThrow() throws IOException {
|
||||
Picture picture = new Picture.Builder()
|
||||
.pictureDate(new Date())
|
||||
.image(createTestImageBase64())
|
||||
.customer(customer)
|
||||
.build();
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, List.of(picture));
|
||||
|
||||
assertNotNull(pdfBytes);
|
||||
try (PDDocument document = Loader.loadPDF(pdfBytes)) {
|
||||
assertEquals(1, document.getNumberOfPages());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_nullDate_doesNotThrow() throws IOException {
|
||||
Picture picture = new Picture.Builder()
|
||||
.comment("Kein Datum")
|
||||
.image(createTestImageBase64())
|
||||
.customer(customer)
|
||||
.build();
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, List.of(picture));
|
||||
|
||||
assertNotNull(pdfBytes);
|
||||
try (PDDocument document = Loader.loadPDF(pdfBytes)) {
|
||||
assertEquals(1, document.getNumberOfPages());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_nullEvaluation_doesNotThrow() {
|
||||
Picture picture = new Picture.Builder()
|
||||
.pictureDate(new Date())
|
||||
.comment("Test")
|
||||
.image(createTestImageBase64())
|
||||
.customer(customer)
|
||||
.build();
|
||||
// evaluation defaults to 0 in Builder
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, List.of(picture));
|
||||
|
||||
assertNotNull(pdfBytes);
|
||||
assertTrue(pdfBytes.length > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_allEvaluationValues_produceValidPdf() throws IOException {
|
||||
for (int eval = 1; eval <= 3; eval++) {
|
||||
Picture picture = createPicture(new Date(), "Eval " + eval, eval);
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(customer, List.of(picture));
|
||||
|
||||
assertNotNull(pdfBytes);
|
||||
try (PDDocument document = Loader.loadPDF(pdfBytes)) {
|
||||
assertEquals(1, document.getNumberOfPages());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPdf_nullFieldsOnCustomer_doesNotThrow() throws IOException {
|
||||
Customer emptyCustomer = new Customer.Builder()
|
||||
.name("Test")
|
||||
.customerNumber("000")
|
||||
.build();
|
||||
|
||||
Picture picture = createPicture(new Date(), "Test", 1);
|
||||
|
||||
byte[] pdfBytes = pdfUtils.createPdf(emptyCustomer, List.of(picture));
|
||||
|
||||
assertNotNull(pdfBytes);
|
||||
try (PDDocument document = Loader.loadPDF(pdfBytes)) {
|
||||
assertEquals(1, document.getNumberOfPages());
|
||||
}
|
||||
}
|
||||
|
||||
private Picture createPicture(Date date, String comment, int evaluation) {
|
||||
return new Picture.Builder()
|
||||
.pictureDate(date)
|
||||
.comment(comment)
|
||||
.evaluation(evaluation)
|
||||
.image(createTestImageBase64())
|
||||
.customer(customer)
|
||||
.build();
|
||||
}
|
||||
|
||||
private String createTestImageBase64() {
|
||||
try {
|
||||
BufferedImage image = new BufferedImage(100, 80, BufferedImage.TYPE_INT_RGB);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "png", baos);
|
||||
return Base64.getEncoder().encodeToString(baos.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String extractText(byte[] pdfBytes) throws IOException {
|
||||
try (PDDocument document = Loader.loadPDF(pdfBytes)) {
|
||||
PDFTextStripper stripper = new PDFTextStripper();
|
||||
return stripper.getText(document);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeToFile(final byte[] content, final 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 saveing pdf file", e);
|
||||
}
|
||||
}
|
||||
|
||||
String longComment = "This is a sample text used for unit testing purposes. It contains multiple sentences with different structures, punctuation marks, and line breaks. The goal is to simulate realistic content that a program might process during normal execution. Developers often need such text to verify that parsing, searching, filtering, or transformation logic behaves as expected.\n"
|
||||
+ "\n"
|
||||
+ "The text includes numbers like 12345, special characters such as @, #, and %, and mixed casing to ensure case-insensitive comparisons can be tested properly. It also contains repeated keywords like skillmatrix and SkillMatrix to validate string matching and normalization features.\n"
|
||||
+ "\n"
|
||||
+ "Additionally, this paragraph spans several lines to test newline handling and formatting behavior. Unit tests may check whether the system correctly reads files, counts words, trims whitespace, or handles empty lines without errors.\n"
|
||||
+ "\n"
|
||||
+ "Overall, this content is intentionally generic but sufficiently detailed to serve as stable input data for automated tests.";
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.mockito.configuration;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2016</p>
|
||||
* <p>Company: heyday marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: Oct 14, 2016
|
||||
*/
|
||||
public class MockitoConfiguration extends DefaultMockitoConfiguration {
|
||||
|
||||
@Override
|
||||
public boolean enableClassCache() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
64
hartmann-foto-documentation-app/src/test/resources/log4j.xml
Normal file
64
hartmann-foto-documentation-app/src/test/resources/log4j.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
|
||||
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
|
||||
<param name="Target" value="System.out"/>
|
||||
<param name="Threshold" value="DEBUG"/>
|
||||
<layout class="org.apache.log4j.PatternLayout">
|
||||
<!-- The default pattern: Date Priority [Category] Message\n -->
|
||||
<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c] %m%n"/>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
|
||||
<!-- ================ -->
|
||||
<!-- Limit categories -->
|
||||
<!-- ================ -->
|
||||
<!-- Limit the org.apache category to INFO as its DEBUG is verbose -->
|
||||
<category name="org.apache">
|
||||
<priority value="INFO"/>
|
||||
</category>
|
||||
<category name="com.bm">
|
||||
<priority value="INFO"/>
|
||||
</category>
|
||||
<category name="com.bm.introspectors">
|
||||
<priority value="ERROR"/>
|
||||
</category>
|
||||
|
||||
<category name="org.hibernate.cfg.annotations">
|
||||
<priority value="WARN"/>
|
||||
</category>
|
||||
|
||||
<category name="org.hibernate.cfg">
|
||||
<priority value="WARN"/>
|
||||
</category>
|
||||
|
||||
<category name="org.hibernate.tool">
|
||||
<priority value="WARN"/>
|
||||
</category>
|
||||
|
||||
<category name="org.hibernate.validator">
|
||||
<priority value="WARN"/>
|
||||
</category>
|
||||
|
||||
<category name="org.hibernate">
|
||||
<priority value="ERROR"/>
|
||||
</category>
|
||||
|
||||
|
||||
<category name="org.dbunit">
|
||||
<priority value="DEBUG"/>
|
||||
</category>
|
||||
|
||||
<category name="de.juwimm">
|
||||
<priority value="DEBUG"/>
|
||||
</category>
|
||||
|
||||
<category name="STDOUT">
|
||||
<priority value="DEBUG"/>
|
||||
</category>
|
||||
|
||||
<root>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
</log4j:configuration>
|
||||
530
hartmann-foto-documentation-docker/pom.xml
Normal file
530
hartmann-foto-documentation-docker/pom.xml
Normal file
@@ -0,0 +1,530 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>marketing.heyday.hartmann.fotodocumentation</groupId>
|
||||
<artifactId>hartmann-foto-documentation</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<relativePath>../hartmann-foto-documentation/pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>hartmann-foto-documentation-docker</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<name>hartmann-foto-documentation docker</name>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<skipDeploy>true</skipDeploy>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M1</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
<testFailureIgnore>true</testFailureIgnore>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<properties>
|
||||
<sonar.java.libraries>${project.build.directory}/lib/*.jar</sonar.java.libraries>
|
||||
<sonar.libraries>${project.build.directory}/lib/*.jar</sonar.libraries>
|
||||
</properties>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>docker</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<skipDeploy>true</skipDeploy>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M1</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
<skipTests>true</skipTests>
|
||||
<testFailureIgnore>true</testFailureIgnore>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>integration-test</id>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<phase>integration-test</phase>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
<skipTests>false</skipTests>
|
||||
<argLine />
|
||||
<forkMode>once</forkMode>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<version>0.45.1</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<autoPull>true</autoPull>
|
||||
<startParallel>false</startParallel>
|
||||
<showLogs>true</showLogs>
|
||||
<autoCreateCustomNetworks>true</autoCreateCustomNetworks>
|
||||
<images>
|
||||
<image>
|
||||
<alias>hartmann_postgres</alias>
|
||||
<name>postgres:11</name>
|
||||
<run>
|
||||
<ports>
|
||||
<port>5430:5432</port>
|
||||
</ports>
|
||||
<network>
|
||||
<name>hartmann_nw</name>
|
||||
<alias>hartmann_postgres</alias>
|
||||
</network>
|
||||
<env>
|
||||
<POSTGRES_HOST_AUTH_METHOD>trust</POSTGRES_HOST_AUTH_METHOD>
|
||||
</env>
|
||||
<volumes>
|
||||
<bind>
|
||||
<volume>${basedir}/src/main/docker/sql:/docker-entrypoint-initdb.d</volume>
|
||||
</bind>
|
||||
</volumes>
|
||||
<wait>
|
||||
<log>database system is ready to accept connections</log>
|
||||
<time>2500000</time>
|
||||
</wait>
|
||||
</run>
|
||||
</image>
|
||||
|
||||
<image>
|
||||
<!-- Artifact Image -->
|
||||
<name>${project.artifactId}</name>
|
||||
<alias>hartmann</alias>
|
||||
<build>
|
||||
<dockerFile>Dockerfile</dockerFile>
|
||||
<args>
|
||||
<deploymentDir>maven</deploymentDir>
|
||||
</args>
|
||||
<tags>
|
||||
<tag>latest</tag>
|
||||
</tags>
|
||||
</build>
|
||||
<registry>hub.heyday.marketing</registry>
|
||||
<run>
|
||||
<cmd>
|
||||
/srv/wait-for-it.sh
|
||||
hartmann_postgres:5432" --
|
||||
/srv/wildfly/bin/standalone-jacoco.sh
|
||||
-b 0.0.0.0 -c test-standalone.xml
|
||||
</cmd>
|
||||
<ports>
|
||||
<port>8180:8080</port>
|
||||
</ports>
|
||||
<dependsOn>
|
||||
<container>hartmann_postgres</container>
|
||||
|
||||
</dependsOn>
|
||||
<network>
|
||||
<name>hartmann_nw</name>
|
||||
<alias>hartmann</alias>
|
||||
</network>
|
||||
<volumes>
|
||||
<bind>
|
||||
<volume>${basedir}/target:/srv/target</volume>
|
||||
</bind>
|
||||
</volumes>
|
||||
<wait>
|
||||
<!-- The plugin waits until this URL
|
||||
is reachable via HTTP ... -->
|
||||
<http>
|
||||
<url>http://${docker.host.address}:8180/api/monitoring/check/hello</url>
|
||||
</http>
|
||||
<time>300000</time>
|
||||
</wait>
|
||||
</run>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
<!-- <goal>push</goal> -->
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>start</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>stop</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-resources</id>
|
||||
<!-- here the phase you need -->
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${basedir}/src/main/docker</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/../hartmann-foto-documentation-web/target/</directory>
|
||||
<includes>
|
||||
<include>*.war</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-resources2</id>
|
||||
<!-- here the phase you need -->
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.basedir}/target/classes</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.basedir}/../hartmann-foto-documentation-app/target/classes</directory>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>copy-jacoco-app</id>
|
||||
<!-- here the phase you need -->
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.basedir}/../hartmann-foto-documentation-app/target</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.basedir}/target</directory>
|
||||
<filtering>false</filtering>
|
||||
<includes>
|
||||
<include>jacoco-it.exec</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
<dependencies>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-java</artifactId>
|
||||
<version>3.141.59</version> <!-- was 3.11.0 -->
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-edge-driver</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-firefox-driver</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-ie-driver</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-opera-driver</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-safari-driver</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-api</artifactId>
|
||||
<version>3.141.59</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-chrome-driver</artifactId>
|
||||
<version>3.141.59</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-remote-driver</artifactId>
|
||||
<version>3.141.59</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-support</artifactId>
|
||||
<version>3.141.59</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>hartmann-foto-documentation-web</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>hartmann-foto-documentation-app</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Websocket test api's -->
|
||||
<dependency>
|
||||
<groupId>javax.websocket</groupId>
|
||||
<artifactId>javax.websocket-client-api</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.tyrus</groupId>
|
||||
<artifactId>tyrus-client</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.tyrus</groupId>
|
||||
<artifactId>tyrus-container-grizzly</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.json</groupId>
|
||||
<artifactId>javax.json-api</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.json</artifactId>
|
||||
<version>1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Websocket test api's -->
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${version.commons-lang3}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>org.jacoco.core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dbunit</groupId>
|
||||
<artifactId>dbunit</artifactId>
|
||||
<version>2.7.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jackson2-provider</artifactId>
|
||||
<version>${version.org.jboss.resteasy}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
<version>1.9.13</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
<version>1.9.13</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-suite-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-suite-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-commons</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-launcher</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${version.commons-io}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
<version>${version.commons-logging}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>fluent-hc</artifactId>
|
||||
<version>4.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
<version>4.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.15</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>mail</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>jms</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jdmk</groupId>
|
||||
<artifactId>jmxtools</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.sun.jmx</groupId>
|
||||
<artifactId>jmxri</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xmlunit</groupId>
|
||||
<artifactId>xmlunit-core</artifactId>
|
||||
<version>2.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.skyscreamer</groupId>
|
||||
<artifactId>jsonassert</artifactId>
|
||||
<version>1.2.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.7.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -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 <p.verboom@heyday.marketing>"
|
||||
|
||||
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"]
|
||||
@@ -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:
|
||||
Binary file not shown.
@@ -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
|
||||
--
|
||||
@@ -0,0 +1,666 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<server xmlns="urn:jboss:domain:20.0">
|
||||
<extensions>
|
||||
<extension module="org.jboss.as.clustering.infinispan"/>
|
||||
<extension module="org.jboss.as.connector"/>
|
||||
<extension module="org.jboss.as.deployment-scanner"/>
|
||||
<extension module="org.jboss.as.ee"/>
|
||||
<extension module="org.jboss.as.ejb3"/>
|
||||
<extension module="org.jboss.as.jaxrs"/>
|
||||
<extension module="org.jboss.as.jdr"/>
|
||||
<extension module="org.jboss.as.jmx"/>
|
||||
<extension module="org.jboss.as.jpa"/>
|
||||
<extension module="org.jboss.as.jsf"/>
|
||||
<extension module="org.jboss.as.logging"/>
|
||||
<extension module="org.jboss.as.mail"/>
|
||||
<extension module="org.jboss.as.naming"/>
|
||||
<extension module="org.jboss.as.pojo"/>
|
||||
<extension module="org.jboss.as.remoting"/>
|
||||
<extension module="org.jboss.as.sar"/>
|
||||
<extension module="org.jboss.as.transactions"/>
|
||||
<extension module="org.jboss.as.webservices"/>
|
||||
<extension module="org.jboss.as.weld"/>
|
||||
<extension module="org.wildfly.extension.batch.jberet"/>
|
||||
<extension module="org.wildfly.extension.bean-validation"/>
|
||||
<extension module="org.wildfly.extension.clustering.ejb"/>
|
||||
<extension module="org.wildfly.extension.clustering.web"/>
|
||||
<extension module="org.wildfly.extension.core-management"/>
|
||||
<extension module="org.wildfly.extension.discovery"/>
|
||||
<extension module="org.wildfly.extension.ee-security"/>
|
||||
<extension module="org.wildfly.extension.elytron"/>
|
||||
<extension module="org.wildfly.extension.elytron-oidc-client"/>
|
||||
<extension module="org.wildfly.extension.health"/>
|
||||
<extension module="org.wildfly.extension.io"/>
|
||||
<extension module="org.wildfly.extension.metrics"/>
|
||||
<extension module="org.wildfly.extension.microprofile.config-smallrye"/>
|
||||
<extension module="org.wildfly.extension.microprofile.jwt-smallrye"/>
|
||||
<extension module="org.wildfly.extension.request-controller"/>
|
||||
<extension module="org.wildfly.extension.security.manager"/>
|
||||
<extension module="org.wildfly.extension.undertow"/>
|
||||
</extensions>
|
||||
<system-properties>
|
||||
<property name="jwt.secret.key" value="-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCK1EvBSGUg/+Id
|
||||
TNnlqXWkWtLypRDW5YtQ1ilT046AQfPyTCK9WGJUqqtxfiOxQ0qKVWsXZd/3JwHP
|
||||
nqpgORxOlkSpCMJo4syflqSwJ/Zqg6nNEQXErNg1L2/6tM7DW3KnNfW5yujvIDm+
|
||||
UJJCbmJtQ6tYaNqygQhL6nvDiP5jMwPmdAgg/dUyHJKrNOvF9znUF0360wNG8x/Q
|
||||
WsmGKPddMD4k7fttA5/GRszRM/WRMbPogsz43PlERchTrYF+nY/4lnD3fNqppJ5u
|
||||
uskj/G86ux4hmb/C9W+uf5NxTtURTv3H5TJW/jPZ424MoGvzQCeQa0lgNDLAIC+W
|
||||
Y81e+ZT9AgMBAAECggEAOu3LbDNZLe0/4zUMZvaMB6Q/15xmbfmIrdsCNuFdoyab
|
||||
sJVNx7adIphBZs7mwqcwHFEOwKNPMp9dnu4YHvkPAXK6mU+tCg1/UxyEMnv8FpFl
|
||||
wbSAkM/XhJfqve4CuBz4qW53rBIr1tkEebrEoqstX3jyYfg8ILoxtdvGBiV/6cYN
|
||||
Oyy01xd0NMz+JwHuk9l9ADHDqMJiPkJ1zsHqoqOsblIHSiFSyqRAJU1ns6/If/pV
|
||||
DI17G2j16lqK9S+fMltJXDfEGhz0A/c9R60nvQObEOtpUCFWS0DP75oZT2AvNqkY
|
||||
/7aLcBu7FPr522+zFzQrIGON1rJXS/qIBQu0x151JwKBgQDCQGAd+t3qn5Pi63xq
|
||||
4GFEMZHqn3wyXhhcwg6yQFKrIr8wP3Mmr8utXC0HdncXb80bavkbV8Ii6ttItWDo
|
||||
eRv3KhJe+83bS0nJc2QQtfKBt1Dg+1TBSsTwQHYKvKSThNcG3ijqtv6Juac+WqfB
|
||||
H/G5lD5yI4xMquWDyuK6hRNkhwKBgQC29dKdDogFg9cdhpArChduVVGaMu0Ifvt+
|
||||
oOsy3IVPeOPlXyeDIINi7tw17+WSwm3gS0TVqamcefYIhaBlmRwrYS8wHiyQiMfX
|
||||
tgthWXtX5z+lw2MdUfAwW6oDRQLVf7YIsas1Loe7KZRoXEbuBeUP/XdpRO1gtUDR
|
||||
gGL6e3OfWwKBgBlGJPthm6QeVTCOMSb6wM0Nog2j6JXpFkRjX2Qj6F2p7LRLXSEo
|
||||
eFi7CITTDhW3jzlFBtpe5byDUDq6lrxInbHgAHnpS1SADD6wy9E8yyvDfTt4mAN6
|
||||
Rft4d6NX/hXPj+at2ycG3kFvLWp4gyEmld3ugt148JU9GxW1vSBFlktbAoGAN1wO
|
||||
TEN3WOPZlS+AM+WrzVC3jkbWffmeM2SRhiQ/mhpkKqUuGXkfCDJqI0/hURTPlkxw
|
||||
GY5qqdQlY9K7A8LeSSnw00huB5W7kkOdEem3bpOkKI4EUXzXhmpV+QNKpjssY1kP
|
||||
Ctp3a2RbaXBybdcOxlXVad7XTKnLYRjN2ii8hX0CgYEApRFzPDU+leHXGIjPZwjA
|
||||
WcZ7IN+B5pdwJUfqulzx73WtOCfuf2J7HQ0pcaOkG2BOxBY1AGtgPDl7071uYvfR
|
||||
hbZlR027QB9GpO8pQKZ98UquAmQNTOBI0k0RX9XZAK2ae60SM8NXFFF1TDZMoKud
|
||||
eZlo8cWlAC5welD3dz1qxEo=
|
||||
-----END PRIVATE KEY-----"/>
|
||||
|
||||
</system-properties>
|
||||
<management>
|
||||
<audit-log>
|
||||
<formatters>
|
||||
<json-formatter name="json-formatter"/>
|
||||
</formatters>
|
||||
<handlers>
|
||||
<file-handler name="file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
|
||||
</handlers>
|
||||
<logger log-boot="true" log-read-only="false" enabled="false">
|
||||
<handlers>
|
||||
<handler name="file"/>
|
||||
</handlers>
|
||||
</logger>
|
||||
</audit-log>
|
||||
<management-interfaces>
|
||||
<http-interface http-authentication-factory="management-http-authentication">
|
||||
<http-upgrade enabled="true" sasl-authentication-factory="management-sasl-authentication"/>
|
||||
<socket-binding http="management-http"/>
|
||||
</http-interface>
|
||||
</management-interfaces>
|
||||
<access-control provider="simple">
|
||||
<role-mapping>
|
||||
<role name="SuperUser">
|
||||
<include>
|
||||
<user name="$local"/>
|
||||
</include>
|
||||
</role>
|
||||
</role-mapping>
|
||||
</access-control>
|
||||
</management>
|
||||
<profile>
|
||||
<subsystem xmlns="urn:jboss:domain:logging:8.0">
|
||||
<console-handler name="CONSOLE">
|
||||
<level name="DEBUG"/>
|
||||
<formatter>
|
||||
<named-formatter name="COLOR-PATTERN"/>
|
||||
</formatter>
|
||||
</console-handler>
|
||||
<periodic-rotating-file-handler name="FILE" autoflush="true">
|
||||
<formatter>
|
||||
<named-formatter name="PATTERN"/>
|
||||
</formatter>
|
||||
<file relative-to="jboss.server.log.dir" path="server.log"/>
|
||||
<suffix value=".yyyy-MM-dd"/>
|
||||
<append value="true"/>
|
||||
</periodic-rotating-file-handler>
|
||||
<logger category="com.arjuna">
|
||||
<level name="WARN"/>
|
||||
</logger>
|
||||
<logger category="com.networknt.schema">
|
||||
<level name="WARN"/>
|
||||
</logger>
|
||||
<logger category="io.jaegertracing.Configuration">
|
||||
<level name="WARN"/>
|
||||
</logger>
|
||||
<logger category="org.jboss.as.config">
|
||||
<level name="DEBUG"/>
|
||||
</logger>
|
||||
<logger category="org.jboss">
|
||||
<level name="INFO"/>
|
||||
</logger>
|
||||
|
||||
<logger category="org.hibernate">
|
||||
<level name="INFO"/>
|
||||
</logger>
|
||||
|
||||
<logger category="marketing.heyday.hartmann">
|
||||
<level name="DEBUG"/>
|
||||
</logger>
|
||||
<logger category="sun.rmi">
|
||||
<level name="WARN"/>
|
||||
</logger>
|
||||
<root-logger>
|
||||
<level name="DEBUG"/>
|
||||
<handlers>
|
||||
<handler name="CONSOLE"/>
|
||||
<handler name="FILE"/>
|
||||
</handlers>
|
||||
</root-logger>
|
||||
<formatter name="PATTERN">
|
||||
<pattern-formatter pattern="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
|
||||
</formatter>
|
||||
<formatter name="COLOR-PATTERN">
|
||||
<pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
|
||||
</formatter>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:batch-jberet:3.0">
|
||||
<default-job-repository name="in-memory"/>
|
||||
<default-thread-pool name="batch"/>
|
||||
<security-domain name="ApplicationDomain"/>
|
||||
<job-repository name="in-memory">
|
||||
<in-memory/>
|
||||
</job-repository>
|
||||
<thread-pool name="batch">
|
||||
<max-threads count="10"/>
|
||||
<keepalive-time time="30" unit="seconds"/>
|
||||
</thread-pool>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:bean-validation:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:core-management:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:datasources:7.1">
|
||||
<datasources>
|
||||
<!-- Patrick -->
|
||||
<datasource jndi-name="java:/jdbc/fotoDocumentationDS" pool-name="fotoDocumentationDS" enabled="true" use-java-context="true" use-ccm="false">
|
||||
<connection-url>jdbc:postgresql://hartmann_postgres:5432/fotodocumentation</connection-url>
|
||||
<driver>postgres</driver>
|
||||
<transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
|
||||
<pool>
|
||||
<min-pool-size>1</min-pool-size>
|
||||
<max-pool-size>10</max-pool-size>
|
||||
</pool>
|
||||
<security user-name="fotodocumentation" password="fotodocumentation"/>
|
||||
<validation>
|
||||
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker"/>
|
||||
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter"/>
|
||||
</validation>
|
||||
<statement>
|
||||
<prepared-statement-cache-size>32</prepared-statement-cache-size>
|
||||
<share-prepared-statements>true</share-prepared-statements>
|
||||
</statement>
|
||||
</datasource>
|
||||
<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||
<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=${wildfly.h2.compatibility.mode:REGULAR}</connection-url>
|
||||
<driver>h2</driver>
|
||||
<security user-name="sa" password="sa"/>
|
||||
</datasource>
|
||||
<drivers>
|
||||
<!-- Patrick -->
|
||||
<driver name="postgres" module="org.postgresql.jdbc">
|
||||
<xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
|
||||
</driver>
|
||||
<driver name="h2" module="com.h2database.h2">
|
||||
<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
|
||||
</driver>
|
||||
</drivers>
|
||||
</datasources>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:deployment-scanner:2.0">
|
||||
<deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000" runtime-failure-causes-rollback="${jboss.deployment.scanner.rollback.on.failure:false}"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:discovery:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:distributable-ejb:1.0" default-bean-management="default">
|
||||
<infinispan-bean-management name="default" max-active-beans="10000" cache-container="ejb" cache="passivation"/>
|
||||
<local-client-mappings-registry/>
|
||||
<infinispan-timer-management name="persistent" cache-container="ejb" cache="persistent" max-active-timers="10000"/>
|
||||
<infinispan-timer-management name="transient" cache-container="ejb" cache="transient" max-active-timers="10000"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:distributable-web:4.0" default-session-management="default" default-single-sign-on-management="default">
|
||||
<infinispan-session-management name="default" cache-container="web" granularity="SESSION">
|
||||
<local-affinity/>
|
||||
</infinispan-session-management>
|
||||
<infinispan-single-sign-on-management name="default" cache-container="web" cache="sso"/>
|
||||
<local-routing/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:ee:6.0">
|
||||
<spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
|
||||
<concurrent>
|
||||
<context-services>
|
||||
<context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default"/>
|
||||
</context-services>
|
||||
<managed-thread-factories>
|
||||
<managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
|
||||
</managed-thread-factories>
|
||||
<managed-executor-services>
|
||||
<managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-termination-period="0" hung-task-threshold="60000" keepalive-time="5000"/>
|
||||
</managed-executor-services>
|
||||
<managed-scheduled-executor-services>
|
||||
<managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-termination-period="0" hung-task-threshold="60000" keepalive-time="3000"/>
|
||||
</managed-scheduled-executor-services>
|
||||
</concurrent>
|
||||
<default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:ee-security:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:ejb3:10.0">
|
||||
<session-bean>
|
||||
<stateless>
|
||||
<bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
|
||||
</stateless>
|
||||
<stateful default-access-timeout="5000" cache-ref="simple" passivation-disabled-cache-ref="simple"/>
|
||||
<singleton default-access-timeout="5000"/>
|
||||
</session-bean>
|
||||
<pools>
|
||||
<bean-instance-pools>
|
||||
<strict-max-pool name="slsb-strict-max-pool" derive-size="from-worker-pools" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
|
||||
<strict-max-pool name="mdb-strict-max-pool" derive-size="from-cpu-count" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
|
||||
</bean-instance-pools>
|
||||
</pools>
|
||||
<caches>
|
||||
<simple-cache name="simple"/>
|
||||
<distributable-cache name="distributable"/>
|
||||
</caches>
|
||||
<async thread-pool-name="default"/>
|
||||
<timer-service thread-pool-name="default" default-data-store="default-file-store">
|
||||
<data-stores>
|
||||
<file-data-store name="default-file-store" path="timer-service-data" relative-to="jboss.server.data.dir"/>
|
||||
</data-stores>
|
||||
</timer-service>
|
||||
<remote connectors="http-remoting-connector" thread-pool-name="default">
|
||||
<channel-creation-options>
|
||||
<option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
|
||||
</channel-creation-options>
|
||||
</remote>
|
||||
<thread-pools>
|
||||
<thread-pool name="default">
|
||||
<max-threads count="10"/>
|
||||
<keepalive-time time="60" unit="seconds"/>
|
||||
</thread-pool>
|
||||
</thread-pools>
|
||||
<default-security-domain value="other"/>
|
||||
<application-security-domains>
|
||||
<application-security-domain name="other" security-domain="ApplicationDomain"/>
|
||||
|
||||
<!--patrick-->
|
||||
<application-security-domain name="fotoDocumentationSecurity" security-domain="fotoDocumentationDomain"/>
|
||||
</application-security-domains>
|
||||
<default-missing-method-permissions-deny-access value="true"/>
|
||||
<statistics enabled="${wildfly.ejb3.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
|
||||
<log-system-exceptions value="true"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:wildfly:elytron:community:18.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
|
||||
<providers>
|
||||
<aggregate-providers name="combined-providers">
|
||||
<providers name="elytron"/>
|
||||
<providers name="openssl"/>
|
||||
</aggregate-providers>
|
||||
<provider-loader name="elytron" module="org.wildfly.security.elytron"/>
|
||||
<provider-loader name="openssl" module="org.wildfly.openssl"/>
|
||||
</providers>
|
||||
<audit-logging>
|
||||
<file-audit-log name="local-audit" path="audit.log" relative-to="jboss.server.log.dir" format="JSON"/>
|
||||
</audit-logging>
|
||||
<security-domains>
|
||||
<security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper">
|
||||
<realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
|
||||
<realm name="local"/>
|
||||
</security-domain>
|
||||
<security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="default-permission-mapper">
|
||||
<realm name="ManagementRealm" role-decoder="groups-to-roles"/>
|
||||
<realm name="local" role-mapper="super-user-mapper"/>
|
||||
</security-domain>
|
||||
|
||||
<!-- patrick -->
|
||||
<security-domain name="fotoDocumentationDomain" default-realm="fotoDocumentationRealm" permission-mapper="default-permission-mapper">
|
||||
<realm name="fotoDocumentationRealm" role-decoder="groups-to-roles"/>
|
||||
</security-domain>
|
||||
|
||||
</security-domains>
|
||||
<security-realms>
|
||||
<identity-realm name="local" identity="$local"/>
|
||||
<properties-realm name="ApplicationRealm">
|
||||
<users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ApplicationRealm"/>
|
||||
<groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
|
||||
</properties-realm>
|
||||
<properties-realm name="ManagementRealm">
|
||||
<users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ManagementRealm"/>
|
||||
<groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
|
||||
</properties-realm>
|
||||
|
||||
<!-- patrick -->
|
||||
<distributed-realm name="fotoDocumentationRealm" realms="fotoDocumentationJwtRealm fotoDocumentationJdbcRealm" />
|
||||
|
||||
<!-- patrick -->
|
||||
<token-realm name="fotoDocumentationJwtRealm" principal-claim="username">
|
||||
<jwt issuer="foto-jwt-issuer" audience="foto-api"
|
||||
public-key="-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAitRLwUhlIP/iHUzZ5al1
|
||||
pFrS8qUQ1uWLUNYpU9OOgEHz8kwivVhiVKqrcX4jsUNKilVrF2Xf9ycBz56qYDkc
|
||||
TpZEqQjCaOLMn5aksCf2aoOpzREFxKzYNS9v+rTOw1typzX1ucro7yA5vlCSQm5i
|
||||
bUOrWGjasoEIS+p7w4j+YzMD5nQIIP3VMhySqzTrxfc51BdN+tMDRvMf0FrJhij3
|
||||
XTA+JO37bQOfxkbM0TP1kTGz6ILM+Nz5REXIU62Bfp2P+JZw93zaqaSebrrJI/xv
|
||||
OrseIZm/wvVvrn+TcU7VEU79x+UyVv4z2eNuDKBr80AnkGtJYDQywCAvlmPNXvmU
|
||||
/QIDAQAB
|
||||
-----END PUBLIC KEY-----"
|
||||
/>
|
||||
</token-realm>
|
||||
|
||||
<!-- patrick -->
|
||||
<jdbc-realm name="fotoDocumentationJdbcRealm" >
|
||||
<principal-query data-source="fotoDocumentationDS" sql="select password, salt, ri.code as Role from x_user u left join user_to_right rtr on rtr.user_id_fk = u.user_id left join x_right ri on rtr.right_id_fk = ri.right_id where username = ?;">
|
||||
<salted-simple-digest-mapper algorithm="password-salt-digest-sha-256" password-index="1" salt-index="2" />
|
||||
<attribute-mapping>
|
||||
<attribute to="groups" index="3"/>
|
||||
</attribute-mapping>
|
||||
</principal-query>
|
||||
</jdbc-realm>
|
||||
</security-realms>
|
||||
<mappers>
|
||||
<simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
|
||||
<permission-mapping>
|
||||
<principal name="anonymous"/>
|
||||
<permission-set name="default-permissions"/>
|
||||
</permission-mapping>
|
||||
<permission-mapping match-all="true">
|
||||
<permission-set name="login-permission"/>
|
||||
<permission-set name="default-permissions"/>
|
||||
</permission-mapping>
|
||||
</simple-permission-mapper>
|
||||
<constant-realm-mapper name="local" realm-name="local"/>
|
||||
<simple-role-decoder name="groups-to-roles" attribute="groups"/>
|
||||
<constant-role-mapper name="super-user-mapper">
|
||||
<role name="SuperUser"/>
|
||||
</constant-role-mapper>
|
||||
</mappers>
|
||||
<permission-sets>
|
||||
<permission-set name="login-permission">
|
||||
<permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
|
||||
</permission-set>
|
||||
<permission-set name="default-permissions">
|
||||
<permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
|
||||
<permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
|
||||
<permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
|
||||
</permission-set>
|
||||
</permission-sets>
|
||||
<http>
|
||||
<http-authentication-factory name="application-http-authentication" security-domain="ApplicationDomain" http-server-mechanism-factory="global">
|
||||
<mechanism-configuration>
|
||||
<mechanism mechanism-name="BASIC">
|
||||
<mechanism-realm realm-name="ApplicationRealm"/>
|
||||
</mechanism>
|
||||
</mechanism-configuration>
|
||||
</http-authentication-factory>
|
||||
<http-authentication-factory name="management-http-authentication" security-domain="ManagementDomain" http-server-mechanism-factory="global">
|
||||
<mechanism-configuration>
|
||||
<mechanism mechanism-name="DIGEST">
|
||||
<mechanism-realm realm-name="ManagementRealm"/>
|
||||
</mechanism>
|
||||
</mechanism-configuration>
|
||||
</http-authentication-factory>
|
||||
|
||||
<!-- patrick -->
|
||||
<http-authentication-factory name="fotoDocumentation-http-authentication" security-domain="fotoDocumentationDomain" http-server-mechanism-factory="global">
|
||||
<mechanism-configuration>
|
||||
<mechanism mechanism-name="BEARER_TOKEN">
|
||||
<mechanism-realm realm-name="fotoDocumentationRealm"/>
|
||||
</mechanism>
|
||||
</mechanism-configuration>
|
||||
</http-authentication-factory>
|
||||
|
||||
<provider-http-server-mechanism-factory name="global"/>
|
||||
</http>
|
||||
<sasl>
|
||||
<sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
|
||||
<mechanism-configuration>
|
||||
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
|
||||
<mechanism mechanism-name="DIGEST-MD5">
|
||||
<mechanism-realm realm-name="ApplicationRealm"/>
|
||||
</mechanism>
|
||||
</mechanism-configuration>
|
||||
</sasl-authentication-factory>
|
||||
<sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="configured" security-domain="ManagementDomain">
|
||||
<mechanism-configuration>
|
||||
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
|
||||
<mechanism mechanism-name="DIGEST-MD5">
|
||||
<mechanism-realm realm-name="ManagementRealm"/>
|
||||
</mechanism>
|
||||
</mechanism-configuration>
|
||||
</sasl-authentication-factory>
|
||||
<configurable-sasl-server-factory name="configured" sasl-server-factory="elytron">
|
||||
<properties>
|
||||
<property name="wildfly.sasl.local-user.default-user" value="$local"/>
|
||||
<property name="wildfly.sasl.local-user.challenge-path" value="${jboss.server.temp.dir}/auth"/>
|
||||
</properties>
|
||||
</configurable-sasl-server-factory>
|
||||
<mechanism-provider-filtering-sasl-server-factory name="elytron" sasl-server-factory="global">
|
||||
<filters>
|
||||
<filter provider-name="WildFlyElytron"/>
|
||||
</filters>
|
||||
</mechanism-provider-filtering-sasl-server-factory>
|
||||
<provider-sasl-server-factory name="global"/>
|
||||
</sasl>
|
||||
<tls>
|
||||
<key-stores>
|
||||
<key-store name="applicationKS">
|
||||
<credential-reference clear-text="password"/>
|
||||
<implementation type="JKS"/>
|
||||
<file path="application.keystore" relative-to="jboss.server.config.dir"/>
|
||||
</key-store>
|
||||
</key-stores>
|
||||
<key-managers>
|
||||
<key-manager name="applicationKM" key-store="applicationKS" generate-self-signed-certificate-host="localhost">
|
||||
<credential-reference clear-text="password"/>
|
||||
</key-manager>
|
||||
</key-managers>
|
||||
<server-ssl-contexts>
|
||||
<server-ssl-context name="applicationSSC" key-manager="applicationKM"/>
|
||||
</server-ssl-contexts>
|
||||
</tls>
|
||||
<policy name="jacc">
|
||||
<jacc-policy/>
|
||||
</policy>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:wildfly:elytron-oidc-client:2.0"/>
|
||||
<subsystem xmlns="urn:wildfly:health:1.0" security-enabled="false"/>
|
||||
<subsystem xmlns="urn:jboss:domain:infinispan:14.0">
|
||||
<cache-container name="hibernate" marshaller="JBOSS" modules="org.infinispan.hibernate-cache">
|
||||
<local-cache name="entity">
|
||||
<heap-memory size="10000"/>
|
||||
<expiration max-idle="100000"/>
|
||||
</local-cache>
|
||||
<local-cache name="local-query">
|
||||
<heap-memory size="10000"/>
|
||||
<expiration max-idle="100000"/>
|
||||
</local-cache>
|
||||
<local-cache name="timestamps">
|
||||
<expiration interval="0"/>
|
||||
</local-cache>
|
||||
<local-cache name="pending-puts">
|
||||
<expiration max-idle="60000"/>
|
||||
</local-cache>
|
||||
</cache-container>
|
||||
<cache-container name="ejb" default-cache="passivation" marshaller="PROTOSTREAM" aliases="sfsb" modules="org.wildfly.clustering.ejb.infinispan">
|
||||
<local-cache name="passivation">
|
||||
<expiration interval="0"/>
|
||||
<file-store passivation="true"/>
|
||||
</local-cache>
|
||||
<local-cache name="persistent">
|
||||
<locking isolation="REPEATABLE_READ"/>
|
||||
<transaction mode="BATCH"/>
|
||||
<expiration interval="0"/>
|
||||
<file-store preload="true"/>
|
||||
</local-cache>
|
||||
<local-cache name="transient">
|
||||
<locking isolation="REPEATABLE_READ"/>
|
||||
<transaction mode="BATCH"/>
|
||||
<expiration interval="0"/>
|
||||
<file-store passivation="true" purge="true"/>
|
||||
</local-cache>
|
||||
</cache-container>
|
||||
<cache-container name="web" default-cache="passivation" marshaller="PROTOSTREAM" modules="org.wildfly.clustering.web.infinispan">
|
||||
<local-cache name="passivation">
|
||||
<expiration interval="0"/>
|
||||
<file-store passivation="true"/>
|
||||
</local-cache>
|
||||
<local-cache name="sso">
|
||||
<expiration interval="0"/>
|
||||
</local-cache>
|
||||
</cache-container>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:io:4.0" default-worker="default">
|
||||
<worker name="default"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:jaxrs:3.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:jca:6.0">
|
||||
<archive-validation enabled="true" fail-on-error="true" fail-on-warn="false"/>
|
||||
<bean-validation enabled="true"/>
|
||||
<default-workmanager>
|
||||
<short-running-threads>
|
||||
<core-threads count="50"/>
|
||||
<queue-length count="50"/>
|
||||
<max-threads count="50"/>
|
||||
<keepalive-time time="10" unit="seconds"/>
|
||||
</short-running-threads>
|
||||
<long-running-threads>
|
||||
<core-threads count="50"/>
|
||||
<queue-length count="50"/>
|
||||
<max-threads count="50"/>
|
||||
<keepalive-time time="10" unit="seconds"/>
|
||||
</long-running-threads>
|
||||
</default-workmanager>
|
||||
<cached-connection-manager/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:jdr:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:jmx:1.3">
|
||||
<expose-resolved-model/>
|
||||
<expose-expression-model/>
|
||||
<remoting-connector/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:jpa:1.1">
|
||||
<jpa default-extended-persistence-inheritance="DEEP"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:jsf:1.1"/>
|
||||
<subsystem xmlns="urn:jboss:domain:mail:4.0">
|
||||
<mail-session name="default" jndi-name="java:jboss/mail/Default">
|
||||
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
|
||||
</mail-session>
|
||||
<!-- Patrick -->
|
||||
<mail-session name="fotoDocumentationMail" debug="true" jndi-name="java:/mail/fotoDocumentation-mail">
|
||||
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
|
||||
</mail-session>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:wildfly:metrics:1.0" security-enabled="false" exposed-subsystems="*" prefix="${wildfly.metrics.prefix:wildfly}"/>
|
||||
<subsystem xmlns="urn:wildfly:microprofile-config-smallrye:2.0"/>
|
||||
<subsystem xmlns="urn:wildfly:microprofile-jwt-smallrye:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:naming:2.0">
|
||||
<remote-naming/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:pojo:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:remoting:7.0">
|
||||
<endpoint worker="default"/>
|
||||
<http-connector name="http-remoting-connector" connector-ref="default" sasl-authentication-factory="application-sasl-authentication"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:resource-adapters:7.1"/>
|
||||
<subsystem xmlns="urn:jboss:domain:sar:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
|
||||
<deployment-permissions>
|
||||
<maximum-set>
|
||||
<permission class="java.security.AllPermission"/>
|
||||
</maximum-set>
|
||||
</deployment-permissions>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:transactions:6.0">
|
||||
<core-environment node-identifier="${jboss.tx.node.id:1}">
|
||||
<process-id>
|
||||
<uuid/>
|
||||
</process-id>
|
||||
</core-environment>
|
||||
<recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
|
||||
<coordinator-environment statistics-enabled="${wildfly.transactions.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
|
||||
<object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:undertow:14.0" default-virtual-host="default-host" default-servlet-container="default" default-server="default-server" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}" default-security-domain="other">
|
||||
<byte-buffer-pool name="default"/>
|
||||
<buffer-cache name="default"/>
|
||||
<server name="default-server">
|
||||
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
|
||||
<https-listener name="https" socket-binding="https" ssl-context="applicationSSC" enable-http2="true"/>
|
||||
<host name="default-host" alias="localhost">
|
||||
<location name="/" handler="welcome-content"/>
|
||||
<http-invoker http-authentication-factory="application-http-authentication"/>
|
||||
|
||||
<!-- patrick -->
|
||||
<filter-ref name="server-header"/>
|
||||
<filter-ref name="x-powered-by-header"/>
|
||||
<filter-ref name="Access-Control-Allow-Origin"/>
|
||||
<filter-ref name="Access-Control-Allow-Methods"/>
|
||||
<filter-ref name="Access-Control-Allow-Headers"/>
|
||||
<filter-ref name="Access-Control-Allow-Credentials"/>
|
||||
<filter-ref name="Access-Control-Max-Age"/>
|
||||
</host>
|
||||
</server>
|
||||
<servlet-container name="default">
|
||||
<jsp-config/>
|
||||
<websockets/>
|
||||
</servlet-container>
|
||||
<handlers>
|
||||
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
|
||||
</handlers>
|
||||
<application-security-domains>
|
||||
|
||||
<application-security-domain name="other" security-domain="ApplicationDomain"/>
|
||||
|
||||
<!-- patrick -->
|
||||
<application-security-domain name="fotoDocumentationSecurity" http-authentication-factory="fotoDocumentation-http-authentication"/>
|
||||
</application-security-domains>
|
||||
|
||||
<!-- patrick -->
|
||||
<filters>
|
||||
<response-header name="server-header" header-name="Server" header-value="WildFly/10"/>
|
||||
<response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
|
||||
<response-header name="Access-Control-Allow-Origin" header-name="Access-Control-Allow-Origin" header-value="http://localhost:1234"/>
|
||||
<response-header name="Access-Control-Allow-Methods" header-name="Access-Control-Allow-Methods" header-value="GET, POST, OPTIONS, PUT, DELETE"/>
|
||||
<response-header name="Access-Control-Allow-Headers" header-name="Access-Control-Allow-Headers" header-value="accept, authorization, content-type, x-requested-with, xsrf-token"/>
|
||||
|
||||
<response-header name="Access-Control-Expose-Headers" header-name="Access-Control-Expose-Headers" header-value="xsrf-token"/>
|
||||
|
||||
<response-header name="Access-Control-Allow-Credentials" header-name="Access-Control-Allow-Credentials" header-value="true"/>
|
||||
<response-header name="Access-Control-Max-Age" header-name="Access-Control-Max-Age" header-value="1"/>
|
||||
</filters>
|
||||
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:webservices:2.0" statistics-enabled="${wildfly.webservices.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||
<wsdl-host>${jboss.bind.address:127.0.0.1}</wsdl-host>
|
||||
<endpoint-config name="Standard-Endpoint-Config"/>
|
||||
<endpoint-config name="Recording-Endpoint-Config">
|
||||
<pre-handler-chain name="recording-handlers" protocol-bindings="##SOAP11_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP ##SOAP12_HTTP_MTOM">
|
||||
<handler name="RecordingHandler" class="org.jboss.ws.common.invocation.RecordingServerHandler"/>
|
||||
</pre-handler-chain>
|
||||
</endpoint-config>
|
||||
<client-config name="Standard-Client-Config"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:weld:5.0"/>
|
||||
</profile>
|
||||
<interfaces>
|
||||
<interface name="management">
|
||||
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
|
||||
</interface>
|
||||
<interface name="public">
|
||||
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
|
||||
</interface>
|
||||
</interfaces>
|
||||
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
|
||||
<socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
|
||||
<socket-binding name="http" port="${jboss.http.port:8080}"/>
|
||||
<socket-binding name="https" port="${jboss.https.port:8443}"/>
|
||||
<socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
|
||||
<socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
|
||||
<socket-binding name="txn-recovery-environment" port="4712"/>
|
||||
<socket-binding name="txn-status-manager" port="4713"/>
|
||||
<outbound-socket-binding name="mail-smtp">
|
||||
<remote-destination host="smtp-server" port="26"/>
|
||||
</outbound-socket-binding>
|
||||
</socket-binding-group>
|
||||
</server>
|
||||
@@ -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 $@
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 22 Nov 2024
|
||||
*/
|
||||
|
||||
|
||||
@Suite
|
||||
@SuiteDisplayName("Rest Resource Suite")
|
||||
@SelectPackages("marketing.heyday.hartmann.fotodocumentation.rest")
|
||||
public class AAAResourceTestSuite {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @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";
|
||||
|
||||
private static Map<String, String> bearerToken = new HashMap<>();
|
||||
|
||||
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) {
|
||||
|
||||
if (!bearerToken.containsKey(user)) {
|
||||
String auth = user + ":" + pass;
|
||||
String encoded = Base64.getEncoder().encodeToString(auth.getBytes());
|
||||
String authorization = "Basic " + encoded;
|
||||
|
||||
bearerToken.put(user, getBearerToken(authorization));
|
||||
}
|
||||
|
||||
return "Bearer " + bearerToken.getOrDefault(user, "");
|
||||
}
|
||||
|
||||
protected String getBasicHeader() {
|
||||
return getBasicHeader(username, password);
|
||||
}
|
||||
|
||||
protected String getBasicHeader(String user, String pass) {
|
||||
String auth = user + ":" + pass;
|
||||
String encoded = Base64.getEncoder().encodeToString(auth.getBytes());
|
||||
return "Basic " + encoded;
|
||||
}
|
||||
|
||||
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<String> 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");
|
||||
}
|
||||
|
||||
protected int customerCount() {
|
||||
return getCount("select count(*) from customer");
|
||||
}
|
||||
|
||||
protected int pictureCount() {
|
||||
return getCount("select count(*) from picture");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 13 Nov 2024
|
||||
*/
|
||||
|
||||
public abstract 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 14 Nov 2024
|
||||
*/
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class CustomerPictureResourceTest extends AbstractRestTest {
|
||||
private static final Log LOG = LogFactory.getLog(CustomerPictureResourceTest.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(2)
|
||||
public void doAddCustomerPicture() throws IOException {
|
||||
LOG.info("doAddCustomerPicture");
|
||||
|
||||
assertEquals(3, customerCount());
|
||||
assertEquals(5, pictureCount());
|
||||
|
||||
String path = deploymentURL + PATH;
|
||||
Request request = Request.Post(path).addHeader("Accept", "application/json; charset=utf-8")
|
||||
.bodyFile(new File(BASE_UPLOAD + "add.json"), ContentType.APPLICATION_JSON);
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(200, code);
|
||||
|
||||
assertEquals(3, customerCount());
|
||||
assertEquals(6, pictureCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void doAddCustomerWithPicture() throws IOException {
|
||||
LOG.info("doAddCustomerWithPicture");
|
||||
|
||||
assertEquals(3, customerCount());
|
||||
assertEquals(6, pictureCount());
|
||||
|
||||
String authorization = getBasicHeader();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH;
|
||||
Request request = Request.Post(path).addHeader("Accept", "application/json; charset=utf-8")
|
||||
.addHeader("Authorization", authorization)
|
||||
.bodyFile(new File(BASE_UPLOAD + "addNewCustomer.json"), ContentType.APPLICATION_JSON);
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(200, code);
|
||||
|
||||
assertEquals(4, customerCount());
|
||||
assertEquals(7, pictureCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doAddCustomerPictureWrongJson() throws IOException {
|
||||
LOG.info("doAddCustomerPictureWrongJson");
|
||||
|
||||
String authorization = getBasicHeader();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH;
|
||||
Request request = Request.Post(path).addHeader("Accept", "application/json; charset=utf-8")
|
||||
.addHeader("Authorization", authorization)
|
||||
.bodyFile(new File(BASE_UPLOAD + "addWrong.json"), ContentType.APPLICATION_JSON);
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(400, code);
|
||||
|
||||
String text = getResponseText(httpResponse, "doGetAll");
|
||||
System.out.println(text);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void doTest() throws IOException {
|
||||
LOG.info("doAddCustomerPicture");
|
||||
|
||||
//String authorization = getBasicHeader();
|
||||
//LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH;
|
||||
Request request = Request.Options(path).addHeader("Accept", "application/json; charset=utf-8");
|
||||
//.addHeader("Authorization", authorization)
|
||||
//.bodyFile(new File(BASE_UPLOAD + "add.json"), ContentType.APPLICATION_JSON);
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
|
||||
var headers = httpResponse.getAllHeaders();
|
||||
for (var header : headers) {
|
||||
System.out.println(header.getName() + " " + header.getValue());
|
||||
}
|
||||
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(200, code);
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
var test = new CustomerPictureResourceTest();
|
||||
|
||||
test.deploymentURL = "http://localhost:8080/";
|
||||
test.deploymentURL = "https://hartmann-cue.heydevelop.de/";
|
||||
test.username = "adm";
|
||||
test.password = "x1t0e7Pb49";
|
||||
|
||||
test.doTest();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
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.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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 14 Nov 2024
|
||||
*/
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class CustomerResourceTest extends AbstractRestTest {
|
||||
private static final Log LOG = LogFactory.getLog(CustomerResourceTest.class);
|
||||
private static final String PATH = "api/customer";
|
||||
private static final String BASE_DOWNLOAD = "json/CustomerResourceTest-";
|
||||
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
initDB();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doGetAll() throws IOException {
|
||||
LOG.info("doGetAll");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH;
|
||||
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 text = getResponseText(httpResponse, "doGetAll");
|
||||
String expected = fileToString(BASE_DOWNLOAD + "doGetAll.json");
|
||||
jsonAssert(expected, text);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doGetAllStartWith() throws IOException {
|
||||
LOG.info("doGetAllStartWith");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH + "?startsWith=M";
|
||||
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 text = getResponseText(httpResponse, "doGetAllStartWith");
|
||||
String expected = fileToString(BASE_DOWNLOAD + "doGetAllStartWith.json");
|
||||
jsonAssert(expected, text);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doGetAllQueryText() throws IOException {
|
||||
LOG.info("doGetAllQueryText");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH + "?query=2345";
|
||||
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 text = getResponseText(httpResponse, "doGetAllQueryText");
|
||||
String expected = fileToString(BASE_DOWNLOAD + "doGetAllQueryText.json");
|
||||
jsonAssert(expected, text);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doGetAllQueryTextWithStart() throws IOException {
|
||||
LOG.info("doGetAllQueryTextWithStart");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH + "?query=45&startsWith=M";
|
||||
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 text = getResponseText(httpResponse, "doGetAllQueryTextWithStart");
|
||||
String expected = fileToString(BASE_DOWNLOAD + "doGetAllQueryTextWithStart.json");
|
||||
jsonAssert(expected, text);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doGetAllQueryDate1() throws IOException {
|
||||
LOG.info("doGetAllQueryDate");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH + "?query=12.01.2026";
|
||||
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 text = getResponseText(httpResponse, "doGetAllQueryDate");
|
||||
String expected = fileToString(BASE_DOWNLOAD + "doGetAllQueryDate.json");
|
||||
jsonAssert(expected, text);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doGetAllQueryDate2() throws IOException {
|
||||
LOG.info("doGetAllQueryDate");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String query = URLEncoder.encode("12 Januar 2026", Charset.forName("utf-8"));
|
||||
String path = deploymentURL + PATH + "?query=" + query;
|
||||
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 text = getResponseText(httpResponse, "doGetAllQueryDate");
|
||||
String expected = fileToString(BASE_DOWNLOAD + "doGetAllQueryDate.json");
|
||||
jsonAssert(expected, text);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doGetAllQueryDate3() throws IOException {
|
||||
LOG.info("doGetAllQueryDate");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String query = URLEncoder.encode("12. Januar 2026", Charset.forName("utf-8"));
|
||||
String path = deploymentURL + PATH + "?query=" + query;
|
||||
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 text = getResponseText(httpResponse, "doGetAllQueryDate");
|
||||
String expected = fileToString(BASE_DOWNLOAD + "doGetAllQueryDate.json");
|
||||
jsonAssert(expected, text);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doGetCustomer() throws IOException {
|
||||
LOG.info("doGetCustomer");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH + "/1";
|
||||
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 text = getResponseText(httpResponse, "doGetCustomer");
|
||||
String expected = fileToString(BASE_DOWNLOAD + "doGetCustomer.json");
|
||||
jsonAssert(expected, text);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doDownload() throws IOException {
|
||||
LOG.info("doDownload");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH + "/export/1";
|
||||
Request request = Request.Get(path).addHeader("Accept", "application/pdf")
|
||||
.addHeader("Authorization", authorization);
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(200, code);
|
||||
|
||||
byte[] text = getResponse(httpResponse);
|
||||
writeFile(text, "doDownload.pdf");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doDownloadNotExist() throws IOException {
|
||||
LOG.info("doDownloadNotExist");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH + "/export/9999";
|
||||
Request request = Request.Get(path).addHeader("Accept", "application/pdf")
|
||||
.addHeader("Authorization", authorization);
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(404, code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 21 Jan 2026
|
||||
*/
|
||||
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class LoginResourceTest extends AbstractRestTest {
|
||||
private static final Log LOG = LogFactory.getLog(LoginResourceTest.class);
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void doTestLogin() {
|
||||
LOG.info("doTestLogin");
|
||||
String token = getBasicHeader();
|
||||
assertNotNull(token);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
var test = new LoginResourceTest();
|
||||
test.deploymentURL = "http://localhost:8080/";
|
||||
String token = test.getAuthorization("hartmann", "nvlev4YnTi");
|
||||
System.out.println(token);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
package marketing.heyday.hartmann.fotodocumentation.rest;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
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.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;
|
||||
|
||||
/**
|
||||
*
|
||||
* <p>Copyright: Copyright (c) 2024</p>
|
||||
* <p>Company: heyday Marketing GmbH</p>
|
||||
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
|
||||
* @version 1.0
|
||||
*
|
||||
* created: 14 Nov 2024
|
||||
*/
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class PictureResourceTest extends AbstractRestTest {
|
||||
private static final Log LOG = LogFactory.getLog(PictureResourceTest.class);
|
||||
private static final String PATH = "api/picture";
|
||||
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
initDB();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
public void doDelete() throws IOException {
|
||||
LOG.info("doDelete");
|
||||
|
||||
assertEquals(5, pictureCount());
|
||||
|
||||
String path = deploymentURL + PATH + "/1";
|
||||
Request request = Request.Delete(path).addHeader("Accept", "application/json; charset=utf-8")
|
||||
.addHeader("Authorization", getAuthorization());
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(200, code);
|
||||
|
||||
assertEquals(4, pictureCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
public void doDeleteNotFound() throws IOException {
|
||||
LOG.info("doDeleteNotFound");
|
||||
|
||||
assertEquals(5, pictureCount());
|
||||
|
||||
String path = deploymentURL + PATH + "/6000";
|
||||
Request request = Request.Delete(path).addHeader("Accept", "application/json; charset=utf-8")
|
||||
.addHeader("Authorization", getAuthorization());
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(404, code);
|
||||
|
||||
assertEquals(5, pictureCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doEvaluation() throws IOException {
|
||||
LOG.info("doEvaluation");
|
||||
|
||||
assertEquals(0, getCount("select count(*) from picture where picture_id = 1 and evaluation = 3"));
|
||||
|
||||
String path = deploymentURL + PATH + "/evaluation/1?evaluation=3";
|
||||
Request request = Request.Put(path).addHeader("Accept", "application/json; charset=utf-8")
|
||||
.addHeader("Authorization", getAuthorization());
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(200, code);
|
||||
|
||||
assertEquals(1, getCount("select count(*) from picture where picture_id = 1 and evaluation = 3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doEvaluationNotFound() throws IOException {
|
||||
LOG.info("doEvaluationNotFound");
|
||||
|
||||
String path = deploymentURL + PATH + "/evaluation/6000?evaluation=3";
|
||||
Request request = Request.Put(path).addHeader("Accept", "application/json; charset=utf-8")
|
||||
.addHeader("Authorization", getAuthorization());
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(404, code);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doEvaluationWrongValue() throws IOException {
|
||||
LOG.info("doEvaluationWrongValue");
|
||||
|
||||
String path = deploymentURL + PATH + "/evaluation/1?evaluation=4";
|
||||
Request request = Request.Put(path).addHeader("Accept", "application/json; charset=utf-8")
|
||||
.addHeader("Authorization", getAuthorization());
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(400, code);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doEvaluationWrongValue2() throws IOException {
|
||||
LOG.info("doEvaluationWrongValue2");
|
||||
|
||||
String path = deploymentURL + PATH + "/evaluation/1?evaluation=0";
|
||||
Request request = Request.Put(path).addHeader("Accept", "application/json; charset=utf-8")
|
||||
.addHeader("Authorization", getAuthorization());
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(400, code);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doEvaluationNoValue() throws IOException {
|
||||
LOG.info("doEvaluationNoValue");
|
||||
|
||||
String path = deploymentURL + PATH + "/evaluation/1";
|
||||
Request request = Request.Put(path).addHeader("Accept", "application/json; charset=utf-8")
|
||||
.addHeader("Authorization", getAuthorization());
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(400, code);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doGetPicture() throws IOException {
|
||||
LOG.info("doGetPicture");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH + "/image/1?size=1";
|
||||
Request request = Request.Get(path).addHeader("Accept", "image/jpg")
|
||||
.addHeader("Authorization", authorization);
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(200, code);
|
||||
|
||||
byte[] file = getResponse(httpResponse);
|
||||
assertTrue(file.length > 0);
|
||||
writeFile(file, "doGetPicture.jpg");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
public void doGetPictureNotFound() throws IOException {
|
||||
LOG.info("doGetPicture");
|
||||
|
||||
String authorization = getAuthorization();
|
||||
LOG.info("authorization: " + authorization);
|
||||
String path = deploymentURL + PATH + "/image/9999?size=1";
|
||||
Request request = Request.Get(path).addHeader("Accept", "image/jpg")
|
||||
.addHeader("Authorization", authorization);
|
||||
|
||||
HttpResponse httpResponse = executeRequest(request);
|
||||
int code = httpResponse.getStatusLine().getStatusCode();
|
||||
assertEquals(404, code);
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Schmidt Apotheke",
|
||||
"customerNumber": "3456",
|
||||
"lastUpdateDate": 1768212570000
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Meier Apotheke",
|
||||
"customerNumber": "2345",
|
||||
"lastUpdateDate": 1767607770000
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Meier Apotheke",
|
||||
"customerNumber": "2345",
|
||||
"lastUpdateDate": 1767607770000
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Müller Apotheke",
|
||||
"customerNumber": "1234",
|
||||
"lastUpdateDate": 1767348570000
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Meier Apotheke",
|
||||
"customerNumber": "2345",
|
||||
"lastUpdateDate": 1767607770000
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Müller Apotheke",
|
||||
"customerNumber": "1234",
|
||||
"city": "Hannover",
|
||||
"zip": "12345",
|
||||
"pictures": [
|
||||
{
|
||||
"id": 1,
|
||||
"comment": "good looking picture 1",
|
||||
"category": null,
|
||||
"pictureDate": 1767262170000,
|
||||
"username": "verboomp",
|
||||
"evaluation": 1,
|
||||
"imageUrl": "http://localhost:8180/api/picture/image/1?size=1",
|
||||
"normalSizeUrl": "http://localhost:8180/api/picture/image/1?size=2",
|
||||
"thumbnailSizeUrl": "http://localhost:8180/api/picture/image/1?size=3"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"comment": "good looking picture 2",
|
||||
"category": null,
|
||||
"pictureDate": 1767348570000,
|
||||
"username": "verboomp",
|
||||
"evaluation": 1,
|
||||
"imageUrl": "http://localhost:8180/api/picture/image/2?size=1",
|
||||
"normalSizeUrl": "http://localhost:8180/api/picture/image/2?size=2",
|
||||
"thumbnailSizeUrl": "http://localhost:8180/api/picture/image/2?size=3"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Müller Apotheke",
|
||||
"customerNumber": "1234",
|
||||
"lastUpdateDate": 1767348570000
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Meier Apotheke",
|
||||
"customerNumber": "2345",
|
||||
"lastUpdateDate": 1767607770000
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Schmidt Apotheke",
|
||||
"customerNumber": "3456",
|
||||
"lastUpdateDate": 1768212570000
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
connection=jdbc:postgresql://localhost:5430/fotodocumentation
|
||||
username=fotodocumentation
|
||||
password=fotodocumentation
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
|
||||
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
|
||||
<param name="Target" value="System.out"/>
|
||||
<param name="Threshold" value="DEBUG"/>
|
||||
<layout class="org.apache.log4j.PatternLayout">
|
||||
<!-- The default pattern: Date Priority [Category] Message\n -->
|
||||
<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c] %m%n"/>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
|
||||
<!-- ================ -->
|
||||
<!-- Limit categories -->
|
||||
<!-- ================ -->
|
||||
<!-- Limit the org.apache category to INFO as its DEBUG is verbose -->
|
||||
<category name="org.apache">
|
||||
<priority value="INFO"/>
|
||||
</category>
|
||||
<category name="com.bm">
|
||||
<priority value="INFO"/>
|
||||
</category>
|
||||
<category name="com.bm.introspectors">
|
||||
<priority value="ERROR"/>
|
||||
</category>
|
||||
|
||||
<category name="org.hibernate.cfg.annotations">
|
||||
<priority value="WARN"/>
|
||||
</category>
|
||||
|
||||
<category name="org.hibernate.cfg">
|
||||
<priority value="WARN"/>
|
||||
</category>
|
||||
|
||||
<category name="org.hibernate.tool">
|
||||
<priority value="WARN"/>
|
||||
</category>
|
||||
|
||||
<category name="org.hibernate.validator">
|
||||
<priority value="WARN"/>
|
||||
</category>
|
||||
|
||||
<category name="org.hibernate">
|
||||
<priority value="ERROR"/>
|
||||
</category>
|
||||
|
||||
|
||||
<category name="org.dbunit">
|
||||
<priority value="DEBUG"/>
|
||||
</category>
|
||||
|
||||
<category name="org.apache.http">
|
||||
<priority value="INFO"/>
|
||||
</category>
|
||||
|
||||
<category name="STDOUT">
|
||||
<priority value="DEBUG"/>
|
||||
</category>
|
||||
|
||||
<root>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
</log4j:configuration>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(flutter test:*)",
|
||||
"Bash(cat:*)",
|
||||
"Bash(flutter gen-l10n:*)",
|
||||
"Bash(flutter analyze:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
45
hartmann-foto-documentation-frontend/.gitignore
vendored
Normal file
45
hartmann-foto-documentation-frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.build/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
.swiftpm/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins-dependencies
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
/coverage/
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
30
hartmann-foto-documentation-frontend/.metadata
Normal file
30
hartmann-foto-documentation-frontend/.metadata
Normal file
@@ -0,0 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "ac4e799d237041cf905519190471f657b657155a"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: ac4e799d237041cf905519190471f657b657155a
|
||||
base_revision: ac4e799d237041cf905519190471f657b657155a
|
||||
- platform: web
|
||||
create_revision: ac4e799d237041cf905519190471f657b657155a
|
||||
base_revision: ac4e799d237041cf905519190471f657b657155a
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
25
hartmann-foto-documentation-frontend/.vscode/launch.json
vendored
Normal file
25
hartmann-foto-documentation-frontend/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "foto-frontend",
|
||||
"request": "launch",
|
||||
"type": "dart"
|
||||
},
|
||||
{
|
||||
"name": "foto-frontend (profile mode)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "profile"
|
||||
},
|
||||
{
|
||||
"name": "foto-frontend (release mode)",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"flutterMode": "release"
|
||||
}
|
||||
]
|
||||
}
|
||||
16
hartmann-foto-documentation-frontend/README.md
Normal file
16
hartmann-foto-documentation-frontend/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# foto documentation
|
||||
|
||||
A new Flutter project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
28
hartmann-foto-documentation-frontend/analysis_options.yaml
Normal file
28
hartmann-foto-documentation-frontend/analysis_options.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at https://dart.dev/lints.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
BIN
hartmann-foto-documentation-frontend/assets/images/logo.png
Normal file
BIN
hartmann-foto-documentation-frontend/assets/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user