Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 483c31bd1e |
96 changed files with 360 additions and 3275 deletions
1
.gitea/CODEOWNERS
Normal file
1
.gitea/CODEOWNERS
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* @jorge.bornhausen
|
||||||
42
.gitea/workflows/build.yml
Normal file
42
.gitea/workflows/build.yml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
name: Build, test and publish the Quarkus libraries
|
||||||
|
on: [ push, workflow_dispatch ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout the code
|
||||||
|
uses: https://github.com/actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up JDK 21
|
||||||
|
uses: https://github.com/actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '21'
|
||||||
|
|
||||||
|
- name: Configure Maven CI/CD settings
|
||||||
|
uses: https://github.com/s4u/maven-settings-action@v3.1.0
|
||||||
|
with:
|
||||||
|
servers: |
|
||||||
|
[{
|
||||||
|
"id": "kvant",
|
||||||
|
"configuration": {
|
||||||
|
"httpHeaders": {
|
||||||
|
"property": {
|
||||||
|
"name": "Authorization",
|
||||||
|
"value": "token ${{ secrets.PHOENIX_PACKAGE_WRITER_TOKEN }}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
- name: Make maven wrapper executable
|
||||||
|
run: chmod +x mvnw
|
||||||
|
|
||||||
|
- name: Build and run tests
|
||||||
|
run: ./mvnw verify
|
||||||
|
|
||||||
|
- name: Upload libs to Gitea Maven Registry
|
||||||
|
if: gitea.ref == 'refs/heads/main'
|
||||||
|
run: ./mvnw deploy -Dmaven.test.skip=true -Dmaven.javadoc.skip=true
|
||||||
5
.github/project.yaml
vendored
5
.github/project.yaml
vendored
|
|
@ -1,5 +0,0 @@
|
||||||
name: Quarkus Commons
|
|
||||||
release:
|
|
||||||
current-version: "1.1.4"
|
|
||||||
next-version: "1.1.5-SNAPSHOT"
|
|
||||||
|
|
||||||
99
.github/workflows/build.yaml
vendored
99
.github/workflows/build.yaml
vendored
|
|
@ -1,99 +0,0 @@
|
||||||
name: Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- "main"
|
|
||||||
tags:
|
|
||||||
- '[0-9]+.[0-9]+.[0-9]+'
|
|
||||||
paths-ignore:
|
|
||||||
- '.gitattributes'
|
|
||||||
- '.gitignore'
|
|
||||||
- '.github/renovate.json5'
|
|
||||||
- '.github/project.yaml'
|
|
||||||
- 'docs/**'
|
|
||||||
- 'README.md'
|
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
COMMON_MAVEN_OPTS: "-e -B --fae"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout the code
|
|
||||||
uses: https://github.com/actions/checkout@v6
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
|
||||||
|
|
||||||
- name: Set up JDK 21
|
|
||||||
uses: https://github.com/actions/setup-java@v5
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '21'
|
|
||||||
|
|
||||||
- name: Generate cache keys
|
|
||||||
id: cache-key
|
|
||||||
run: |
|
|
||||||
CURRENT_BRANCH="${{ github.head_ref || github.ref_name }}"
|
|
||||||
CURRENT_MONTH=$(/bin/date -u "+%Y-%m")
|
|
||||||
CURRENT_DAY=$(/bin/date -u "+%d")
|
|
||||||
ROOT_CACHE_KEY="m2-cache-quarkus-commons"
|
|
||||||
echo "m2-monthly-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}" >> $GITHUB_OUTPUT
|
|
||||||
echo "m2-monthly-branch-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}" >> $GITHUB_OUTPUT
|
|
||||||
echo "m2-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}-${CURRENT_DAY}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Cache Maven Repository
|
|
||||||
id: cache-maven
|
|
||||||
uses: https://github.com/actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.m2/repository
|
|
||||||
# The cache is per branch but in case we don't find a branch for a given branch, we will get a cache from another branch.
|
|
||||||
key: ${{ steps.cache-key.outputs.m2-cache-key }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ steps.cache-key.outputs.m2-monthly-branch-cache-key }}
|
|
||||||
${{ steps.cache-key.outputs.m2-monthly-cache-key }}
|
|
||||||
|
|
||||||
- name: Configure Maven CI/CD settings
|
|
||||||
uses: https://github.com/s4u/maven-settings-action@v4.0.0
|
|
||||||
with:
|
|
||||||
servers: |
|
|
||||||
[{
|
|
||||||
"id": "phoenix-oss",
|
|
||||||
"username": "${{ vars.ORG_PACKAGE_WRITER_USERNAME }}",
|
|
||||||
"password": "${{ secrets.ORG_PACKAGE_WRITER_TOKEN }}"
|
|
||||||
}]
|
|
||||||
|
|
||||||
- name: Make maven wrapper executable
|
|
||||||
run: chmod +x mvnw
|
|
||||||
|
|
||||||
- name: Validate tag
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
run: |
|
|
||||||
TAG_NAME="${GITHUB_REF#refs/tags/}"
|
|
||||||
PROJECT_VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)
|
|
||||||
|
|
||||||
if [[ "$PROJECT_VERSION" != "$TAG_NAME" ]]; then
|
|
||||||
echo "::error::pom.xml version '$PROJECT_VERSION' does not match tag '$TAG_NAME'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Download dependencies
|
|
||||||
run: ./mvnw $COMMON_MAVEN_OPTS quarkus:go-offline
|
|
||||||
|
|
||||||
- name: Build and run tests
|
|
||||||
run: ./mvnw $COMMON_MAVEN_OPTS verify
|
|
||||||
|
|
||||||
- name: Analyze with Sonar
|
|
||||||
if: vars.SONAR_ENABLED == 'true'
|
|
||||||
env:
|
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
||||||
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
|
|
||||||
run: ./mvnw $COMMON_MAVEN_OPTS org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=quarkus-commons -Dsonar.projectName='quarkus-commons' -Dsonar.coverage.jacoco.xmlReportPaths=../**/target/jacoco-report/jacoco.xml
|
|
||||||
|
|
||||||
- name: Publish jars
|
|
||||||
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
|
|
||||||
run: ./mvnw $COMMON_MAVEN_OPTS deploy -Dmaven.test.skip=true -Dmaven.javadoc.skip=true
|
|
||||||
97
.github/workflows/release.yaml
vendored
97
.github/workflows/release.yaml
vendored
|
|
@ -1,97 +0,0 @@
|
||||||
name: Release the current version
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- "main"
|
|
||||||
paths:
|
|
||||||
- '.github/project.yaml'
|
|
||||||
|
|
||||||
env:
|
|
||||||
COMMON_MAVEN_OPTS: "-e -B --fae"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
name: Execute the release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout the code
|
|
||||||
uses: https://github.com/actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Retrieve project metadata
|
|
||||||
uses: https://github.com/radcortez/project-metadata-action@main
|
|
||||||
id: metadata
|
|
||||||
with:
|
|
||||||
metadata-file-path: '.github/project.yaml'
|
|
||||||
local-file: true
|
|
||||||
|
|
||||||
- name: Validate current version
|
|
||||||
if: contains(steps.metadata.outputs.current-version, 'SNAPSHOT')
|
|
||||||
run: |
|
|
||||||
echo '::error::Cannot release a SNAPSHOT version.'
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Validate next version
|
|
||||||
if: contains(steps.metadata.outputs.next-version, 'SNAPSHOT') == 'false'
|
|
||||||
run: |
|
|
||||||
echo '::error::Next development version should be a SNAPSHOT version.'
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Set environment variables
|
|
||||||
run: |
|
|
||||||
echo "CURRENT_VERSION=${{steps.metadata.outputs.current-version}}" >> $GITHUB_ENV
|
|
||||||
echo "NEXT_VERSION=${{steps.metadata.outputs.next-version}}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Configure SSH and Git
|
|
||||||
env:
|
|
||||||
SSH_DIR: /root/.ssh
|
|
||||||
MAVEN_RELEASE_SSH_KEY: ${{ secrets.MAVEN_RELEASE_SSH_KEY }}
|
|
||||||
run: |
|
|
||||||
mkdir -p ${SSH_DIR}/
|
|
||||||
touch ${SSH_DIR}/known_hosts
|
|
||||||
ssh-keyscan -t rsa -p 2222 git-ssh.kvant.cloud >> ${SSH_DIR}/known_hosts
|
|
||||||
echo "${MAVEN_RELEASE_SSH_KEY}" | base64 -d >> ${SSH_DIR}/id_rsa
|
|
||||||
chmod 600 ${SSH_DIR}/id_rsa
|
|
||||||
git config --global user.name 'maven_release_technical_account'
|
|
||||||
git config --global user.email 'maven-release-bot@phoenix-technologies.ch'
|
|
||||||
git config --global commit.gpgsign true
|
|
||||||
git config --global gpg.format ssh
|
|
||||||
git config --global user.signingkey ${SSH_DIR}/id_rsa
|
|
||||||
|
|
||||||
- name: Set up JDK 21
|
|
||||||
uses: https://github.com/actions/setup-java@v5
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: '21'
|
|
||||||
|
|
||||||
- name: Generate cache keys
|
|
||||||
id: cache-key
|
|
||||||
run: |
|
|
||||||
CURRENT_BRANCH="${{ github.ref_name }}"
|
|
||||||
CURRENT_MONTH=$(/bin/date -u "+%Y-%m")
|
|
||||||
CURRENT_DAY=$(/bin/date -u "+%d")
|
|
||||||
ROOT_CACHE_KEY="m2-cache-quarkus-commons"
|
|
||||||
echo "m2-monthly-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}" >> $GITHUB_OUTPUT
|
|
||||||
echo "m2-monthly-branch-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}" >> $GITHUB_OUTPUT
|
|
||||||
echo "m2-cache-key=${ROOT_CACHE_KEY}-${CURRENT_MONTH}-${CURRENT_BRANCH}-${CURRENT_DAY}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Cache Maven Repository
|
|
||||||
id: cache-maven
|
|
||||||
uses: https://github.com/actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.m2/repository
|
|
||||||
# The cache is per branch but in case we don't find a branch for a given branch, we will get a cache from another branch.
|
|
||||||
key: ${{ steps.cache-key.outputs.m2-cache-key }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ steps.cache-key.outputs.m2-monthly-branch-cache-key }}
|
|
||||||
${{ steps.cache-key.outputs.m2-monthly-cache-key }}
|
|
||||||
|
|
||||||
- name: Make maven wrapper executable
|
|
||||||
run: chmod +x mvnw
|
|
||||||
|
|
||||||
- name: Download dependencies
|
|
||||||
run: ./mvnw $COMMON_MAVEN_OPTS quarkus:go-offline
|
|
||||||
|
|
||||||
- name: Prepare release
|
|
||||||
run: ./mvnw $COMMON_MAVEN_OPTS release:prepare -DreleaseVersion=${CURRENT_VERSION} -DdevelopmentVersion=${NEXT_VERSION}
|
|
||||||
33
.github/workflows/validate-versions.yaml
vendored
33
.github/workflows/validate-versions.yaml
vendored
|
|
@ -1,33 +0,0 @@
|
||||||
name: Validate release versions
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- '.github/project.yaml'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout the code
|
|
||||||
uses: https://github.com/actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Retrieve project metadata
|
|
||||||
uses: https://github.com/radcortez/project-metadata-action@main
|
|
||||||
id: metadata
|
|
||||||
with:
|
|
||||||
metadata-file-path: '.github/project.yaml'
|
|
||||||
local-file: true
|
|
||||||
|
|
||||||
- name: Validate current version
|
|
||||||
if: contains(steps.metadata.outputs.current-version, 'SNAPSHOT')
|
|
||||||
run: |
|
|
||||||
echo '::error::Cannot release a SNAPSHOT version.'
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Validate next version
|
|
||||||
if: contains(steps.metadata.outputs.next-version, 'SNAPSHOT') == false
|
|
||||||
run: |
|
|
||||||
echo '::error::Next development version should be a SNAPSHOT version.'
|
|
||||||
exit 1
|
|
||||||
19
.mvn/wrapper/maven-wrapper.properties
vendored
19
.mvn/wrapper/maven-wrapper.properties
vendored
|
|
@ -1,2 +1,19 @@
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
wrapperVersion=3.3.2
|
||||||
distributionType=only-script
|
distributionType=only-script
|
||||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
||||||
|
|
|
||||||
19
README.md
19
README.md
|
|
@ -1,13 +1,14 @@
|
||||||
Quarkus Commons
|
Quarkus Commons
|
||||||
===============
|
===============
|
||||||
|
|
||||||
[](https://openjdk.org/projects/jdk/21/)
|
[](https://openjdk.org/projects/jdk/21/)
|
||||||
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
[](https://quarkus.io/guides/)
|
||||||
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
[](https://maven.apache.org/)
|
||||||
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
|
||||||
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
<!-- TOC -->
|
||||||
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
* [Quarkus Commons](#quarkus-commons)
|
||||||
[](https://sonarqube.pub.basel.kvant.cloud/dashboard?id=quarkus-commons)
|
* [Introduction](#introduction)
|
||||||
|
<!-- TOC -->
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
|
|
@ -16,13 +17,9 @@ that can be used by Quarkus applications.
|
||||||
|
|
||||||
The modules are:
|
The modules are:
|
||||||
|
|
||||||
* `quarkus-audit-tools`
|
|
||||||
* `quarkus-client-logger`
|
|
||||||
* `quarkus-clock-service`
|
* `quarkus-clock-service`
|
||||||
* `quarkus-json-service`
|
* `quarkus-json-service`
|
||||||
* `quarkus-message-digest-service`
|
* `quarkus-message-digest-service`
|
||||||
* `quarkus-random-number-generator`
|
* `quarkus-random-number-generator`
|
||||||
* `quarkus-table-generator`
|
|
||||||
* `quarkus-tracing-service`
|
|
||||||
* `quarkus-uuid-generator`
|
* `quarkus-uuid-generator`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
* @jorge.bornhausen @roque.caballero @joseluis.gutierrez @pawel.derehajlo @michal.tumilowicz @humberto.casanova
|
|
||||||
2
hooks/pre-commit
Normal file
2
hooks/pre-commit
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
./mvnw spotless:apply
|
||||||
50
mvnw
vendored
50
mvnw
vendored
|
|
@ -19,7 +19,7 @@
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Apache Maven Wrapper startup batch script, version 3.3.3
|
# Apache Maven Wrapper startup batch script, version 3.3.2
|
||||||
#
|
#
|
||||||
# Optional ENV vars
|
# Optional ENV vars
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|
@ -105,17 +105,14 @@ trim() {
|
||||||
printf "%s" "${1}" | tr -d '[:space:]'
|
printf "%s" "${1}" | tr -d '[:space:]'
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptDir="$(dirname "$0")"
|
|
||||||
scriptName="$(basename "$0")"
|
|
||||||
|
|
||||||
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
while IFS="=" read -r key value; do
|
while IFS="=" read -r key value; do
|
||||||
case "${key-}" in
|
case "${key-}" in
|
||||||
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||||
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||||
esac
|
esac
|
||||||
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||||
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||||
|
|
||||||
case "${distributionUrl##*/}" in
|
case "${distributionUrl##*/}" in
|
||||||
maven-mvnd-*bin.*)
|
maven-mvnd-*bin.*)
|
||||||
|
|
@ -133,7 +130,7 @@ maven-mvnd-*bin.*)
|
||||||
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||||
;;
|
;;
|
||||||
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||||
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
|
|
@ -230,7 +227,7 @@ if [ -n "${distributionSha256Sum-}" ]; then
|
||||||
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||||
exit 1
|
exit 1
|
||||||
elif command -v sha256sum >/dev/null; then
|
elif command -v sha256sum >/dev/null; then
|
||||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
|
||||||
distributionSha256Result=true
|
distributionSha256Result=true
|
||||||
fi
|
fi
|
||||||
elif command -v shasum >/dev/null; then
|
elif command -v shasum >/dev/null; then
|
||||||
|
|
@ -255,41 +252,8 @@ if command -v unzip >/dev/null; then
|
||||||
else
|
else
|
||||||
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||||
fi
|
fi
|
||||||
|
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
|
||||||
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||||
actualDistributionDir=""
|
|
||||||
|
|
||||||
# First try the expected directory name (for regular distributions)
|
|
||||||
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
|
|
||||||
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
|
|
||||||
actualDistributionDir="$distributionUrlNameMain"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If not found, search for any directory with the Maven executable (for snapshots)
|
|
||||||
if [ -z "$actualDistributionDir" ]; then
|
|
||||||
# enable globbing to iterate over items
|
|
||||||
set +f
|
|
||||||
for dir in "$TMP_DOWNLOAD_DIR"/*; do
|
|
||||||
if [ -d "$dir" ]; then
|
|
||||||
if [ -f "$dir/bin/$MVN_CMD" ]; then
|
|
||||||
actualDistributionDir="$(basename "$dir")"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
set -f
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$actualDistributionDir" ]; then
|
|
||||||
verbose "Contents of $TMP_DOWNLOAD_DIR:"
|
|
||||||
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
|
|
||||||
die "Could not find Maven distribution directory in extracted archive"
|
|
||||||
fi
|
|
||||||
|
|
||||||
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
|
||||||
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
|
|
||||||
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
|
||||||
|
|
||||||
clean || :
|
clean || :
|
||||||
exec_maven "$@"
|
exec_maven "$@"
|
||||||
|
|
|
||||||
338
mvnw.cmd
vendored
338
mvnw.cmd
vendored
|
|
@ -1,189 +1,149 @@
|
||||||
<# : batch portion
|
<# : batch portion
|
||||||
@REM ----------------------------------------------------------------------------
|
@REM ----------------------------------------------------------------------------
|
||||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||||
@REM or more contributor license agreements. See the NOTICE file
|
@REM or more contributor license agreements. See the NOTICE file
|
||||||
@REM distributed with this work for additional information
|
@REM distributed with this work for additional information
|
||||||
@REM regarding copyright ownership. The ASF licenses this file
|
@REM regarding copyright ownership. The ASF licenses this file
|
||||||
@REM to you under the Apache License, Version 2.0 (the
|
@REM to you under the Apache License, Version 2.0 (the
|
||||||
@REM "License"); you may not use this file except in compliance
|
@REM "License"); you may not use this file except in compliance
|
||||||
@REM with the License. You may obtain a copy of the License at
|
@REM with the License. You may obtain a copy of the License at
|
||||||
@REM
|
@REM
|
||||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||||
@REM
|
@REM
|
||||||
@REM Unless required by applicable law or agreed to in writing,
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
@REM software distributed under the License is distributed on an
|
@REM software distributed under the License is distributed on an
|
||||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
@REM KIND, either express or implied. See the License for the
|
@REM KIND, either express or implied. See the License for the
|
||||||
@REM specific language governing permissions and limitations
|
@REM specific language governing permissions and limitations
|
||||||
@REM under the License.
|
@REM under the License.
|
||||||
@REM ----------------------------------------------------------------------------
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
@REM ----------------------------------------------------------------------------
|
@REM ----------------------------------------------------------------------------
|
||||||
@REM Apache Maven Wrapper startup batch script, version 3.3.3
|
@REM Apache Maven Wrapper startup batch script, version 3.3.2
|
||||||
@REM
|
@REM
|
||||||
@REM Optional ENV vars
|
@REM Optional ENV vars
|
||||||
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||||
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||||
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||||
@REM ----------------------------------------------------------------------------
|
@REM ----------------------------------------------------------------------------
|
||||||
|
|
||||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||||
@SET __MVNW_CMD__=
|
@SET __MVNW_CMD__=
|
||||||
@SET __MVNW_ERROR__=
|
@SET __MVNW_ERROR__=
|
||||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||||
@SET PSModulePath=
|
@SET PSModulePath=
|
||||||
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||||
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||||
)
|
)
|
||||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||||
@SET __MVNW_PSMODULEP_SAVE=
|
@SET __MVNW_PSMODULEP_SAVE=
|
||||||
@SET __MVNW_ARG0_NAME__=
|
@SET __MVNW_ARG0_NAME__=
|
||||||
@SET MVNW_USERNAME=
|
@SET MVNW_USERNAME=
|
||||||
@SET MVNW_PASSWORD=
|
@SET MVNW_PASSWORD=
|
||||||
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
|
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
|
||||||
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||||
@GOTO :EOF
|
@GOTO :EOF
|
||||||
: end batch / begin powershell #>
|
: end batch / begin powershell #>
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
if ($env:MVNW_VERBOSE -eq "true") {
|
if ($env:MVNW_VERBOSE -eq "true") {
|
||||||
$VerbosePreference = "Continue"
|
$VerbosePreference = "Continue"
|
||||||
}
|
}
|
||||||
|
|
||||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||||
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||||
if (!$distributionUrl) {
|
if (!$distributionUrl) {
|
||||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||||
}
|
}
|
||||||
|
|
||||||
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||||
"maven-mvnd-*" {
|
"maven-mvnd-*" {
|
||||||
$USE_MVND = $true
|
$USE_MVND = $true
|
||||||
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||||
$MVN_CMD = "mvnd.cmd"
|
$MVN_CMD = "mvnd.cmd"
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default {
|
default {
|
||||||
$USE_MVND = $false
|
$USE_MVND = $false
|
||||||
$MVN_CMD = $script -replace '^mvnw','mvn'
|
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||||
if ($env:MVNW_REPOURL) {
|
if ($env:MVNW_REPOURL) {
|
||||||
$MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||||
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
|
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
|
||||||
}
|
}
|
||||||
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||||
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||||
|
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
|
||||||
$MAVEN_M2_PATH = "$HOME/.m2"
|
if ($env:MAVEN_USER_HOME) {
|
||||||
if ($env:MAVEN_USER_HOME) {
|
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
|
||||||
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
|
}
|
||||||
}
|
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||||
|
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||||
if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
|
|
||||||
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
|
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||||
}
|
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
$MAVEN_WRAPPER_DISTS = $null
|
exit $?
|
||||||
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
|
}
|
||||||
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
|
|
||||||
} else {
|
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||||
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
|
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||||
}
|
}
|
||||||
|
|
||||||
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
|
# prepare tmp dir
|
||||||
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||||
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||||
|
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||||
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
trap {
|
||||||
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
exit $?
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
|
||||||
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||||
}
|
|
||||||
|
# Download and Install Apache Maven
|
||||||
# prepare tmp dir
|
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||||
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
Write-Verbose "Downloading from: $distributionUrl"
|
||||||
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||||
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
|
||||||
trap {
|
$webclient = New-Object System.Net.WebClient
|
||||||
if ($TMP_DOWNLOAD_DIR.Exists) {
|
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
}
|
||||||
}
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||||
}
|
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||||
|
|
||||||
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||||
|
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||||
# Download and Install Apache Maven
|
if ($distributionSha256Sum) {
|
||||||
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
if ($USE_MVND) {
|
||||||
Write-Verbose "Downloading from: $distributionUrl"
|
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||||
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
}
|
||||||
|
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||||
$webclient = New-Object System.Net.WebClient
|
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||||
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||||
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
}
|
||||||
}
|
}
|
||||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
||||||
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
# unzip and move
|
||||||
|
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||||
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
try {
|
||||||
if ($distributionSha256Sum) {
|
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||||
if ($USE_MVND) {
|
} catch {
|
||||||
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||||
}
|
Write-Error "fail to move MAVEN_HOME"
|
||||||
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
}
|
||||||
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
} finally {
|
||||||
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||||
}
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||||
}
|
}
|
||||||
|
|
||||||
# unzip and move
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||||
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
|
||||||
|
|
||||||
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
|
||||||
$actualDistributionDir = ""
|
|
||||||
|
|
||||||
# First try the expected directory name (for regular distributions)
|
|
||||||
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
|
|
||||||
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
|
|
||||||
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
|
|
||||||
$actualDistributionDir = $distributionUrlNameMain
|
|
||||||
}
|
|
||||||
|
|
||||||
# If not found, search for any directory with the Maven executable (for snapshots)
|
|
||||||
if (!$actualDistributionDir) {
|
|
||||||
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
|
|
||||||
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
|
|
||||||
if (Test-Path -Path $testPath -PathType Leaf) {
|
|
||||||
$actualDistributionDir = $_.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$actualDistributionDir) {
|
|
||||||
Write-Error "Could not find Maven distribution directory in extracted archive"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
|
||||||
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
|
|
||||||
try {
|
|
||||||
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
|
||||||
} catch {
|
|
||||||
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
|
||||||
Write-Error "fail to move MAVEN_HOME"
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
|
||||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
|
||||||
|
|
|
||||||
104
pom.xml
104
pom.xml
|
|
@ -1,60 +1,52 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenixtechnologies.quarkus</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
<version>1.0.1-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>quarkus-audit-tools</module>
|
|
||||||
<module>quarkus-client-logger</module>
|
|
||||||
<module>quarkus-clock-service</module>
|
<module>quarkus-clock-service</module>
|
||||||
<module>quarkus-json-service</module>
|
<module>quarkus-json-service</module>
|
||||||
<module>quarkus-message-digest-service</module>
|
<module>quarkus-message-digest-service</module>
|
||||||
<module>quarkus-random-number-generator</module>
|
<module>quarkus-random-number-generator</module>
|
||||||
<module>quarkus-table-generator</module>
|
|
||||||
<module>quarkus-tracing-service</module>
|
|
||||||
<module>quarkus-uuid-generator</module>
|
<module>quarkus-uuid-generator</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<!-- Quarkus properties -->
|
<java.version>21</java.version>
|
||||||
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
|
||||||
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
|
||||||
<quarkus.platform.version>3.30.2</quarkus.platform.version>
|
|
||||||
|
|
||||||
<!-- Plugin versions -->
|
|
||||||
<compiler-plugin.version>3.14.1</compiler-plugin.version>
|
|
||||||
<surefire-plugin.version>3.5.4</surefire-plugin.version>
|
|
||||||
<spotless-plugin.version>3.1.0</spotless-plugin.version>
|
|
||||||
<palantir-java-format.version>2.72.0</palantir-java-format.version>
|
|
||||||
<jacoco-plugin.version>0.8.14</jacoco-plugin.version> <!-- Match with version from Quarkus BOM -->
|
|
||||||
<jandex-plugin.version>3.5.2</jandex-plugin.version>
|
|
||||||
<release-plugin.version>3.2.0</release-plugin.version>
|
|
||||||
<source-plugin.version>3.4.0</source-plugin.version>
|
|
||||||
|
|
||||||
<!-- Build properties -->
|
|
||||||
<maven.compiler.release>21</maven.compiler.release>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<skipITs>true</skipITs>
|
|
||||||
|
<compiler-plugin.version>3.13.0</compiler-plugin.version>
|
||||||
|
<surefire-plugin.version>3.5.2</surefire-plugin.version>
|
||||||
|
<jacoco-plugin.version>0.8.12</jacoco-plugin.version>
|
||||||
|
<spotless-plugin.version>2.43.0</spotless-plugin.version>
|
||||||
|
<palantir-java-format.version>2.50.0</palantir-java-format.version>
|
||||||
|
<git-build-hook-plugin.version>3.5.0</git-build-hook-plugin.version>
|
||||||
|
|
||||||
|
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
||||||
|
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
||||||
|
<quarkus.platform.version>3.16.2</quarkus.platform.version>
|
||||||
|
|
||||||
<!-- Dependencies -->
|
<!-- Dependencies -->
|
||||||
<java-uuid-generator.version>5.2.0</java-uuid-generator.version>
|
<java-uuid-generator.version>5.1.0</java-uuid-generator.version>
|
||||||
|
|
||||||
<!-- Test dependencies -->
|
<!-- Test dependencies -->
|
||||||
<assertj-core.version>3.27.6</assertj-core.version>
|
<assertj-core.version>3.26.3</assertj-core.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
<repository>
|
<repository>
|
||||||
<id>phoenix-oss</id>
|
<id>kvant</id>
|
||||||
<url>https://git.kvant.cloud/api/packages/phoenix-oss/maven</url>
|
<url>https://git.kvant.cloud/api/packages/phoenix/maven</url>
|
||||||
</repository>
|
</repository>
|
||||||
<snapshotRepository>
|
<snapshotRepository>
|
||||||
<id>phoenix-oss</id>
|
<id>kvant</id>
|
||||||
<url>https://git.kvant.cloud/api/packages/phoenix-oss/maven</url>
|
<url>https://git.kvant.cloud/api/packages/phoenix/maven</url>
|
||||||
</snapshotRepository>
|
</snapshotRepository>
|
||||||
</distributionManagement>
|
</distributionManagement>
|
||||||
|
|
||||||
|
|
@ -70,22 +62,11 @@
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
<scm>
|
|
||||||
<connection>scm:git:ssh://git@git-ssh.kvant.cloud:2222/phoenix-oss/quarkus-commons.git</connection>
|
|
||||||
<developerConnection>scm:git:ssh://git@git-ssh.kvant.cloud:2222/phoenix-oss/quarkus-commons.git</developerConnection>
|
|
||||||
<url>https://git.kvant.cloud/phoenix-oss/quarkus-commons.git</url>
|
|
||||||
<tag>HEAD</tag>
|
|
||||||
</scm>
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-arc</artifactId>
|
<artifactId>quarkus-arc</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-cyclonedx</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.assertj</groupId>
|
<groupId>org.assertj</groupId>
|
||||||
<artifactId>assertj-core</artifactId>
|
<artifactId>assertj-core</artifactId>
|
||||||
|
|
@ -139,6 +120,7 @@
|
||||||
<compilerArgs>
|
<compilerArgs>
|
||||||
<arg>-parameters</arg>
|
<arg>-parameters</arg>
|
||||||
</compilerArgs>
|
</compilerArgs>
|
||||||
|
<release>${java.version}</release>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
@ -167,7 +149,7 @@
|
||||||
</executions>
|
</executions>
|
||||||
<configuration>
|
<configuration>
|
||||||
<java>
|
<java>
|
||||||
<removeUnusedImports />
|
<removeUnusedImports/>
|
||||||
<palantirJavaFormat>
|
<palantirJavaFormat>
|
||||||
<version>${palantir-java-format.version}</version>
|
<version>${palantir-java-format.version}</version>
|
||||||
<style>PALANTIR</style>
|
<style>PALANTIR</style>
|
||||||
|
|
@ -177,40 +159,18 @@
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>io.smallrye</groupId>
|
<groupId>com.rudikershaw.gitbuildhook</groupId>
|
||||||
<artifactId>jandex-maven-plugin</artifactId>
|
<artifactId>git-build-hook-maven-plugin</artifactId>
|
||||||
<version>${jandex-plugin.version}</version>
|
<version>${git-build-hook-plugin.version}</version>
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>make-index</id>
|
|
||||||
<goals>
|
|
||||||
<goal>jandex</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-release-plugin</artifactId>
|
|
||||||
<version>${release-plugin.version}</version>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<tagNameFormat>@{project.version}</tagNameFormat>
|
<installHooks>
|
||||||
<checkModificationExcludes>mvnw</checkModificationExcludes>
|
<pre-commit>hooks/pre-commit</pre-commit>
|
||||||
<scmReleaseCommitComment>chore: release @{releaseLabel}</scmReleaseCommitComment>
|
</installHooks>
|
||||||
<scmDevelopmentCommitComment>chore: prepare for next development iteration</scmDevelopmentCommitComment>
|
|
||||||
<autoVersionSubmodules>true</autoVersionSubmodules>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
|
||||||
<version>${source-plugin.version}</version>
|
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>attach-sources</id>
|
|
||||||
<phase>verify</phase>
|
|
||||||
<goals>
|
<goals>
|
||||||
<goal>jar-no-fork</goal>
|
<goal>install</goal>
|
||||||
</goals>
|
</goals>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
|
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
|
||||||
<artifactId>quarkus-commons</artifactId>
|
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>quarkus-audit-tools</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
|
||||||
<artifactId>quarkus-tracing-service</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-hibernate-envers</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-hibernate-orm-panache</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-jdbc-postgresql</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-flyway</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-flyway-postgresql</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-elytron-security-properties-file</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-config-yaml</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
<version>${jacoco-plugin.version}</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>jacoco-check</id>
|
|
||||||
<goals>
|
|
||||||
<goal>check</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>test</phase>
|
|
||||||
<configuration>
|
|
||||||
<dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>
|
|
||||||
<rules>
|
|
||||||
<rule>
|
|
||||||
<element>BUNDLE</element>
|
|
||||||
<limits>
|
|
||||||
<limit>
|
|
||||||
<counter>INSTRUCTION</counter>
|
|
||||||
<value>COVEREDRATIO</value>
|
|
||||||
<minimum>1</minimum>
|
|
||||||
</limit>
|
|
||||||
</limits>
|
|
||||||
</rule>
|
|
||||||
</rules>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import jakarta.enterprise.inject.spi.CDI;
|
|
||||||
import org.hibernate.envers.RevisionListener;
|
|
||||||
|
|
||||||
public class AuditRevisionListener implements RevisionListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void newRevision(Object revisionEntity) {
|
|
||||||
var provider = CDI.current().select(RevisionContextProvider.class).get();
|
|
||||||
|
|
||||||
var rev = (Revision) revisionEntity;
|
|
||||||
rev.actor = provider.getActor();
|
|
||||||
rev.traceId = provider.getTraceId();
|
|
||||||
rev.spanId = provider.getSpanId();
|
|
||||||
rev.requestId = provider.getRequestId();
|
|
||||||
rev.clientIp = provider.getClientIp();
|
|
||||||
rev.hostName = provider.getHostName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import io.quarkus.hibernate.orm.panache.PanacheEntity;
|
|
||||||
import jakarta.persistence.Column;
|
|
||||||
import jakarta.persistence.MappedSuperclass;
|
|
||||||
import java.time.Instant;
|
|
||||||
import org.hibernate.envers.NotAudited;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The goal of this class is to have on the entity itself the exact same
|
|
||||||
* timestamps as the ones from revisions generated by envers. Because of that,
|
|
||||||
* we can't use @CreationTimestamp and @UpdateTimestamp, as those timestamp values
|
|
||||||
* are managed by different Hibernate classes, so the generated values will drift.
|
|
||||||
* <p>
|
|
||||||
* Manually setting these values to match envers revisions would be error-prone,
|
|
||||||
* verbose and tedious. So, the recommendation is to implement triggers on the
|
|
||||||
* audit tables which will update the main entity whenever a revision is created.
|
|
||||||
* An example of how to do that can be found in this module's integration tests.
|
|
||||||
*/
|
|
||||||
@MappedSuperclass
|
|
||||||
public abstract class AuditedPanacheEntity extends PanacheEntity {
|
|
||||||
|
|
||||||
@NotAudited
|
|
||||||
@Column(updatable = false)
|
|
||||||
public Instant createdAt;
|
|
||||||
|
|
||||||
@NotAudited
|
|
||||||
@Column(updatable = false)
|
|
||||||
public Instant lastUpdatedAt;
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
|
|
||||||
import jakarta.persistence.Column;
|
|
||||||
import jakarta.persistence.MappedSuperclass;
|
|
||||||
import java.time.Instant;
|
|
||||||
import org.hibernate.envers.NotAudited;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The goal of this class is to have on the entity itself the exact same
|
|
||||||
* timestamps as the ones from revisions generated by envers. Because of that,
|
|
||||||
* we can't use @CreationTimestamp and @UpdateTimestamp, as those timestamp values
|
|
||||||
* are managed by different Hibernate classes, so the generated values will drift.
|
|
||||||
* <p>
|
|
||||||
* Manually setting these values to match envers revisions would be error-prone,
|
|
||||||
* verbose and tedious. So, the recommendation is to implement triggers on the
|
|
||||||
* audit tables which will update the main entity whenever a revision is created.
|
|
||||||
* An example of how to do that can be found in this module's integration tests.
|
|
||||||
*/
|
|
||||||
@MappedSuperclass
|
|
||||||
public abstract class AuditedPanacheEntityBase extends PanacheEntityBase {
|
|
||||||
|
|
||||||
@NotAudited
|
|
||||||
@Column(updatable = false)
|
|
||||||
public Instant createdAt;
|
|
||||||
|
|
||||||
@NotAudited
|
|
||||||
@Column(updatable = false)
|
|
||||||
public Instant lastUpdatedAt;
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import ch.phoenix.oss.quarkus.commons.tracing.TracingService;
|
|
||||||
import io.opentelemetry.instrumentation.annotations.WithSpan;
|
|
||||||
import io.quarkus.arc.DefaultBean;
|
|
||||||
import io.quarkus.arc.Unremovable;
|
|
||||||
import io.quarkus.logging.Log;
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
|
|
||||||
@Unremovable
|
|
||||||
@DefaultBean
|
|
||||||
@ApplicationScoped
|
|
||||||
class DefaultRevisionContextProvider implements RevisionContextProvider {
|
|
||||||
|
|
||||||
private static final String UNKNOWN = "unknown";
|
|
||||||
|
|
||||||
private final TracingService tracingService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
DefaultRevisionContextProvider(TracingService tracingService) {
|
|
||||||
this.tracingService = tracingService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WithSpan
|
|
||||||
public String getActor() {
|
|
||||||
return tracingService.getActor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WithSpan
|
|
||||||
public String getTraceId() {
|
|
||||||
return tracingService.getTraceId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WithSpan
|
|
||||||
public String getSpanId() {
|
|
||||||
return tracingService.getSpanId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WithSpan
|
|
||||||
public String getRequestId() {
|
|
||||||
return tracingService.getRequestId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WithSpan
|
|
||||||
public String getClientIp() {
|
|
||||||
return tracingService.getClientIp();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@WithSpan
|
|
||||||
public String getHostName() {
|
|
||||||
try {
|
|
||||||
return InetAddress.getLocalHost().getHostName();
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
Log.error("Unable to determine host name", e);
|
|
||||||
return UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Objects;
|
|
||||||
import org.hibernate.envers.RevisionEntity;
|
|
||||||
import org.hibernate.envers.RevisionNumber;
|
|
||||||
import org.hibernate.envers.RevisionTimestamp;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "revinfo")
|
|
||||||
@RevisionEntity(AuditRevisionListener.class)
|
|
||||||
public class Revision extends PanacheEntityBase {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue
|
|
||||||
@RevisionNumber
|
|
||||||
public long rev;
|
|
||||||
|
|
||||||
@RevisionTimestamp
|
|
||||||
@Column(nullable = false, updatable = false)
|
|
||||||
public Instant timestamp;
|
|
||||||
|
|
||||||
@Column(updatable = false)
|
|
||||||
public String actor;
|
|
||||||
|
|
||||||
@Column(updatable = false)
|
|
||||||
public String traceId;
|
|
||||||
|
|
||||||
@Column(updatable = false)
|
|
||||||
public String spanId;
|
|
||||||
|
|
||||||
@Column(updatable = false)
|
|
||||||
public String requestId;
|
|
||||||
|
|
||||||
@Column(updatable = false)
|
|
||||||
public String clientIp;
|
|
||||||
|
|
||||||
@Column(updatable = false)
|
|
||||||
public String hostName;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (!(o instanceof Revision that)) return false;
|
|
||||||
return rev == that.rev;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(rev);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Revision{rev=" + rev + '}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
public interface RevisionContextProvider {
|
|
||||||
|
|
||||||
String getActor();
|
|
||||||
|
|
||||||
String getTraceId();
|
|
||||||
|
|
||||||
String getSpanId();
|
|
||||||
|
|
||||||
String getRequestId();
|
|
||||||
|
|
||||||
String getClientIp();
|
|
||||||
|
|
||||||
String getHostName();
|
|
||||||
}
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import ch.phoenix.oss.quarkus.commons.tracing.TracingService;
|
|
||||||
import io.quarkus.narayana.jta.QuarkusTransaction;
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import jakarta.persistence.EntityManager;
|
|
||||||
import java.time.Instant;
|
|
||||||
import org.hibernate.envers.AuditReaderFactory;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class AuditedPanacheEntityBaseTest {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
EntityManager entityManager;
|
|
||||||
|
|
||||||
@InjectSpy
|
|
||||||
TracingService tracingService;
|
|
||||||
|
|
||||||
@SuppressWarnings("Convert2MethodRef")
|
|
||||||
@BeforeEach
|
|
||||||
void setup() {
|
|
||||||
QuarkusTransaction.requiringNew().run(() -> TestEntity2.deleteAll());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void persistAndUpdate() {
|
|
||||||
when(tracingService.getRequestId()).thenReturn("00000000-0000-0000-0000-000000000000");
|
|
||||||
when(tracingService.getSpanId()).thenReturn("0000000000000000");
|
|
||||||
when(tracingService.getTraceId()).thenReturn("00000000000000000000000000000000");
|
|
||||||
when(tracingService.getActor()).thenReturn("unknown");
|
|
||||||
when(tracingService.getClientIp()).thenReturn("unknown");
|
|
||||||
|
|
||||||
var now = Instant.now();
|
|
||||||
|
|
||||||
QuarkusTransaction.requiringNew().run(() -> {
|
|
||||||
var entity = new TestEntity2("something");
|
|
||||||
entity.persist();
|
|
||||||
});
|
|
||||||
|
|
||||||
QuarkusTransaction.requiringNew().run(() -> {
|
|
||||||
var entity = TestEntity2.findBySomething("something");
|
|
||||||
assertAll(
|
|
||||||
() -> assertThat(entity.createdAt)
|
|
||||||
.as("createdAt should be after or equal to expected value")
|
|
||||||
.isAfterOrEqualTo(now),
|
|
||||||
() -> assertThat(entity.lastUpdatedAt)
|
|
||||||
.as("lastUpdatedAt should be equal to createdAt")
|
|
||||||
.isEqualTo(entity.createdAt));
|
|
||||||
|
|
||||||
var auditReader = AuditReaderFactory.get(entityManager);
|
|
||||||
|
|
||||||
var revisions = auditReader.getRevisions(TestEntity2.class, entity.id);
|
|
||||||
assertThat(revisions).hasSize(1);
|
|
||||||
|
|
||||||
var revInfo = entityManager.find(Revision.class, revisions.getFirst());
|
|
||||||
assertThat(revInfo).isNotNull();
|
|
||||||
|
|
||||||
assertAll(
|
|
||||||
() -> assertThat(revInfo.timestamp)
|
|
||||||
.as("revision timestamp should be equal to entity's createdAt timestamp")
|
|
||||||
.isEqualTo(entity.createdAt),
|
|
||||||
() -> assertThat(revInfo.actor)
|
|
||||||
.as("actor should match expected value")
|
|
||||||
.isEqualTo("unknown"),
|
|
||||||
() -> assertThat(revInfo.traceId)
|
|
||||||
.as("traceId should match expected value")
|
|
||||||
.isEqualTo("00000000000000000000000000000000"),
|
|
||||||
() -> assertThat(revInfo.spanId)
|
|
||||||
.as("spanId should match expected value")
|
|
||||||
.isEqualTo("0000000000000000"),
|
|
||||||
() -> assertThat(revInfo.requestId)
|
|
||||||
.as("requestId should match expected value")
|
|
||||||
.isEqualTo("00000000-0000-0000-0000-000000000000"),
|
|
||||||
() -> assertThat(revInfo.clientIp)
|
|
||||||
.as("clientIp should match expected value")
|
|
||||||
.isEqualTo("unknown"),
|
|
||||||
() -> assertThat(revInfo.hostName)
|
|
||||||
.as("hostName should not be blank")
|
|
||||||
.isNotBlank());
|
|
||||||
});
|
|
||||||
|
|
||||||
QuarkusTransaction.requiringNew().run(() -> {
|
|
||||||
var entity = TestEntity2.findBySomething("something");
|
|
||||||
entity.something = "else";
|
|
||||||
});
|
|
||||||
|
|
||||||
QuarkusTransaction.requiringNew().run(() -> {
|
|
||||||
var entity = TestEntity2.findBySomething("else");
|
|
||||||
assertAll(() -> assertThat(entity.createdAt)
|
|
||||||
.as("createdAt should be before lastUpdatedAt")
|
|
||||||
.isBefore(entity.lastUpdatedAt));
|
|
||||||
|
|
||||||
var auditReader = AuditReaderFactory.get(entityManager);
|
|
||||||
|
|
||||||
var revisions = auditReader.getRevisions(TestEntity2.class, entity.id);
|
|
||||||
assertThat(revisions).hasSize(2);
|
|
||||||
|
|
||||||
Revision revInfo = Revision.findById(revisions.getLast());
|
|
||||||
assertThat(revInfo).isNotNull();
|
|
||||||
|
|
||||||
assertAll(
|
|
||||||
() -> assertThat(revInfo.timestamp)
|
|
||||||
.as("revision timestamp should not be equal to entity's createdAt")
|
|
||||||
.isNotEqualTo(entity.createdAt),
|
|
||||||
() -> assertThat(revInfo.timestamp)
|
|
||||||
.as("revision timestamp should be equal to entity's lastUpdatedAt")
|
|
||||||
.isEqualTo(entity.lastUpdatedAt),
|
|
||||||
() -> assertThat(revInfo.actor)
|
|
||||||
.as("actor should match expected value")
|
|
||||||
.isEqualTo("unknown"),
|
|
||||||
() -> assertThat(revInfo.traceId)
|
|
||||||
.as("traceId should match expected value")
|
|
||||||
.isEqualTo("00000000000000000000000000000000"),
|
|
||||||
() -> assertThat(revInfo.spanId)
|
|
||||||
.as("spanId should match expected value")
|
|
||||||
.isEqualTo("0000000000000000"),
|
|
||||||
() -> assertThat(revInfo.requestId)
|
|
||||||
.as("requestId should match expected value")
|
|
||||||
.isEqualTo("00000000-0000-0000-0000-000000000000"),
|
|
||||||
() -> assertThat(revInfo.clientIp)
|
|
||||||
.as("clientIp should match expected value")
|
|
||||||
.isEqualTo("unknown"),
|
|
||||||
() -> assertThat(revInfo.hostName)
|
|
||||||
.as("hostName should not be blank")
|
|
||||||
.isNotBlank());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import ch.phoenix.oss.quarkus.commons.tracing.TracingService;
|
|
||||||
import io.quarkus.narayana.jta.QuarkusTransaction;
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import jakarta.persistence.EntityManager;
|
|
||||||
import java.time.Instant;
|
|
||||||
import org.hibernate.envers.AuditReaderFactory;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class AuditedPanacheEntityTest {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
EntityManager entityManager;
|
|
||||||
|
|
||||||
@InjectSpy
|
|
||||||
TracingService tracingService;
|
|
||||||
|
|
||||||
@SuppressWarnings("Convert2MethodRef")
|
|
||||||
@BeforeEach
|
|
||||||
void setup() {
|
|
||||||
QuarkusTransaction.requiringNew().run(() -> TestEntity.deleteAll());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void persistAndUpdate() {
|
|
||||||
when(tracingService.getRequestId()).thenReturn("00000000-0000-0000-0000-000000000000");
|
|
||||||
when(tracingService.getSpanId()).thenReturn("0000000000000000");
|
|
||||||
when(tracingService.getTraceId()).thenReturn("00000000000000000000000000000000");
|
|
||||||
when(tracingService.getActor()).thenReturn("unknown");
|
|
||||||
when(tracingService.getClientIp()).thenReturn("unknown");
|
|
||||||
|
|
||||||
var now = Instant.now();
|
|
||||||
|
|
||||||
QuarkusTransaction.requiringNew().run(() -> {
|
|
||||||
var entity = new TestEntity("something");
|
|
||||||
entity.persist();
|
|
||||||
});
|
|
||||||
|
|
||||||
QuarkusTransaction.requiringNew().run(() -> {
|
|
||||||
var entity = TestEntity.findBySomething("something");
|
|
||||||
assertAll(
|
|
||||||
() -> assertThat(entity.createdAt)
|
|
||||||
.as("createdAt should be after or equal to expected value")
|
|
||||||
.isAfterOrEqualTo(now),
|
|
||||||
() -> assertThat(entity.lastUpdatedAt)
|
|
||||||
.as("lastUpdatedAt should be equal to createdAt")
|
|
||||||
.isEqualTo(entity.createdAt));
|
|
||||||
|
|
||||||
var auditReader = AuditReaderFactory.get(entityManager);
|
|
||||||
|
|
||||||
var revisions = auditReader.getRevisions(TestEntity.class, entity.id);
|
|
||||||
assertThat(revisions).hasSize(1);
|
|
||||||
|
|
||||||
var revInfo = entityManager.find(Revision.class, revisions.getFirst());
|
|
||||||
assertThat(revInfo).isNotNull();
|
|
||||||
|
|
||||||
assertAll(
|
|
||||||
() -> assertThat(revInfo.timestamp)
|
|
||||||
.as("revision timestamp should be equal to entity's createdAt timestamp")
|
|
||||||
.isEqualTo(entity.createdAt),
|
|
||||||
() -> assertThat(revInfo.actor)
|
|
||||||
.as("actor should match expected value")
|
|
||||||
.isEqualTo("unknown"),
|
|
||||||
() -> assertThat(revInfo.traceId)
|
|
||||||
.as("traceId should match expected value")
|
|
||||||
.isEqualTo("00000000000000000000000000000000"),
|
|
||||||
() -> assertThat(revInfo.spanId)
|
|
||||||
.as("spanId should match expected value")
|
|
||||||
.isEqualTo("0000000000000000"),
|
|
||||||
() -> assertThat(revInfo.requestId)
|
|
||||||
.as("requestId should match expected value")
|
|
||||||
.isEqualTo("00000000-0000-0000-0000-000000000000"),
|
|
||||||
() -> assertThat(revInfo.clientIp)
|
|
||||||
.as("clientIp should match expected value")
|
|
||||||
.isEqualTo("unknown"),
|
|
||||||
() -> assertThat(revInfo.hostName)
|
|
||||||
.as("hostName should not be blank")
|
|
||||||
.isNotBlank());
|
|
||||||
});
|
|
||||||
|
|
||||||
QuarkusTransaction.requiringNew().run(() -> {
|
|
||||||
var entity = TestEntity.findBySomething("something");
|
|
||||||
entity.something = "else";
|
|
||||||
});
|
|
||||||
|
|
||||||
QuarkusTransaction.requiringNew().run(() -> {
|
|
||||||
var entity = TestEntity.findBySomething("else");
|
|
||||||
assertAll(() -> assertThat(entity.createdAt)
|
|
||||||
.as("createdAt should be before lastUpdatedAt")
|
|
||||||
.isBefore(entity.lastUpdatedAt));
|
|
||||||
|
|
||||||
var auditReader = AuditReaderFactory.get(entityManager);
|
|
||||||
|
|
||||||
var revisions = auditReader.getRevisions(TestEntity.class, entity.id);
|
|
||||||
assertThat(revisions).hasSize(2);
|
|
||||||
|
|
||||||
Revision revInfo = Revision.findById(revisions.getLast());
|
|
||||||
assertThat(revInfo).isNotNull();
|
|
||||||
|
|
||||||
assertAll(
|
|
||||||
() -> assertThat(revInfo.timestamp)
|
|
||||||
.as("revision timestamp should not be equal to entity's createdAt")
|
|
||||||
.isNotEqualTo(entity.createdAt),
|
|
||||||
() -> assertThat(revInfo.timestamp)
|
|
||||||
.as("revision timestamp should be equal to entity's lastUpdatedAt")
|
|
||||||
.isEqualTo(entity.lastUpdatedAt),
|
|
||||||
() -> assertThat(revInfo.actor)
|
|
||||||
.as("actor should match expected value")
|
|
||||||
.isEqualTo("unknown"),
|
|
||||||
() -> assertThat(revInfo.traceId)
|
|
||||||
.as("traceId should match expected value")
|
|
||||||
.isEqualTo("00000000000000000000000000000000"),
|
|
||||||
() -> assertThat(revInfo.spanId)
|
|
||||||
.as("spanId should match expected value")
|
|
||||||
.isEqualTo("0000000000000000"),
|
|
||||||
() -> assertThat(revInfo.requestId)
|
|
||||||
.as("requestId should match expected value")
|
|
||||||
.isEqualTo("00000000-0000-0000-0000-000000000000"),
|
|
||||||
() -> assertThat(revInfo.clientIp)
|
|
||||||
.as("clientIp should match expected value")
|
|
||||||
.isEqualTo("unknown"),
|
|
||||||
() -> assertThat(revInfo.hostName)
|
|
||||||
.as("hostName should not be blank")
|
|
||||||
.isNotBlank());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
|
|
||||||
import static org.mockito.Mockito.mockStatic;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class DefaultRevisionContextProviderTest {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
DefaultRevisionContextProvider underTest;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getHostName() {
|
|
||||||
assertThat(underTest.getHostName()).isNotBlank();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getHostNameWhenUnknown() {
|
|
||||||
try (var inetMock = mockStatic(InetAddress.class)) {
|
|
||||||
inetMock.when(InetAddress::getLocalHost).thenThrow(new UnknownHostException("simulated failure"));
|
|
||||||
|
|
||||||
assertThat(underTest.getHostName()).isEqualTo("unknown");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class RevisionTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testEquals() {
|
|
||||||
var r1 = new Revision();
|
|
||||||
r1.rev = 1;
|
|
||||||
|
|
||||||
var r2 = new Revision();
|
|
||||||
r2.rev = 1;
|
|
||||||
|
|
||||||
var r3 = new Revision();
|
|
||||||
r3.rev = 2;
|
|
||||||
|
|
||||||
assertThat(r1)
|
|
||||||
.as("Revisions equality should should match expected value")
|
|
||||||
.isEqualTo(r1)
|
|
||||||
.isEqualTo(r2)
|
|
||||||
.isNotEqualTo(r3)
|
|
||||||
.isNotEqualTo(new Object());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testHashCode() {
|
|
||||||
var r1 = new Revision();
|
|
||||||
r1.rev = 123;
|
|
||||||
|
|
||||||
var r2 = new Revision();
|
|
||||||
r2.rev = 123;
|
|
||||||
|
|
||||||
var r3 = new Revision();
|
|
||||||
r3.rev = 2;
|
|
||||||
|
|
||||||
assertThat(r1.hashCode()).isEqualTo(123).isEqualTo(r2.hashCode()).isNotEqualTo(r3.hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testToString() {
|
|
||||||
var rev = new Revision();
|
|
||||||
rev.rev = 1;
|
|
||||||
|
|
||||||
assertThat(rev).as("Revision's toString should match expected value").hasToString("Revision{rev=1}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
import org.hibernate.envers.Audited;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Audited
|
|
||||||
@Table(name = "test_entity")
|
|
||||||
public class TestEntity extends AuditedPanacheEntity {
|
|
||||||
|
|
||||||
public String something;
|
|
||||||
|
|
||||||
public TestEntity() {}
|
|
||||||
|
|
||||||
public TestEntity(String something) {
|
|
||||||
this.something = something;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TestEntity findBySomething(String something) {
|
|
||||||
return find("something", something).singleResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.audit;
|
|
||||||
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.GeneratedValue;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
import org.hibernate.envers.Audited;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Audited
|
|
||||||
@Table(name = "test_entity")
|
|
||||||
public class TestEntity2 extends AuditedPanacheEntityBase {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue
|
|
||||||
public Long id;
|
|
||||||
|
|
||||||
public String something;
|
|
||||||
|
|
||||||
public TestEntity2() {}
|
|
||||||
|
|
||||||
public TestEntity2(String something) {
|
|
||||||
this.something = something;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TestEntity2 findBySomething(String something) {
|
|
||||||
return find("something", something).singleResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
quarkus:
|
|
||||||
flyway:
|
|
||||||
migrate-at-start: true
|
|
||||||
datasource:
|
|
||||||
db-kind: postgresql
|
|
||||||
hibernate-orm:
|
|
||||||
sql-load-script: no-file
|
|
||||||
schema-management:
|
|
||||||
strategy: none
|
|
||||||
log:
|
|
||||||
sql: true
|
|
||||||
bind-parameters: true
|
|
||||||
hibernate-envers:
|
|
||||||
audit-strategy: org.hibernate.envers.strategy.internal.ValidityAuditStrategy
|
|
||||||
revision-listener: ch.phoenix.oss.quarkus.commons.audit.AuditRevisionListener
|
|
||||||
security:
|
|
||||||
users:
|
|
||||||
embedded:
|
|
||||||
enabled: true
|
|
||||||
plain-text: true
|
|
||||||
users:
|
|
||||||
jon: doe
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
create sequence revinfo_seq start with 1 increment by 50;
|
|
||||||
create sequence test_entity_seq start with 1 increment by 50;
|
|
||||||
|
|
||||||
create table revinfo
|
|
||||||
(
|
|
||||||
rev bigint not null,
|
|
||||||
timestamp timestamp(6) with time zone not null,
|
|
||||||
actor varchar(255),
|
|
||||||
spanId varchar(255),
|
|
||||||
traceId varchar(255),
|
|
||||||
requestId varchar(255),
|
|
||||||
clientIp varchar(255),
|
|
||||||
hostName varchar(255),
|
|
||||||
primary key (rev)
|
|
||||||
);
|
|
||||||
|
|
||||||
create table test_entity
|
|
||||||
(
|
|
||||||
|
|
||||||
id bigint primary key not null,
|
|
||||||
something varchar(255),
|
|
||||||
createdAt timestamp(6) with time zone,
|
|
||||||
lastUpdatedAt timestamp(6) with time zone
|
|
||||||
);
|
|
||||||
|
|
||||||
create table test_entity_aud
|
|
||||||
(
|
|
||||||
revtype smallint,
|
|
||||||
rev bigint not null,
|
|
||||||
revend bigint,
|
|
||||||
id bigint not null,
|
|
||||||
something varchar(255),
|
|
||||||
primary key (rev, id)
|
|
||||||
);
|
|
||||||
|
|
||||||
alter table if exists test_entity_aud
|
|
||||||
add constraint fk_rev__revinfo_rev
|
|
||||||
foreign key (rev)
|
|
||||||
references revinfo;
|
|
||||||
|
|
||||||
|
|
||||||
alter table if exists test_entity_aud
|
|
||||||
add constraint fk_revend__revinfo_rev
|
|
||||||
foreign key (revend)
|
|
||||||
references revinfo;
|
|
||||||
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
CREATE OR REPLACE FUNCTION trg_test_entity_aud_apply_rev()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $func$
|
|
||||||
DECLARE
|
|
||||||
ts TIMESTAMP;
|
|
||||||
BEGIN
|
|
||||||
-- fetch the exact revision timestamp from revinfo
|
|
||||||
SELECT r.timestamp
|
|
||||||
INTO ts
|
|
||||||
FROM revinfo r
|
|
||||||
WHERE r.rev = NEW.rev;
|
|
||||||
|
|
||||||
-- only set created_at once, when still NULL
|
|
||||||
UPDATE test_entity
|
|
||||||
SET createdAt = ts
|
|
||||||
WHERE id = NEW.id
|
|
||||||
AND createdAt IS NULL;
|
|
||||||
|
|
||||||
-- always bump last_updated_at
|
|
||||||
UPDATE test_entity
|
|
||||||
SET lastUpdatedAt = ts
|
|
||||||
WHERE id = NEW.id;
|
|
||||||
|
|
||||||
RETURN NULL;
|
|
||||||
END;
|
|
||||||
$func$;
|
|
||||||
|
|
||||||
DROP TRIGGER IF EXISTS trg_test_entity_aud_after_insert
|
|
||||||
ON test_entity_aud;
|
|
||||||
|
|
||||||
CREATE TRIGGER trg_test_entity_aud_after_insert
|
|
||||||
AFTER INSERT ON test_entity_aud
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE FUNCTION trg_test_entity_aud_apply_rev();
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
-- 1) Create or replace the trigger function
|
|
||||||
CREATE OR REPLACE FUNCTION trg_test_entity_aud_apply_rev()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $func$
|
|
||||||
DECLARE
|
|
||||||
ts TIMESTAMP;
|
|
||||||
BEGIN
|
|
||||||
-- fetch the exact revision timestamp from revinfo
|
|
||||||
SELECT r.timestamp
|
|
||||||
INTO ts
|
|
||||||
FROM revinfo r
|
|
||||||
WHERE r.rev = NEW.rev;
|
|
||||||
|
|
||||||
-- only set created_at once, when still NULL
|
|
||||||
UPDATE test_entity
|
|
||||||
SET created_at = ts
|
|
||||||
WHERE id = NEW.id
|
|
||||||
AND created_at IS NULL;
|
|
||||||
|
|
||||||
-- always bump last_updated_at
|
|
||||||
UPDATE test_entity
|
|
||||||
SET last_updated_at = ts
|
|
||||||
WHERE id = NEW.id;
|
|
||||||
|
|
||||||
RETURN NULL; -- AFTER trigger ignores return value
|
|
||||||
END;
|
|
||||||
$func$;
|
|
||||||
|
|
||||||
-- 2) Drop any existing trigger, then attach the new one
|
|
||||||
DROP TRIGGER IF EXISTS trg_test_entity_aud_after_insert
|
|
||||||
ON test_entity_aud;
|
|
||||||
|
|
||||||
CREATE TRIGGER trg_test_entity_aud_after_insert
|
|
||||||
AFTER INSERT ON test_entity_aud
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE FUNCTION trg_test_entity_aud_apply_rev();
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
|
||||||
<artifactId>quarkus-commons</artifactId>
|
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>quarkus-client-logger</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-rest-client</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-config-yaml</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-rest-jackson</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
<version>${jacoco-plugin.version}</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>jacoco-check</id>
|
|
||||||
<goals>
|
|
||||||
<goal>check</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>test</phase>
|
|
||||||
<configuration>
|
|
||||||
<dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>
|
|
||||||
<rules>
|
|
||||||
<rule>
|
|
||||||
<element>BUNDLE</element>
|
|
||||||
<limits>
|
|
||||||
<limit>
|
|
||||||
<counter>INSTRUCTION</counter>
|
|
||||||
<value>COVEREDRATIO</value>
|
|
||||||
<minimum>0.92</minimum>
|
|
||||||
</limit>
|
|
||||||
</limits>
|
|
||||||
</rule>
|
|
||||||
</rules>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.client.logger;
|
|
||||||
|
|
||||||
import org.eclipse.microprofile.config.spi.Converter;
|
|
||||||
|
|
||||||
public class LowerCaseStringConverter implements Converter<String> {
|
|
||||||
@Override
|
|
||||||
public String convert(String value) throws IllegalArgumentException, NullPointerException {
|
|
||||||
return value.toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.client.logger;
|
|
||||||
|
|
||||||
import io.vertx.core.Handler;
|
|
||||||
import io.vertx.core.MultiMap;
|
|
||||||
import io.vertx.core.buffer.Buffer;
|
|
||||||
import io.vertx.core.http.HttpClientRequest;
|
|
||||||
import io.vertx.core.http.HttpClientResponse;
|
|
||||||
import jakarta.enterprise.context.Dependent;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
import org.jboss.resteasy.reactive.client.api.ClientLogger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is based on org.jboss.resteasy.reactive.client.logging.DefaultClientLogger,
|
|
||||||
* with the only change being that headers are redacted based on the Set provided
|
|
||||||
* by the configuration.
|
|
||||||
*/
|
|
||||||
@Dependent
|
|
||||||
public class RedactingClientLogger implements ClientLogger {
|
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(RedactingClientLogger.class);
|
|
||||||
|
|
||||||
private static final String REDACTED_VALUE = "*****";
|
|
||||||
|
|
||||||
private final Set<String> redactedHeaders;
|
|
||||||
|
|
||||||
private int bodySize;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public RedactingClientLogger(RedactingClientLoggerConfiguration configuration) {
|
|
||||||
this.redactedHeaders = configuration
|
|
||||||
.headers()
|
|
||||||
.redact()
|
|
||||||
.orElse(RedactingClientLoggerConfiguration.Headers.DEFAULT_REDACTED_HEADERS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBodySize(int bodySize) {
|
|
||||||
this.bodySize = bodySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logResponse(HttpClientResponse response, boolean redirect) {
|
|
||||||
if (!log.isDebugEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection Convert2Lambda
|
|
||||||
response.bodyHandler(new Handler<>() {
|
|
||||||
@Override
|
|
||||||
public void handle(Buffer body) {
|
|
||||||
log.debugf(
|
|
||||||
"%s: %s %s, Status[%d %s], Headers[%s], Body:\n%s",
|
|
||||||
redirect ? "Redirect" : "Response",
|
|
||||||
response.request().getMethod(),
|
|
||||||
response.request().absoluteURI(),
|
|
||||||
response.statusCode(),
|
|
||||||
response.statusMessage(),
|
|
||||||
asString(response.headers()),
|
|
||||||
bodyToString(body));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void logRequest(HttpClientRequest request, Buffer body, boolean omitBody) {
|
|
||||||
if (!log.isDebugEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (omitBody) {
|
|
||||||
log.debugf(
|
|
||||||
"Request: %s %s Headers[%s], Body omitted",
|
|
||||||
request.getMethod(), request.absoluteURI(), asString(request.headers()));
|
|
||||||
} else if (body == null || body.length() == 0) {
|
|
||||||
log.debugf(
|
|
||||||
"Request: %s %s Headers[%s], Empty body",
|
|
||||||
request.getMethod(), request.absoluteURI(), asString(request.headers()));
|
|
||||||
} else {
|
|
||||||
log.debugf(
|
|
||||||
"Request: %s %s Headers[%s], Body:\n%s",
|
|
||||||
request.getMethod(), request.absoluteURI(), asString(request.headers()), bodyToString(body));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String bodyToString(Buffer body) {
|
|
||||||
if (body == null) {
|
|
||||||
return "";
|
|
||||||
} else if (bodySize <= 0) {
|
|
||||||
return body.toString();
|
|
||||||
} else {
|
|
||||||
String bodyAsString = body.toString();
|
|
||||||
return bodyAsString.substring(0, Math.min(bodySize, bodyAsString.length()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String asString(MultiMap headers) {
|
|
||||||
if (headers.isEmpty()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
StringBuilder sb = new StringBuilder((headers.size() * (6 + 1 + 6))
|
|
||||||
+ (headers.size() - 1)); // this is a very rough estimate of a result like 'key1=value1 key2=value2'
|
|
||||||
boolean isFirst = true;
|
|
||||||
for (Map.Entry<String, String> entry : headers) {
|
|
||||||
if (isFirst) {
|
|
||||||
isFirst = false;
|
|
||||||
} else {
|
|
||||||
sb.append(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = entry.getKey();
|
|
||||||
var value = redactedHeaders.contains(key.toLowerCase()) ? REDACTED_VALUE : entry.getValue();
|
|
||||||
|
|
||||||
sb.append(key).append('=').append(value);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.client.logger;
|
|
||||||
|
|
||||||
import io.smallrye.config.ConfigMapping;
|
|
||||||
import io.smallrye.config.WithConverter;
|
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@ConfigMapping(prefix = "phoenix-oss.client-logger")
|
|
||||||
public interface RedactingClientLoggerConfiguration {
|
|
||||||
|
|
||||||
Headers headers();
|
|
||||||
|
|
||||||
interface Headers {
|
|
||||||
|
|
||||||
Set<String> DEFAULT_REDACTED_HEADERS = Set.of(HttpHeaders.AUTHORIZATION.toLowerCase());
|
|
||||||
|
|
||||||
Optional<Set<@WithConverter(LowerCaseStringConverter.class) String>> redact();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.client.logger;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTestProfile;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class InfoLevelProfile implements QuarkusTestProfile {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, String> getConfigOverrides() {
|
|
||||||
return Map.of("quarkus.log.category.\"ch.phoenix.oss.quarkus.commons.client.logger\".level", "INFO");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.client.logger;
|
|
||||||
|
|
||||||
import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import io.quarkus.test.junit.TestProfile;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Optional;
|
|
||||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
@TestProfile(InfoLevelProfile.class)
|
|
||||||
class InfoLevelTest {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@RestClient
|
|
||||||
TestClient injectedClient;
|
|
||||||
|
|
||||||
TestClient builtClient = QuarkusRestClientBuilder.newBuilder()
|
|
||||||
.clientLogger(new RedactingClientLogger(() -> Optional::empty))
|
|
||||||
.baseUri(URI.create("http://localhost:8087"))
|
|
||||||
.build(TestClient.class);
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getWithInjectedClient() {
|
|
||||||
injectedClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getWithBuiltClientAndEmptyConfig() {
|
|
||||||
builtClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void postWithInjectedClient() {
|
|
||||||
injectedClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted", "body");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void postWithBuiltClientAndEmptyConfig() {
|
|
||||||
builtClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.client.logger;
|
|
||||||
|
|
||||||
import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Optional;
|
|
||||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class RedactingClientLoggerTest {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@RestClient
|
|
||||||
TestClient injectedClient;
|
|
||||||
|
|
||||||
TestClient builtClient = QuarkusRestClientBuilder.newBuilder()
|
|
||||||
.clientLogger(new RedactingClientLogger(() -> Optional::empty))
|
|
||||||
.baseUri(URI.create("http://localhost:8087"))
|
|
||||||
.build(TestClient.class);
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getWithInjectedClient() {
|
|
||||||
injectedClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getWithBuiltClientAndEmptyConfig() {
|
|
||||||
builtClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void postWithInjectedClient() {
|
|
||||||
injectedClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted", "body");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void postWithInjectedClientAndNullBody() {
|
|
||||||
injectedClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void postWithBuiltClientAndEmptyConfig() {
|
|
||||||
builtClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.client.logger;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTestProfile;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class ScopeNoneProfile implements QuarkusTestProfile {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, String> getConfigOverrides() {
|
|
||||||
return Map.of("quarkus.rest-client.logging.scope", "none");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.client.logger;
|
|
||||||
|
|
||||||
import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import io.quarkus.test.junit.TestProfile;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Optional;
|
|
||||||
import org.eclipse.microprofile.rest.client.inject.RestClient;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
@TestProfile(ScopeNoneProfile.class)
|
|
||||||
class ScopeNoneTest {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@RestClient
|
|
||||||
TestClient injectedClient;
|
|
||||||
|
|
||||||
TestClient builtClient = QuarkusRestClientBuilder.newBuilder()
|
|
||||||
.clientLogger(new RedactingClientLogger(() -> Optional::empty))
|
|
||||||
.baseUri(URI.create("http://localhost:8087"))
|
|
||||||
.build(TestClient.class);
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getWithInjectedClient() {
|
|
||||||
injectedClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getWithBuiltClientAndEmptyConfig() {
|
|
||||||
builtClient.get("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void postWithInjectedClient() {
|
|
||||||
injectedClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "also redacted", "body");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void postWithBuiltClientAndEmptyConfig() {
|
|
||||||
builtClient.post("this will be redacted", "5c0d8e45-e402-4b71-8f84-24cc0cfd7eec", "not redacted", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.client.logger;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.*;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
|
||||||
|
|
||||||
@SuppressWarnings("UastIncorrectHttpHeaderInspection")
|
|
||||||
@RegisterRestClient(configKey = "test")
|
|
||||||
public interface TestClient {
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/")
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
String get(
|
|
||||||
@HeaderParam("Authorization") String authorization,
|
|
||||||
@HeaderParam("X-Request-ID") String requestId,
|
|
||||||
@HeaderParam("X-Something-Else") String somethingElse);
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("/")
|
|
||||||
@Consumes(MediaType.TEXT_PLAIN)
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
String post(
|
|
||||||
@HeaderParam("Authorization") String authorization,
|
|
||||||
@HeaderParam("X-Request-ID") String requestId,
|
|
||||||
@HeaderParam("X-Something-Else") String somethingElse,
|
|
||||||
String body);
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.client.logger;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.*;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
@Path("/")
|
|
||||||
public class TestResource {
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public String get() {
|
|
||||||
return "get";
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Consumes(MediaType.TEXT_PLAIN)
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public String post(String body) {
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
quarkus:
|
|
||||||
http:
|
|
||||||
test-port: 8087
|
|
||||||
rest-client:
|
|
||||||
logging:
|
|
||||||
scope: request-response
|
|
||||||
body-limit: 10000
|
|
||||||
test:
|
|
||||||
url: http://localhost:${quarkus.http.test-port}
|
|
||||||
log:
|
|
||||||
category:
|
|
||||||
"ch.phoenix.oss.quarkus.commons.client.logger":
|
|
||||||
level: DEBUG
|
|
||||||
|
|
||||||
phoenix-oss:
|
|
||||||
client-logger:
|
|
||||||
headers:
|
|
||||||
redact:
|
|
||||||
- AUTHORIZATION
|
|
||||||
- X-SOMETHING-ELSE
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenixtechnologies.quarkus</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
<version>1.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-clock-service</artifactId>
|
<artifactId>quarkus-clock-service</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.clock;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
|
|
||||||
public interface ClockService {
|
|
||||||
|
|
||||||
Instant instant();
|
|
||||||
|
|
||||||
long currentTimeMillis();
|
|
||||||
|
|
||||||
LocalDate localDate();
|
|
||||||
|
|
||||||
LocalDateTime localDateTime();
|
|
||||||
|
|
||||||
ZonedDateTime zonedDateTime();
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.clock;
|
package ch.phoenixtechnologies.quarkus.commons.clock;
|
||||||
|
|
||||||
import io.quarkus.arc.DefaultBean;
|
import io.quarkus.arc.DefaultBean;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package ch.phoenixtechnologies.quarkus.commons.clock;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
public interface ClockService {
|
||||||
|
|
||||||
|
Instant instant();
|
||||||
|
|
||||||
|
long currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.clock;
|
package ch.phoenixtechnologies.quarkus.commons.clock;
|
||||||
|
|
||||||
import io.quarkus.arc.DefaultBean;
|
import io.quarkus.arc.DefaultBean;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import java.time.*;
|
import java.time.Clock;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
@DefaultBean
|
@DefaultBean
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
|
|
@ -23,19 +24,4 @@ class ClockServiceImpl implements ClockService {
|
||||||
public long currentTimeMillis() {
|
public long currentTimeMillis() {
|
||||||
return clock.millis();
|
return clock.millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalDate localDate() {
|
|
||||||
return LocalDate.now(clock);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalDateTime localDateTime() {
|
|
||||||
return LocalDateTime.now(clock);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ZonedDateTime zonedDateTime() {
|
|
||||||
return ZonedDateTime.now(clock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.clock;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import io.quarkus.test.InjectMock;
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import java.time.*;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class ClockServiceImplTest {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ClockService clockService;
|
|
||||||
|
|
||||||
@InjectMock
|
|
||||||
Clock clock;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void instant() {
|
|
||||||
var expected = Instant.ofEpochMilli(1729280640915L);
|
|
||||||
when(clock.instant()).thenReturn(expected);
|
|
||||||
|
|
||||||
assertThat(clockService.instant())
|
|
||||||
.as("Instant should match expected value")
|
|
||||||
.isEqualTo(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void currentTimeMillis() {
|
|
||||||
var expected = 1729280640915L;
|
|
||||||
when(clock.millis()).thenReturn(expected);
|
|
||||||
|
|
||||||
assertThat(clockService.currentTimeMillis())
|
|
||||||
.as("Instant should match expected value")
|
|
||||||
.isEqualTo(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void localDate() {
|
|
||||||
var instant = Instant.ofEpochMilli(1729280640915L);
|
|
||||||
when(clock.instant()).thenReturn(instant);
|
|
||||||
when(clock.getZone()).thenReturn(ZoneId.of("UTC"));
|
|
||||||
assertThat(clockService.localDate())
|
|
||||||
.as("LocalDate should match expected value")
|
|
||||||
.isEqualTo(LocalDate.parse("2024-10-18"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void localDateTime() {
|
|
||||||
var instant = Instant.ofEpochMilli(1729280640915L);
|
|
||||||
when(clock.instant()).thenReturn(instant);
|
|
||||||
when(clock.getZone()).thenReturn(ZoneId.of("UTC"));
|
|
||||||
assertThat(clockService.localDateTime())
|
|
||||||
.as("LocalDateTime should match expected value")
|
|
||||||
.isEqualTo(LocalDateTime.parse("2024-10-18T19:44:00.915"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void zonedDateTime() {
|
|
||||||
var instant = Instant.ofEpochMilli(1729280640915L);
|
|
||||||
when(clock.instant()).thenReturn(instant);
|
|
||||||
when(clock.getZone()).thenReturn(ZoneId.of("UTC"));
|
|
||||||
assertThat(clockService.zonedDateTime())
|
|
||||||
.as("ZonedDateTime should match expected value")
|
|
||||||
.isEqualTo(ZonedDateTime.parse("2024-10-18T19:44:00.915Z[UTC]"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.clock;
|
package ch.phoenixtechnologies.quarkus.commons.clock;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package ch.phoenixtechnologies.quarkus.commons.clock;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import io.quarkus.test.InjectMock;
|
||||||
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
|
import jakarta.inject.Inject;
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.time.Instant;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@QuarkusTest
|
||||||
|
class ClockServiceImplTest {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ClockService clockService;
|
||||||
|
|
||||||
|
@InjectMock
|
||||||
|
Clock clock;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void instant() {
|
||||||
|
var expected = Instant.ofEpochMilli(1729280640915L);
|
||||||
|
when(clock.instant()).thenReturn(expected);
|
||||||
|
|
||||||
|
assertThat(clockService.instant())
|
||||||
|
.as("Instant should match expected value")
|
||||||
|
.isEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void currentTimeMillis() {
|
||||||
|
var expected = 1729280640915L;
|
||||||
|
when(clock.millis()).thenReturn(expected);
|
||||||
|
|
||||||
|
assertThat(clockService.currentTimeMillis())
|
||||||
|
.as("Instant should match expected value")
|
||||||
|
.isEqualTo(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenixtechnologies.quarkus</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
<version>1.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-json-service</artifactId>
|
<artifactId>quarkus-json-service</artifactId>
|
||||||
|
|
@ -40,7 +42,7 @@
|
||||||
<limit>
|
<limit>
|
||||||
<counter>INSTRUCTION</counter>
|
<counter>INSTRUCTION</counter>
|
||||||
<value>COVEREDRATIO</value>
|
<value>COVEREDRATIO</value>
|
||||||
<minimum>1</minimum>
|
<minimum>0.83</minimum>
|
||||||
</limit>
|
</limit>
|
||||||
</limits>
|
</limits>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.json;
|
package ch.phoenixtechnologies.quarkus.commons.json;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.json;
|
package ch.phoenixtechnologies.quarkus.commons.json;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.json;
|
package ch.phoenixtechnologies.quarkus.commons.json;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
@ -17,15 +17,6 @@ class JsonServiceImplTest {
|
||||||
|
|
||||||
record TestRecord(String name, int age) {}
|
record TestRecord(String name, int age) {}
|
||||||
|
|
||||||
static class CircularReference {
|
|
||||||
CircularReference reference;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "CircularReference";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
JsonServiceImpl jsonService;
|
JsonServiceImpl jsonService;
|
||||||
|
|
||||||
|
|
@ -38,17 +29,6 @@ class JsonServiceImplTest {
|
||||||
assertThat(actual).as("Json should match expected value").isEqualTo(expected);
|
assertThat(actual).as("Json should match expected value").isEqualTo(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void toJsonWithCircularReference() {
|
|
||||||
var circularReference = new CircularReference();
|
|
||||||
circularReference.reference = circularReference;
|
|
||||||
|
|
||||||
assertThatThrownBy(() -> jsonService.toJson(circularReference))
|
|
||||||
.as("Should throw IllegalArgumentException when input is invalid")
|
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
|
||||||
.hasMessage("Unable to write object as json String: CircularReference");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void fromJsonWithClass() {
|
void fromJsonWithClass() {
|
||||||
var json = "{\"name\":\"John Doe\",\"age\":30}";
|
var json = "{\"name\":\"John Doe\",\"age\":30}";
|
||||||
|
|
@ -67,7 +47,7 @@ class JsonServiceImplTest {
|
||||||
.as("Should throw IllegalArgumentException when input is invalid")
|
.as("Should throw IllegalArgumentException when input is invalid")
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessage(
|
.hasMessage(
|
||||||
"Unable to read object of class [ch.phoenix.oss.quarkus.commons.json.JsonServiceImplTest$TestRecord] from json String: {\"name\":\"John Doe\",\"age\":\"30\"");
|
"Unable to read object of class [ch.phoenixtechnologies.quarkus.commons.json.JsonServiceImplTest$TestRecord] from json String: {\"name\":\"John Doe\",\"age\":\"30\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -90,6 +70,6 @@ class JsonServiceImplTest {
|
||||||
.as("Should throw IllegalArgumentException when input is invalid")
|
.as("Should throw IllegalArgumentException when input is invalid")
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
.isInstanceOf(IllegalArgumentException.class)
|
||||||
.hasMessage(
|
.hasMessage(
|
||||||
"Unable to read object of type [java.util.List<ch.phoenix.oss.quarkus.commons.json.JsonServiceImplTest$TestRecord>] from json String: {\"name\":\"John Doe\",\"age\":30},{\"name\":\"Jane Doe\",\"age\":\"25\"}");
|
"Unable to read object of type [java.util.List<ch.phoenixtechnologies.quarkus.commons.json.JsonServiceImplTest$TestRecord>] from json String: {\"name\":\"John Doe\",\"age\":30},{\"name\":\"Jane Doe\",\"age\":\"25\"}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenixtechnologies.quarkus</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
<version>1.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-message-digest-service</artifactId>
|
<artifactId>quarkus-message-digest-service</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.digest;
|
package ch.phoenixtechnologies.quarkus.commons.digest;
|
||||||
|
|
||||||
import io.smallrye.config.ConfigMapping;
|
import io.smallrye.config.ConfigMapping;
|
||||||
import io.smallrye.config.WithDefault;
|
import io.smallrye.config.WithDefault;
|
||||||
|
|
||||||
@ConfigMapping(prefix = "phoenix-oss.message-digest")
|
@ConfigMapping(prefix = "phoenix.message-digest")
|
||||||
public interface MessageDigestConfiguration {
|
public interface MessageDigestConfiguration {
|
||||||
|
|
||||||
@WithDefault("SHA-256")
|
@WithDefault("SHA-256")
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.digest;
|
package ch.phoenixtechnologies.quarkus.commons.digest;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.digest;
|
package ch.phoenixtechnologies.quarkus.commons.digest;
|
||||||
|
|
||||||
import io.quarkus.arc.DefaultBean;
|
import io.quarkus.arc.DefaultBean;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.digest;
|
package ch.phoenixtechnologies.quarkus.commons.digest;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenixtechnologies.quarkus</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
<version>1.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-random-number-generator</artifactId>
|
<artifactId>quarkus-random-number-generator</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.random;
|
package ch.phoenixtechnologies.quarkus.commons.random;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.random;
|
package ch.phoenixtechnologies.quarkus.commons.random;
|
||||||
|
|
||||||
import io.quarkus.arc.DefaultBean;
|
import io.quarkus.arc.DefaultBean;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.random;
|
package ch.phoenixtechnologies.quarkus.commons.random;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.random;
|
package ch.phoenixtechnologies.quarkus.commons.random;
|
||||||
|
|
||||||
import io.quarkus.arc.DefaultBean;
|
import io.quarkus.arc.DefaultBean;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.random;
|
package ch.phoenixtechnologies.quarkus.commons.random;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.random;
|
package ch.phoenixtechnologies.quarkus.commons.random;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
|
||||||
<artifactId>quarkus-commons</artifactId>
|
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>quarkus-table-generator</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
<version>${jacoco-plugin.version}</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>jacoco-check</id>
|
|
||||||
<goals>
|
|
||||||
<goal>check</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>test</phase>
|
|
||||||
<configuration>
|
|
||||||
<dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>
|
|
||||||
<rules>
|
|
||||||
<rule>
|
|
||||||
<element>BUNDLE</element>
|
|
||||||
<limits>
|
|
||||||
<limit>
|
|
||||||
<counter>INSTRUCTION</counter>
|
|
||||||
<value>COVEREDRATIO</value>
|
|
||||||
<minimum>1</minimum>
|
|
||||||
</limit>
|
|
||||||
</limits>
|
|
||||||
</rule>
|
|
||||||
</rules>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.table;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface TableGenerator {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a Jira-style table from a list of records.
|
|
||||||
* Refer to <a href="https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all">notation</a>
|
|
||||||
*
|
|
||||||
* @param records List of records to generate the table from
|
|
||||||
* @param headers Optional custom headers, list must match record field order and count.
|
|
||||||
* If null, field names are used as headers.
|
|
||||||
* @return String representing the records as a table in Jira notation
|
|
||||||
* @throws IllegalArgumentException If records are null or empty, if the type is not a Record.
|
|
||||||
* Also throws if custom headers are provided but the size does not match the record's field count
|
|
||||||
* @throws IllegalStateException if an error occurs while invoking a record's accessor
|
|
||||||
*/
|
|
||||||
String generateJiraTable(List<?> records, List<String> headers);
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.table;
|
|
||||||
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
import java.lang.reflect.RecordComponent;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@ApplicationScoped
|
|
||||||
class TableGeneratorImpl implements TableGenerator {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String generateJiraTable(List<?> records, List<String> headers) {
|
|
||||||
if (records == null || records.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("Records can't be null or empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<?> recordClass = records.getFirst().getClass();
|
|
||||||
if (!recordClass.isRecord()) {
|
|
||||||
throw new IllegalArgumentException("Only records are supported at the moment");
|
|
||||||
}
|
|
||||||
|
|
||||||
var components = recordClass.getRecordComponents();
|
|
||||||
var columns = components.length;
|
|
||||||
|
|
||||||
List<String> headerRow;
|
|
||||||
if (headers == null) {
|
|
||||||
headerRow = Arrays.stream(components).map(RecordComponent::getName).toList();
|
|
||||||
} else if (headers.size() == columns) {
|
|
||||||
headerRow = headers;
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Custom header count [%s] does not match actual number of columns [%s]"
|
|
||||||
.formatted(headers.size(), columns));
|
|
||||||
}
|
|
||||||
|
|
||||||
var dataRows = new ArrayList<List<String>>();
|
|
||||||
for (var record : records) {
|
|
||||||
var row = new ArrayList<String>(columns);
|
|
||||||
for (var component : components) {
|
|
||||||
try {
|
|
||||||
var val = component.getAccessor().invoke(record);
|
|
||||||
row.add(val == null ? "" : val.toString());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Unable to invoke accessor for component [%s] of class [%s]"
|
|
||||||
.formatted(component.getName(), recordClass.getName()),
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dataRows.add(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
var sb = new StringBuilder("||");
|
|
||||||
headerRow.forEach(header -> sb.append(" ").append(header).append(" ||"));
|
|
||||||
sb.append("\n");
|
|
||||||
for (var row : dataRows) {
|
|
||||||
sb.append("|");
|
|
||||||
for (String cell : row) {
|
|
||||||
sb.append(" ").append(cell).append(" |");
|
|
||||||
}
|
|
||||||
sb.append("\n");
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.table;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
||||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class TableGeneratorImplTest {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
TableGenerator tableGenerator;
|
|
||||||
|
|
||||||
public record TestDto(String what, Instant instant, Double number) {}
|
|
||||||
|
|
||||||
static Stream<Arguments> generateJiraTable() {
|
|
||||||
var dtos = List.of(
|
|
||||||
new TestDto("What", Instant.ofEpochMilli(123).truncatedTo(ChronoUnit.MILLIS), 2.0d),
|
|
||||||
new TestDto("SupDog", Instant.ofEpochMilli(124313425).truncatedTo(ChronoUnit.MILLIS), null),
|
|
||||||
new TestDto(null, null, 214534.134d),
|
|
||||||
new TestDto(null, null, null));
|
|
||||||
return Stream.of(
|
|
||||||
arguments(
|
|
||||||
dtos,
|
|
||||||
List.of("What", "Instant", "SomeNumber"),
|
|
||||||
"""
|
|
||||||
|| What || Instant || SomeNumber ||
|
|
||||||
| What | 1970-01-01T00:00:00.123Z | 2.0 |
|
|
||||||
| SupDog | 1970-01-02T10:31:53.425Z | |
|
|
||||||
| | | 214534.134 |
|
|
||||||
| | | |
|
|
||||||
"""),
|
|
||||||
arguments(
|
|
||||||
dtos,
|
|
||||||
null,
|
|
||||||
"""
|
|
||||||
|| what || instant || number ||
|
|
||||||
| What | 1970-01-01T00:00:00.123Z | 2.0 |
|
|
||||||
| SupDog | 1970-01-02T10:31:53.425Z | |
|
|
||||||
| | | 214534.134 |
|
|
||||||
| | | |
|
|
||||||
"""));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MethodSource
|
|
||||||
@ParameterizedTest
|
|
||||||
void generateJiraTable(List<TestDto> testDtos, List<String> headers, String expected) {
|
|
||||||
var actual = tableGenerator.generateJiraTable(testDtos, headers);
|
|
||||||
assertThat(actual).as("Generated table should match expected value").isEqualTo(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void generateJiraTableWhenRecordsIsNull() {
|
|
||||||
assertThatThrownBy(() -> tableGenerator.generateJiraTable(null, null))
|
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
|
||||||
.hasMessageContaining("Records can't be null or empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void generateJiraTableWhenRecordsIsEmpty() {
|
|
||||||
assertThatThrownBy(() -> tableGenerator.generateJiraTable(List.of(), null))
|
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
|
||||||
.hasMessageContaining("Records can't be null or empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void generateJiraTableWhenNotRecord() {
|
|
||||||
var notARecord = List.of("just a string");
|
|
||||||
assertThatThrownBy(() -> tableGenerator.generateJiraTable(notARecord, null))
|
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
|
||||||
.hasMessageContaining("Only records are supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void generateJiraTableWhenCustomHeaderWrongSize() {
|
|
||||||
var dtos = List.of(new TestDto("foo", Instant.now(), 1.2d));
|
|
||||||
assertThatThrownBy(() -> tableGenerator.generateJiraTable(dtos, List.of("One", "Two")))
|
|
||||||
.isInstanceOf(IllegalArgumentException.class)
|
|
||||||
.hasMessageContaining("Custom header count [2] does not match actual number of columns [3]");
|
|
||||||
}
|
|
||||||
|
|
||||||
public record EvilRecord(String value) {
|
|
||||||
@Override
|
|
||||||
public String value() {
|
|
||||||
throw new RuntimeException("Accessor failure!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void generateJiraTableWhenAccessorThrows() {
|
|
||||||
var list = List.of(new EvilRecord("hi"));
|
|
||||||
assertThatThrownBy(() -> tableGenerator.generateJiraTable(list, null))
|
|
||||||
.isInstanceOf(IllegalStateException.class)
|
|
||||||
.hasMessageContaining("Unable to invoke accessor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
|
||||||
<artifactId>quarkus-commons</artifactId>
|
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>quarkus-tracing-service</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-rest-jackson</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-opentelemetry</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-security</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-elytron-security-properties-file</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.quarkus</groupId>
|
|
||||||
<artifactId>quarkus-config-yaml</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.rest-assured</groupId>
|
|
||||||
<artifactId>rest-assured</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.jacoco</groupId>
|
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
|
||||||
<version>${jacoco-plugin.version}</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>jacoco-check</id>
|
|
||||||
<goals>
|
|
||||||
<goal>check</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>test</phase>
|
|
||||||
<configuration>
|
|
||||||
<dataFile>${project.build.directory}/jacoco-quarkus.exec</dataFile>
|
|
||||||
<rules>
|
|
||||||
<rule>
|
|
||||||
<element>BUNDLE</element>
|
|
||||||
<limits>
|
|
||||||
<limit>
|
|
||||||
<counter>INSTRUCTION</counter>
|
|
||||||
<value>COVEREDRATIO</value>
|
|
||||||
<minimum>0.95</minimum>
|
|
||||||
</limit>
|
|
||||||
</limits>
|
|
||||||
</rule>
|
|
||||||
</rules>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import org.eclipse.microprofile.config.spi.Converter;
|
|
||||||
|
|
||||||
public class LowerCaseStringConverter implements Converter<String> {
|
|
||||||
@Override
|
|
||||||
public String convert(String value) throws IllegalArgumentException, NullPointerException {
|
|
||||||
return value.toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import io.smallrye.config.ConfigMapping;
|
|
||||||
import io.smallrye.config.WithConverter;
|
|
||||||
import io.smallrye.config.WithDefault;
|
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@ConfigMapping(prefix = "phoenix-oss.tracing")
|
|
||||||
public interface TracingConfiguration {
|
|
||||||
|
|
||||||
RequestFilterConfiguration requestFilter();
|
|
||||||
|
|
||||||
interface RequestFilterConfiguration {
|
|
||||||
|
|
||||||
Headers headers();
|
|
||||||
|
|
||||||
interface Headers {
|
|
||||||
|
|
||||||
Set<String> DEFAULT_REDACTED = Set.of(HttpHeaders.AUTHORIZATION.toLowerCase());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional set of headers to redact when tracing. By default, redacts
|
|
||||||
* the 'Authorization' header.
|
|
||||||
*
|
|
||||||
* @return the set of headers to be redacted
|
|
||||||
*/
|
|
||||||
Optional<Set<@WithConverter(LowerCaseStringConverter.class) String>> redact();
|
|
||||||
}
|
|
||||||
|
|
||||||
Path path();
|
|
||||||
|
|
||||||
interface Path {
|
|
||||||
|
|
||||||
@WithDefault("false")
|
|
||||||
boolean includeRaw();
|
|
||||||
}
|
|
||||||
|
|
||||||
Query query();
|
|
||||||
|
|
||||||
interface Query {
|
|
||||||
|
|
||||||
Set<String> DEFAULT_REDACTED = Set.of("access_token", "refresh_token", "apikey");
|
|
||||||
|
|
||||||
@WithDefault("false")
|
|
||||||
boolean includeRaw();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional set of query params to redact when tracing. By default, redacts
|
|
||||||
* the following params: 'access_token', 'refresh_token' and 'apikey'.
|
|
||||||
*
|
|
||||||
* @return the set of query params to be redacted
|
|
||||||
*/
|
|
||||||
Optional<Set<@WithConverter(LowerCaseStringConverter.class) String>> redact();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
public class TracingConstants {
|
|
||||||
|
|
||||||
public static final String ACTOR = "actor";
|
|
||||||
public static final String ANONYMOUS = "anonymous";
|
|
||||||
public static final String REQUEST_ROUTE = "request.route";
|
|
||||||
public static final String REQUEST_METHOD = "request.method";
|
|
||||||
public static final String REQUEST_PATH_RAW = "request.path.raw";
|
|
||||||
public static final String REQUEST_PATH_PARAMS = "request.path.params";
|
|
||||||
public static final String REQUEST_QUERY_RAW = "request.query.raw";
|
|
||||||
public static final String REQUEST_QUERY_PARAMS = "request.query.params";
|
|
||||||
public static final String REQUEST_HEADERS = "request.headers";
|
|
||||||
public static final String REQUEST_CLIENT_IP = "request.client.ip";
|
|
||||||
public static final String SCHEDULER_JOB_NAME = "scheduler.job.name";
|
|
||||||
|
|
||||||
private TracingConstants() {}
|
|
||||||
}
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import io.quarkus.arc.Unremovable;
|
|
||||||
import io.quarkus.logging.Log;
|
|
||||||
import io.quarkus.security.identity.SecurityIdentity;
|
|
||||||
import io.vertx.ext.web.RoutingContext;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
|
||||||
import jakarta.ws.rs.container.ResourceInfo;
|
|
||||||
import java.util.List;
|
|
||||||
import org.jboss.resteasy.reactive.server.ServerRequestFilter;
|
|
||||||
|
|
||||||
@Unremovable
|
|
||||||
public class TracingRequestFilter {
|
|
||||||
|
|
||||||
private static final String REDACTED_VALUE = "********";
|
|
||||||
|
|
||||||
private final RoutingContext routingContext;
|
|
||||||
private final TracingService tracingService;
|
|
||||||
private final SecurityIdentity securityIdentity;
|
|
||||||
private final TracingConfiguration configuration;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TracingRequestFilter(
|
|
||||||
RoutingContext routingContext,
|
|
||||||
TracingService tracingService,
|
|
||||||
SecurityIdentity securityIdentity,
|
|
||||||
TracingConfiguration configuration) {
|
|
||||||
this.routingContext = routingContext;
|
|
||||||
this.tracingService = tracingService;
|
|
||||||
this.securityIdentity = securityIdentity;
|
|
||||||
this.configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ServerRequestFilter
|
|
||||||
public void filter(ContainerRequestContext requestContext, ResourceInfo resourceInfo) {
|
|
||||||
if (securityIdentity.isAnonymous()) {
|
|
||||||
tracingService.trace(TracingConstants.ACTOR, TracingConstants.ANONYMOUS);
|
|
||||||
} else {
|
|
||||||
tracingService.trace(
|
|
||||||
TracingConstants.ACTOR, securityIdentity.getPrincipal().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
var method = requestContext.getMethod();
|
|
||||||
tracingService.trace(TracingConstants.REQUEST_METHOD, method);
|
|
||||||
|
|
||||||
var routePattern = getRoutePattern(resourceInfo);
|
|
||||||
tracingService.trace(TracingConstants.REQUEST_ROUTE, routePattern);
|
|
||||||
|
|
||||||
var uriInfo = requestContext.getUriInfo();
|
|
||||||
uriInfo.getPathParameters()
|
|
||||||
.forEach((key, value) ->
|
|
||||||
tracingService.trace(TracingConstants.REQUEST_PATH_PARAMS + '.' + key, joinStrings(value)));
|
|
||||||
|
|
||||||
if (configuration.requestFilter().path().includeRaw()) {
|
|
||||||
tracingService.trace(
|
|
||||||
TracingConstants.REQUEST_PATH_RAW, uriInfo.getAbsolutePath().getRawPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
var redactedHeaders = configuration
|
|
||||||
.requestFilter()
|
|
||||||
.headers()
|
|
||||||
.redact()
|
|
||||||
.orElse(TracingConfiguration.RequestFilterConfiguration.Headers.DEFAULT_REDACTED);
|
|
||||||
|
|
||||||
requestContext.getHeaders().forEach((key, value) -> {
|
|
||||||
var lowerCaseKey = key.toLowerCase();
|
|
||||||
var property = TracingConstants.REQUEST_HEADERS + '.' + lowerCaseKey;
|
|
||||||
if (redactedHeaders.contains(lowerCaseKey)) {
|
|
||||||
tracingService.trace(property, REDACTED_VALUE);
|
|
||||||
} else {
|
|
||||||
tracingService.trace(property, joinStrings(value));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var redactedQueryParams = configuration
|
|
||||||
.requestFilter()
|
|
||||||
.query()
|
|
||||||
.redact()
|
|
||||||
.orElse(TracingConfiguration.RequestFilterConfiguration.Query.DEFAULT_REDACTED);
|
|
||||||
|
|
||||||
uriInfo.getQueryParameters().forEach((key, value) -> {
|
|
||||||
var lowerCaseKey = key.toLowerCase();
|
|
||||||
var property = TracingConstants.REQUEST_QUERY_PARAMS + '.' + lowerCaseKey;
|
|
||||||
|
|
||||||
if (redactedQueryParams.contains(lowerCaseKey)) {
|
|
||||||
tracingService.trace(property, REDACTED_VALUE);
|
|
||||||
} else {
|
|
||||||
tracingService.trace(property, joinStrings(value));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (configuration.requestFilter().query().includeRaw()) {
|
|
||||||
var rawQuery = uriInfo.getRequestUri().getRawQuery();
|
|
||||||
if (rawQuery != null && !rawQuery.isBlank()) {
|
|
||||||
tracingService.trace(TracingConstants.REQUEST_QUERY_RAW, rawQuery);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tracingService.trace(
|
|
||||||
TracingConstants.REQUEST_CLIENT_IP,
|
|
||||||
routingContext.request().connection().remoteAddress(true).hostAddress());
|
|
||||||
|
|
||||||
Log.debugf("Incoming request: %s %s", method, uriInfo.getAbsolutePath().getRawPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String joinStrings(List<String> value) {
|
|
||||||
return String.join(", ", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getRoutePattern(ResourceInfo resourceInfo) {
|
|
||||||
String classPath = getPathValue(resourceInfo.getResourceClass().getAnnotation(Path.class));
|
|
||||||
String methodPath = getPathValue(resourceInfo.getResourceMethod().getAnnotation(Path.class));
|
|
||||||
|
|
||||||
if (!classPath.isEmpty()) {
|
|
||||||
if (methodPath.isEmpty()) {
|
|
||||||
return "/" + classPath;
|
|
||||||
} else {
|
|
||||||
return "/" + classPath + "/" + methodPath;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (methodPath.isEmpty()) {
|
|
||||||
return "/";
|
|
||||||
} else {
|
|
||||||
return "/" + methodPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getPathValue(Path path) {
|
|
||||||
if (path == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return trimSlashes(path.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String trimSlashes(String segment) {
|
|
||||||
if (segment.isEmpty() || "/".equals(segment)) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assuming that it's not possible for a segment to contain //,
|
|
||||||
// thus it's possible to avoid extra ifs and whiles, as well
|
|
||||||
// as the use of regexes
|
|
||||||
int start = (segment.charAt(0) == '/') ? 1 : 0;
|
|
||||||
int end = (segment.charAt(segment.length() - 1) == '/') ? segment.length() - 1 : segment.length();
|
|
||||||
|
|
||||||
return segment.substring(start, end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
public interface TracingService {
|
|
||||||
|
|
||||||
void clearAll();
|
|
||||||
|
|
||||||
void trace(String key, Object value);
|
|
||||||
|
|
||||||
String getActor();
|
|
||||||
|
|
||||||
String getRequestPathRaw();
|
|
||||||
|
|
||||||
String getRequestMethod();
|
|
||||||
|
|
||||||
String getRequestId();
|
|
||||||
|
|
||||||
String getTraceId();
|
|
||||||
|
|
||||||
String getSpanId();
|
|
||||||
|
|
||||||
String getClientIp();
|
|
||||||
|
|
||||||
String getSchedulerJob();
|
|
||||||
}
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.Span;
|
|
||||||
import io.quarkus.arc.DefaultBean;
|
|
||||||
import io.quarkus.logging.Log;
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
|
||||||
import org.jboss.logging.MDC;
|
|
||||||
|
|
||||||
@DefaultBean
|
|
||||||
@ApplicationScoped
|
|
||||||
class TracingServiceImpl implements TracingService {
|
|
||||||
|
|
||||||
private final Span span;
|
|
||||||
|
|
||||||
TracingServiceImpl(Span span) {
|
|
||||||
this.span = span;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearAll() {
|
|
||||||
MDC.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void trace(final String key, final Object value) {
|
|
||||||
Log.tracef("tracing key=%s value=%s", key, value);
|
|
||||||
MDC.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getActor() {
|
|
||||||
return (String) MDC.get(TracingConstants.ACTOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRequestPathRaw() {
|
|
||||||
return (String) MDC.get(TracingConstants.REQUEST_PATH_RAW);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRequestMethod() {
|
|
||||||
return (String) MDC.get(TracingConstants.REQUEST_METHOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRequestId() {
|
|
||||||
return (String) MDC.get(TracingConstants.REQUEST_HEADERS + ".x-request-id");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTraceId() {
|
|
||||||
return span.getSpanContext().getTraceId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSpanId() {
|
|
||||||
return span.getSpanContext().getSpanId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getClientIp() {
|
|
||||||
return (String) MDC.get(TracingConstants.REQUEST_CLIENT_IP);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSchedulerJob() {
|
|
||||||
return (String) MDC.get(TracingConstants.SCHEDULER_JOB_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.ArgumentMatchers.startsWith;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import io.restassured.http.ContentType;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class ActorTest {
|
|
||||||
|
|
||||||
@InjectSpy
|
|
||||||
TracingService tracingService;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getAuthenticated() {
|
|
||||||
var route = "/authenticated";
|
|
||||||
RestAssured.given()
|
|
||||||
.auth()
|
|
||||||
.basic("jon", "doe")
|
|
||||||
.accept(ContentType.TEXT)
|
|
||||||
.when()
|
|
||||||
.get(route)
|
|
||||||
.then()
|
|
||||||
.statusCode(200);
|
|
||||||
|
|
||||||
verify(tracingService).trace("actor", "jon");
|
|
||||||
verify(tracingService).trace("request.method", "GET");
|
|
||||||
verify(tracingService).trace("request.route", route);
|
|
||||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
|
||||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
|
||||||
verify(tracingService).trace("request.headers.authorization", "********");
|
|
||||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
|
||||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
|
||||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
|
||||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
|
||||||
verifyNoMoreInteractions(tracingService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.ArgumentMatchers.startsWith;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import io.restassured.http.ContentType;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class QueryParamTest {
|
|
||||||
|
|
||||||
@InjectSpy
|
|
||||||
TracingService tracingService;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void traceQueryParams() {
|
|
||||||
var route = "/authenticated";
|
|
||||||
RestAssured.given()
|
|
||||||
.auth()
|
|
||||||
.basic("jon", "doe")
|
|
||||||
.accept(ContentType.TEXT)
|
|
||||||
.header("X-SOMETHING-ELSE", "whatever")
|
|
||||||
.queryParam("access_token", "api123")
|
|
||||||
.queryParam("refresh_token", "refresh123")
|
|
||||||
.queryParam("apikey", "apikey123")
|
|
||||||
.queryParam("grant_type", "authorization_code")
|
|
||||||
.when()
|
|
||||||
.get(route)
|
|
||||||
.then()
|
|
||||||
.statusCode(200);
|
|
||||||
|
|
||||||
verify(tracingService).trace("actor", "jon");
|
|
||||||
verify(tracingService).trace("request.method", "GET");
|
|
||||||
verify(tracingService).trace("request.route", route);
|
|
||||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
|
||||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
|
||||||
verify(tracingService).trace("request.headers.authorization", "********");
|
|
||||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
|
||||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
|
||||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
|
||||||
verify(tracingService).trace("request.headers.x-something-else", "whatever");
|
|
||||||
verify(tracingService).trace("request.query.params.access_token", "********");
|
|
||||||
verify(tracingService).trace("request.query.params.refresh_token", "********");
|
|
||||||
verify(tracingService).trace("request.query.params.apikey", "********");
|
|
||||||
verify(tracingService).trace("request.query.params.grant_type", "authorization_code");
|
|
||||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
|
||||||
verifyNoMoreInteractions(tracingService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.ArgumentMatchers.startsWith;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import io.quarkus.test.junit.TestProfile;
|
|
||||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import io.restassured.http.ContentType;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
@TestProfile(Test2Profile.class)
|
|
||||||
class RawPathTest {
|
|
||||||
|
|
||||||
@InjectSpy
|
|
||||||
TracingService tracingService;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getWithRawPath() {
|
|
||||||
var route = "/no-leading-and-trailing/{param}/{param2}";
|
|
||||||
RestAssured.given()
|
|
||||||
.accept(ContentType.TEXT)
|
|
||||||
.when()
|
|
||||||
.get(route, 1, 2)
|
|
||||||
.then()
|
|
||||||
.statusCode(200);
|
|
||||||
|
|
||||||
verify(tracingService).trace("actor", "anonymous");
|
|
||||||
verify(tracingService).trace("request.method", "GET");
|
|
||||||
verify(tracingService).trace("request.route", route);
|
|
||||||
verify(tracingService).trace("request.path.params.param", "1");
|
|
||||||
verify(tracingService).trace("request.path.params.param2", "2");
|
|
||||||
verify(tracingService).trace("request.path.raw", "/no-leading-and-trailing/1/2");
|
|
||||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
|
||||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
|
||||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
|
||||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
|
||||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
|
||||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
|
||||||
verifyNoMoreInteractions(tracingService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.ArgumentMatchers.startsWith;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import io.quarkus.test.junit.TestProfile;
|
|
||||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import io.restassured.http.ContentType;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
@TestProfile(Test2Profile.class)
|
|
||||||
class RedactedTest {
|
|
||||||
|
|
||||||
@InjectSpy
|
|
||||||
TracingService tracingService;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void traceRedactedValues() {
|
|
||||||
var route = "/authenticated";
|
|
||||||
RestAssured.given()
|
|
||||||
.auth()
|
|
||||||
.basic("jon", "doe")
|
|
||||||
.accept(ContentType.TEXT)
|
|
||||||
.header("X-SOMETHING-ELSE", "whatever")
|
|
||||||
.queryParam("access_token", "api123")
|
|
||||||
.queryParam("refresh_token", "refresh123")
|
|
||||||
.queryParam("apikey", "apikey123")
|
|
||||||
.queryParam("grant_type", "authorization_code")
|
|
||||||
.when()
|
|
||||||
.get(route)
|
|
||||||
.then()
|
|
||||||
.statusCode(200);
|
|
||||||
|
|
||||||
verify(tracingService).trace("actor", "jon");
|
|
||||||
verify(tracingService).trace("request.method", "GET");
|
|
||||||
verify(tracingService).trace("request.route", route);
|
|
||||||
verify(tracingService).trace("request.path.raw", route);
|
|
||||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
|
||||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
|
||||||
verify(tracingService).trace("request.headers.authorization", "********");
|
|
||||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
|
||||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
|
||||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
|
||||||
verify(tracingService).trace("request.headers.x-something-else", "********");
|
|
||||||
verify(tracingService).trace("request.query.params.access_token", "********");
|
|
||||||
verify(tracingService).trace("request.query.params.refresh_token", "refresh123");
|
|
||||||
verify(tracingService).trace("request.query.params.apikey", "apikey123");
|
|
||||||
verify(tracingService).trace("request.query.params.grant_type", "authorization_code");
|
|
||||||
verify(tracingService)
|
|
||||||
.trace(
|
|
||||||
"request.query.raw",
|
|
||||||
"access_token=api123&refresh_token=refresh123&apikey=apikey123&grant_type=authorization_code");
|
|
||||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
|
||||||
verifyNoMoreInteractions(tracingService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.ArgumentMatchers.startsWith;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
|
||||||
import io.restassured.RestAssured;
|
|
||||||
import io.restassured.http.ContentType;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class RoutePatternTest {
|
|
||||||
|
|
||||||
@InjectSpy
|
|
||||||
TracingService tracingService;
|
|
||||||
|
|
||||||
static Stream<Arguments> get() {
|
|
||||||
return Stream.of(
|
|
||||||
arguments("/", Map.of()),
|
|
||||||
arguments("/leading-and-no-trailing", Map.of()),
|
|
||||||
arguments("/leading/{param}/{param2}", Map.of("param", "1", "param2", "2")),
|
|
||||||
arguments("/{param}/{param2}/trailing", Map.of("param", "1", "param2", "2")),
|
|
||||||
arguments("/leading-and-no-trailing/{param}", Map.of("param", "1")),
|
|
||||||
arguments("/leading-and-no-trailing/{param}/{param2}", Map.of("param", "1", "param2", "2")),
|
|
||||||
arguments("/leading-and-trailing", Map.of()),
|
|
||||||
arguments("/leading-and-trailing/{param}", Map.of("param", "1")),
|
|
||||||
arguments("/leading-and-trailing/{param}/{param2}", Map.of("param", "1", "param2", "2")),
|
|
||||||
arguments("/no-leading-and-no-trailing", Map.of()),
|
|
||||||
arguments("/no-leading-and-no-trailing/{param}", Map.of("param", "1")),
|
|
||||||
arguments("/no-leading-and-no-trailing/{param}/{param2}", Map.of("param", "1", "param2", "2")),
|
|
||||||
arguments("/no-leading-and-trailing", Map.of()),
|
|
||||||
arguments("/no-leading-and-trailing/{param}", Map.of("param", "1")),
|
|
||||||
arguments("/no-leading-and-trailing/{param}/{param2}", Map.of("param", "1", "param2", "2")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MethodSource
|
|
||||||
@ParameterizedTest
|
|
||||||
void get(String route, Map<String, String> pathParams) {
|
|
||||||
RestAssured.given()
|
|
||||||
.accept(ContentType.TEXT)
|
|
||||||
.when()
|
|
||||||
.get(route, pathParams)
|
|
||||||
.then()
|
|
||||||
.statusCode(200);
|
|
||||||
|
|
||||||
verify(tracingService).trace("actor", "anonymous");
|
|
||||||
verify(tracingService).trace("request.method", "GET");
|
|
||||||
verify(tracingService).trace("request.route", route);
|
|
||||||
pathParams.forEach((key, value) -> verify(tracingService).trace("request.path.params." + key, value));
|
|
||||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
|
||||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
|
||||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
|
||||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
|
||||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
|
||||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
|
||||||
verifyNoMoreInteractions(tracingService);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void postBlankResource() {
|
|
||||||
var route = "/";
|
|
||||||
RestAssured.given().accept(ContentType.TEXT).when().post(route).then().statusCode(200);
|
|
||||||
|
|
||||||
verify(tracingService).trace("actor", "anonymous");
|
|
||||||
verify(tracingService).trace("request.method", "POST");
|
|
||||||
verify(tracingService).trace("request.route", route);
|
|
||||||
verify(tracingService).trace("request.headers.accept", "text/plain");
|
|
||||||
verify(tracingService).trace("request.headers.accept-encoding", "gzip,deflate");
|
|
||||||
verify(tracingService).trace("request.headers.connection", "Keep-Alive");
|
|
||||||
verify(tracingService).trace("request.headers.content-length", "0");
|
|
||||||
verify(tracingService)
|
|
||||||
.trace("request.headers.content-type", "application/x-www-form-urlencoded; charset=ISO-8859-1");
|
|
||||||
verify(tracingService).trace(eq("request.headers.host"), startsWith("localhost:"));
|
|
||||||
verify(tracingService).trace(eq("request.headers.user-agent"), startsWith("Apache-HttpClient"));
|
|
||||||
verify(tracingService).trace("request.client.ip", "127.0.0.1");
|
|
||||||
verifyNoMoreInteractions(tracingService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTestProfile;
|
|
||||||
|
|
||||||
public class Test2Profile implements QuarkusTestProfile {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConfigProfile() {
|
|
||||||
return "test2";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
import io.opentelemetry.api.trace.Span;
|
|
||||||
import io.opentelemetry.instrumentation.annotations.WithSpan;
|
|
||||||
import io.quarkus.test.junit.QuarkusTest;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import org.jboss.logging.MDC;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class TracingServiceImplTest {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
TracingService tracingService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
Span span;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() {
|
|
||||||
MDC.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getActor() {
|
|
||||||
tracingService.trace("actor", "abc");
|
|
||||||
assertThat(tracingService.getActor())
|
|
||||||
.as("Actor should match expected value")
|
|
||||||
.isEqualTo("abc");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getRequestPathRaw() {
|
|
||||||
tracingService.trace("request.path.raw", "/foo/bar");
|
|
||||||
assertThat(tracingService.getRequestPathRaw())
|
|
||||||
.as("Request Path Raw should match expected value")
|
|
||||||
.isEqualTo("/foo/bar");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getRequestMethod() {
|
|
||||||
tracingService.trace("request.method", "GET");
|
|
||||||
assertThat(tracingService.getRequestMethod())
|
|
||||||
.as("Request Method should match expected value")
|
|
||||||
.isEqualTo("GET");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getRequestId() {
|
|
||||||
tracingService.trace("request.headers.x-request-id", "ba458367-bfeb-46ba-87da-50b9343be8f9");
|
|
||||||
assertThat(tracingService.getRequestId())
|
|
||||||
.as("Request Id should match expected value")
|
|
||||||
.isEqualTo("ba458367-bfeb-46ba-87da-50b9343be8f9");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithSpan
|
|
||||||
void getTraceId() {
|
|
||||||
assertThat(tracingService.getTraceId())
|
|
||||||
.as("Request Trace Id should match expected value")
|
|
||||||
.isEqualTo(span.getSpanContext().getTraceId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@WithSpan
|
|
||||||
void getSpanId() {
|
|
||||||
assertThat(tracingService.getSpanId())
|
|
||||||
.as("Request Span Id should match expected value")
|
|
||||||
.isEqualTo(span.getSpanContext().getSpanId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getClientIp() {
|
|
||||||
tracingService.trace("request.client.ip", "127.0.0.1");
|
|
||||||
assertThat(tracingService.getClientIp())
|
|
||||||
.as("Request Client Iü should match expected value")
|
|
||||||
.isEqualTo("127.0.0.1");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void getSchedulerJob() {
|
|
||||||
tracingService.trace("scheduler.job.name", "scheduler/abc");
|
|
||||||
assertThat(tracingService.getSchedulerJob())
|
|
||||||
.as("Scheduler Job Name should match expected value")
|
|
||||||
.isEqualTo("scheduler/abc");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void clearAll() {
|
|
||||||
tracingService.trace("aaa", "bbb");
|
|
||||||
assertThat(MDC.get("aaa")).isEqualTo("bbb");
|
|
||||||
tracingService.clearAll();
|
|
||||||
assertThat(MDC.get("aaa")).isNull();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
|
||||||
|
|
||||||
import io.quarkus.security.Authenticated;
|
|
||||||
import jakarta.ws.rs.GET;
|
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
|
|
||||||
@Authenticated
|
|
||||||
@Path("authenticated")
|
|
||||||
public class AuthenticatedResource {
|
|
||||||
|
|
||||||
@GET
|
|
||||||
public String get() {
|
|
||||||
return "Hello user";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
|
||||||
|
|
||||||
import jakarta.annotation.security.PermitAll;
|
|
||||||
import jakarta.ws.rs.GET;
|
|
||||||
import jakarta.ws.rs.POST;
|
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
import jakarta.ws.rs.Produces;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
@Path("")
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public class BlankResource {
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@PermitAll
|
|
||||||
public String get() {
|
|
||||||
return "get";
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("")
|
|
||||||
public String post() {
|
|
||||||
return "post";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.GET;
|
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
import jakarta.ws.rs.Produces;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
@Path("/leading-and-no-trailing")
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public class LeadingAndNoTrailingResource {
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("")
|
|
||||||
public String root() {
|
|
||||||
return "root";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{param}")
|
|
||||||
public String singleParam(String param) {
|
|
||||||
return param;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{param}/{param2}")
|
|
||||||
public String multiParam(String param, String param2) {
|
|
||||||
return param + "/" + param2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.GET;
|
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
import jakarta.ws.rs.Produces;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
@Path("/leading-and-trailing/")
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public class LeadingAndTrailingResource {
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/")
|
|
||||||
public String root() {
|
|
||||||
return "root";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{param}/")
|
|
||||||
public String singleParam(String param) {
|
|
||||||
return param;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{param}/{param2}/")
|
|
||||||
public String multiParam(String param, String param2) {
|
|
||||||
return param + "/" + param2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.GET;
|
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
import jakarta.ws.rs.Produces;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
@Path("no-leading-and-no-trailing")
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public class NoLeadingAndNoTrailingResource {
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("")
|
|
||||||
public String root() {
|
|
||||||
return "root";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("{param}")
|
|
||||||
public String singleParam(String param) {
|
|
||||||
return param;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("{param}/{param2}")
|
|
||||||
public String multiParam(String param, String param2) {
|
|
||||||
return param + "/" + param2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.GET;
|
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
import jakarta.ws.rs.Produces;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
@Path("no-leading-and-trailing/")
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public class NoLeadingAndTrailingResource {
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/")
|
|
||||||
public String root() {
|
|
||||||
return "root";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("{param}/")
|
|
||||||
public String singleParam(String param) {
|
|
||||||
return param;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("{param}/{param2}/")
|
|
||||||
public String multiParam(String param, String param2) {
|
|
||||||
return param + "/" + param2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.tracing.resource;
|
|
||||||
|
|
||||||
import jakarta.ws.rs.GET;
|
|
||||||
import jakarta.ws.rs.Path;
|
|
||||||
import jakarta.ws.rs.Produces;
|
|
||||||
import jakarta.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
@Path("/")
|
|
||||||
@Produces(MediaType.TEXT_PLAIN)
|
|
||||||
public class SlashResource {
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/leading/{param}/{param2}")
|
|
||||||
public String doubleLeading(int param, int param2) {
|
|
||||||
return "leading/" + param + "/" + param2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("{param}/{param2}/trailing/")
|
|
||||||
public String doubleTrailing(int param, int param2) {
|
|
||||||
return param + "/" + param2 + "/trailing";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
quarkus:
|
|
||||||
http:
|
|
||||||
test-port: 0
|
|
||||||
log:
|
|
||||||
category:
|
|
||||||
"org.apache.http.wire":
|
|
||||||
level: DEBUG # set to DEBUG when RestAssured logs are necessary to understand test failures
|
|
||||||
otel:
|
|
||||||
traces:
|
|
||||||
exporter: none
|
|
||||||
security:
|
|
||||||
users:
|
|
||||||
embedded:
|
|
||||||
enabled: true
|
|
||||||
plain-text: true
|
|
||||||
users:
|
|
||||||
jon: doe
|
|
||||||
|
|
||||||
"%test2":
|
|
||||||
quarkus:
|
|
||||||
log:
|
|
||||||
min-level: TRACE
|
|
||||||
category:
|
|
||||||
"ch.phoenix.oss.quarkus.commons.tracing":
|
|
||||||
level: TRACE
|
|
||||||
phoenix-oss:
|
|
||||||
tracing:
|
|
||||||
request-filter:
|
|
||||||
path:
|
|
||||||
include-raw: true
|
|
||||||
headers:
|
|
||||||
redact:
|
|
||||||
- AUTHORIZATION
|
|
||||||
- X-SOMETHING-ELSE
|
|
||||||
query:
|
|
||||||
include-raw: true
|
|
||||||
redact:
|
|
||||||
- ACCESS_TOKEN
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.phoenix.oss</groupId>
|
<groupId>ch.phoenixtechnologies.quarkus</groupId>
|
||||||
<artifactId>quarkus-commons</artifactId>
|
<artifactId>quarkus-commons</artifactId>
|
||||||
<version>1.1.5-SNAPSHOT</version>
|
<version>1.0.1-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>quarkus-uuid-generator</artifactId>
|
<artifactId>quarkus-uuid-generator</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.uuid;
|
package ch.phoenixtechnologies.quarkus.commons.uuid;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.uuid;
|
package ch.phoenixtechnologies.quarkus.commons.uuid;
|
||||||
|
|
||||||
import com.fasterxml.uuid.Generators;
|
import com.fasterxml.uuid.Generators;
|
||||||
import io.quarkus.arc.DefaultBean;
|
import io.quarkus.arc.DefaultBean;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package ch.phoenix.oss.quarkus.commons.uuid;
|
package ch.phoenixtechnologies.quarkus.commons.uuid;
|
||||||
|
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue