Compare commits

..
Author SHA1 Message Date
james.maes 2913a9f200 Merge pull request 'build(deps): align with Gitea registry (parent 2.2.1, QQQ 4.1.0-RC.26)' (#30) from deps/align-gitea into develop
build / maven (push) Successful in 6m48s
maven / build (push) Successful in 3m22s
2026-11-07 18:16:08 +00:00
james.maesandClaude Opus 5.5 caaf34c9ff build(deps): align with Gitea registry (parent 2.2.1, QQQ 4.1.0-RC.26)
build / maven (push) Successful in 2m43s
maven / build (push) Successful in 2m43s
pom.xml: parent -> 2.2.1
pom.xml: qqq.version: 4.1.0-RC.21 -> 4.1.0-RC.26

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 18:09:48 +00:00
james.maes c2f23156ac Merge pull request 'ci: pin shared workflow to v1' (#29) from ci/pin-v1-develop into develop
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 13s
build / maven (push) Successful in 2m40s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (push) Successful in 15s
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: native / native-result (push) Successful in 9s
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (push) Skipped
maven / build (push) Successful in 2m40s
2026-11-06 16:11:11 +00:00
james.maesandClaude Opus 5.5 0f2327507f ci: pin shared workflow to v1
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
build / maven (push) Successful in 1m50s
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (pull_request) Successful in 8s
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
maven / build (push) Successful in 1m49s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 14:14:26 +00:00
james.maes 760b7f0a0d Merge pull request 'ci: use shared ci-workflows Maven workflow' (#28) from ci/ci-workflows into develop
build / maven (push) Successful in 1m36s
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 5s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1e25bb5a243dfdcd95d4b9a4f780200ff8c8778a] (push) Successful in 6s
QRun-IO/ci-policy: native / native-result (push) Successful in 5s
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
maven / build (push) Successful in 1m36s
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (push) Skipped
2026-11-06 13:18:47 +00:00
CI aeec64c539 ci: rerun checks after dispatcher retirement
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow d20a578c7d818bc7ca32a2ac2c675da4e4f27385] (pull_request) Successful in 7s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
build / maven (push) Successful in 1m44s
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 5s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 4s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
maven / build (push) Successful in 1m44s
2026-11-05 17:19:20 -06:00
CI e2bce64102 ci: rerun shared workflow
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1b3648d9421543350067e95e54959dfb471b26d5] (pull_request) Successful in 8s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / secrets (pull_request) Failing after 1s
build / maven (push) Successful in 1m7s
QRun-IO/ci-policy: Shared Java Maven CI / verify (pull_request) Successful in 57s
QRun-IO/ci-policy: Shared Java Maven CI / gate (pull_request) Failing after 17s
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Failing after 1s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Failing after 18m36s
QRun-IO/ci-policy: ci / ci-result (pull_request) Failing after 7s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
maven / build (push) Successful in 1m7s
2026-11-05 16:27:40 -06:00
james.maesandClaude Opus 5.5 b6a2ac3631 ci: use shared ci-workflows Maven workflow
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 1b3648d9421543350067e95e54959dfb471b26d5] (pull_request) Failing after 2s
build / maven (push) Failing after 2s
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Failing after 0s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Failing after 1s
maven / build (push) Failing after 2s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
Replace the platform-ci release callers with one caller of
KofTwentyTwo/ci-workflows maven.yaml (qqq-orb port).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:09:00 +00:00
james.maes 81647be127 Merge pull request 'Align with published QQQ 4.1 and preserve KOF22 integration' (#27) from feature/kof22-stp-canonical into develop
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow cf802461ca4ebfcf5c500440715cb333d92ea606] (push) Successful in 13s
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: ci / original-cli (push) Skipped
Shared Java Maven CI / secrets (push) Successful in 49s
Shared Java Maven CI / verify (push) Successful in 2m1s
QRun-IO/ci-policy: ci / ci-result (push) Successful in 8s
qrun-release / postpublish (push) Successful in 6m51s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: native / native-result (push) Successful in 12s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (push) Successful in 33s
QRun-IO/ci-policy: Shared Java Maven CI / verify (push) Successful in 1m4s
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (push) Skipped
Shared Java Maven CI / gate (push) Successful in 16s
qrun-release / release (push) Successful in 35m6s
QRun-IO/ci-policy: Shared Java Maven CI / gate (push) Successful in 19s
QRun-IO/ci-policy: ci / java-maven-6 (push) Successful in 37m5s
Shared QRun source version / version (push) Successful in 6m51s
2026-11-05 13:46:54 +00:00
James MaesandClaude Sonnet 5.5 e6bda14097 build: require the published QQQ 4.1 core (4.1.0-RC.21)
QRun-IO/ci-policy: Shared Java Maven CI / secrets (pull_request) Successful in 37s
Shared Java Maven CI / secrets (push) Successful in 38s
Shared Java Maven CI / verify (push) Canceled after 0s
qrun-release / postpublish (push) Canceled after 0s
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
QRun-IO/ci-policy: ci / original-voyage (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / verify (pull_request) Successful in 54s
QRun-IO/ci-policy: Shared Java Maven CI / gate (pull_request) Successful in 22s
Release candidate / release-rc (pull_request) Skipped
QRun-IO/ci-policy: ci / Scoped workflow [workflow 60e5a80ed7a5a67877bfb3e129b1929c83464b6e] (pull_request) Successful in 12s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-cli (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 9s
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Successful in 42m16s
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 11s
Shared Java Maven CI / gate (push) Canceled after 0s
qrun-release / release (push) Canceled after 5m21s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / shared-pipeline (pull_request) Skipped
Import qqq-bom-pom 4.1.0-RC.21 unconditionally ahead of the parent's BOM
and remove the opt-in qqq-snapshot profile and its Sonatype snapshot
repository. State the minimum QQQ version as 4.1.0+ in the README. The
development revision and the parent are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 05:46:43 -06:00
forge-recovery f9f415f5d3 ci: enroll shared Gitea lifecycle
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: ci / original-voyage (push) Skipped
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
Shared Java Maven CI / secrets (push) Successful in 21s
qrun-release / postpublish (push) Failing after 10m17s
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 6s
Shared Java Maven CI / verify (push) Successful in 1m40s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: native / native-result (push) Successful in 6s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (push) Successful in 16s
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / verify (push) Successful in 59s
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
Shared QRun source version / version (push) Failing after 10m17s
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / gate (push) Successful in 12s
QRun-IO/ci-policy: ci / java-maven-6 (push) Successful in 1m18s
Shared Java Maven CI / gate (push) Successful in 9s
qrun-release / release (push) Successful in 1m51s
2026-11-03 20:34:57 +00:00
forge-recoveryandCodex c6f13121a1 ci: enroll standard release candidate lifecycle
Release candidate / release-rc (pull_request) Skipped
Shared Java Maven CI / verify (push) Successful in 21s
qrun-release / postpublish (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / verify (pull_request) Successful in 1m5s
QRun-IO/ci-policy: Shared Java Maven CI / gate (pull_request) Successful in 9s
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Successful in 1h25m50s
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
Shared Java Maven CI / secrets (push) Successful in 34s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (pull_request) Successful in 34s
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 6s
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 8s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
Shared Java Maven CI / gate (push) Successful in 19s
qrun-release / release (push) Successful in 1h25m0s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
Co-Authored-By: Codex <noreply@openai.com>

Co-Authored-By: Codex <noreply@openai.com>
2026-11-03 18:45:24 +00:00
forge-recovery 9086c8d15e ci: enroll shared Gitea lifecycle
Shared Java Maven CI / verify (push) Successful in 16s
qrun-release / postpublish (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (push) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (push) Skipped
QRun-IO/ci-policy: ci / source-config-14 (push) Skipped
QRun-IO/ci-policy: native / dotnet-0 (push) Skipped
QRun-IO/ci-policy: ci / java-core-2-head (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 6s
Shared Java Maven CI / secrets (push) Successful in 33s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: ci / head-profile (push) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (push) Skipped
QRun-IO/ci-policy: ci / node-8 (push) Skipped
QRun-IO/ci-policy: ci / node-9 (push) Skipped
QRun-IO/ci-policy: ci / node-10 (push) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (push) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (push) Skipped
QRun-IO/ci-policy: ci / source-config-13 (push) Skipped
QRun-IO/ci-policy: ci / original-business (push) Skipped
QRun-IO/ci-policy: native / fork-deny (push) Skipped
QRun-IO/ci-policy: native / native-result (push) Successful in 6s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (push) Successful in 20s
QRun-IO/ci-policy: ci / repository-policy (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / verify (push) Successful in 54s
Shared Java Maven CI / gate (push) Successful in 18s
qrun-release / release (push) Successful in 1h24m38s
QRun-IO/ci-policy: ci / application-template-0 (push) Skipped
QRun-IO/ci-policy: ci / gradle-1 (push) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (push) Skipped
QRun-IO/ci-policy: ci / android-0 (push) Skipped
QRun-IO/ci-policy: ci / original-mcp (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (push) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / gate (push) Successful in 13s
QRun-IO/ci-policy: ci / java-maven-6 (push) Successful in 6m35s
Shared QRun source version / version (push) Successful in 2m6s
2026-11-03 17:51:18 +00:00
forge-recoveryandCodex fefcb4fbfa ci: retire superseded Stdtrace pilot caller
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
Shared Java Maven CI / secrets (push) Successful in 26s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (pull_request) Successful in 30s
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
Shared Java Maven CI / verify (push) Successful in 16s
qrun-release / postpublish (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / verify (pull_request) Successful in 50s
QRun-IO/ci-policy: Shared Java Maven CI / gate (pull_request) Successful in 11s
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 11s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Successful in 29m29s
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 5s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
Shared Java Maven CI / gate (push) Successful in 10s
qrun-release / release (push) Successful in 28m38s
Co-Authored-By: Codex <noreply@openai.com>
2026-11-03 17:20:24 +00:00
forge-recoveryandCodex 22c850c025 ci: enroll shared Gitea lifecycle
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / head-profile (pull_request) Skipped
QRun-IO/ci-policy: ci / application-template-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-8 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-business (pull_request) Skipped
Shared Java Maven CI / verify (push) Successful in 21s
Shared Java Maven CI / secrets (push) Successful in 23s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (pull_request) Successful in 24s
QRun-IO/ci-policy: ci / repository-policy (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-6-head (pull_request) Skipped
qrun-release / postpublish (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / verify (pull_request) Successful in 1m3s
QRun-IO/ci-policy: Shared Java Maven CI / gate (pull_request) Successful in 11s
QRun-IO/ci-policy: ci / java-maven-6 (pull_request) Successful in 1m21s
Release candidate / candidate (pull_request) Successful in 1m23s
QRun-IO/ci-policy: ci / node-10 (pull_request) Skipped
QRun-IO/ci-policy: ci / android-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / original-mcp (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: native / fork-deny (pull_request) Skipped
QRun-IO/ci-policy: native / dotnet-0 (pull_request) Skipped
QRun-IO/ci-policy: native / native-result (pull_request) Successful in 7s
QRun-IO/ci-policy: ci / java-core-2-head (pull_request) Skipped
QRun-IO/ci-policy: ci / java-maven-7-head (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 5s
QRun-IO/ci-policy: ci / gradle-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-4 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-frontend-browser-5 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-9 (pull_request) Skipped
QRun-IO/ci-policy: ci / node-playwright-11 (pull_request) Skipped
QRun-IO/ci-policy: ci / repository-policy-12 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-13 (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-14 (pull_request) Skipped
QRun-IO/ci-policy: ci / java-docker-3-head (pull_request) Skipped
Shared Java Maven CI / gate (push) Successful in 11s
qrun-release / release (push) Successful in 38s
Java release candidate artifact / build (pull_request) Successful in 1m23s
Co-Authored-By: Codex <noreply@openai.com>

Co-Authored-By: Codex <noreply@openai.com>
2026-11-03 17:05:13 +00:00
forge-recovery 03db9b6f89 Merge pull request 'Verify automatic Gitea snapshot publication' (#23) from qualification/automatic-publish-proof-20261031 into develop
Release candidate / candidate (push) Successful in 58s
QRun-IO/ci-policy: ci / java-docker-0 (push) Skipped
QRun-IO/ci-policy: ci / node-2 (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 3s
QRun-IO/ci-policy: ci / java-maven-1 (push) Successful in 1m18s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
Java release candidate artifact / build (push) Successful in 58s
QRun-IO/ci-policy: Shared Java Maven CI / verify (push) Successful in 56s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (push) Successful in 12s
QRun-IO/ci-policy: ci / source-config-3 (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / gate (push) Successful in 7s
2026-11-01 00:17:35 +00:00
forge-recoveryandGPT-6.1 afde841966 test(ci): verify automatic snapshot publication from a fresh source change
Release candidate / candidate (pull_request) Successful in 1m3s
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-3 (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / verify (pull_request) Successful in 52s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (pull_request) Successful in 13s
QRun-IO/ci-policy: Shared Java Maven CI / gate (pull_request) Successful in 8s
QRun-IO/ci-policy: ci / java-docker-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 3s
QRun-IO/ci-policy: ci / node-2 (pull_request) Skipped
Java release candidate artifact / build (pull_request) Successful in 1m3s
QRun-IO/ci-policy: ci / java-maven-1 (pull_request) Successful in 1m15s
Co-Authored-By: GPT-6.1 <noreply@openai.com>
2026-11-01 00:14:40 +00:00
forge-recovery 1731d749ab Merge pull request 'Complete the central Maven publication happy path' (#22) from qualification/happy-release-20261031 into develop
QRun-IO/ci-policy: ci / java-docker-0 (push) Skipped
QRun-IO/ci-policy: ci / node-2 (push) Skipped
Release candidate / candidate (push) Successful in 1m2s
QRun-IO/ci-policy: ci / ci-result (push) Successful in 4s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / secrets (push) Successful in 11s
Java release candidate artifact / build (push) Successful in 1m2s
QRun-IO/ci-policy: Shared Java Maven CI / verify (push) Successful in 54s
QRun-IO/ci-policy: ci / java-maven-1 (push) Successful in 1m16s
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: ci / source-config-3 (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / gate (push) Successful in 8s
2026-10-31 21:14:39 +00:00
forge-recovery 241e843364 feat(ci): build tested source-bound Maven publication candidates
Release candidate / candidate (pull_request) Successful in 1m0s
QRun-IO/ci-policy: Shared Java Maven CI / verify (pull_request) Successful in 54s
QRun-IO/ci-policy: Shared Java Maven CI / gate (pull_request) Successful in 9s
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-3 (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / secrets (pull_request) Successful in 11s
QRun-IO/ci-policy: ci / java-docker-0 (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 4s
QRun-IO/ci-policy: ci / node-2 (pull_request) Skipped
Java release candidate artifact / build (pull_request) Successful in 1m0s
QRun-IO/ci-policy: ci / java-maven-1 (pull_request) Successful in 1m16s
2026-10-31 20:32:52 +00:00
forge-recovery ab57dd545a Merge pull request 'Qualify shared Gitea Actions on application source' (#21) from qualification/central-actions-20261031 into develop
QRun-IO/ci-policy: ci / java-docker-0 (push) Skipped
QRun-IO/ci-policy: ci / node-2 (push) Skipped
QRun-IO/ci-policy: ci / ci-result (push) Successful in 5s
QRun-IO/ci-policy: ci / fork-guard (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / verify (push) Successful in 1m2s
QRun-IO/ci-policy: ci / java-maven-1 (push) Successful in 1m42s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (push) Successful in 18s
QBit CodeQL / Analyze branch source (push) Canceled after 0s
CodeQL / analyze (push) Canceled after 0s
QRun-IO/ci-policy: ci / source-config-3 (push) Skipped
QRun-IO/ci-policy: ci / not-enrolled (push) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / gate (push) Successful in 11s
2026-10-31 20:09:15 +00:00
forge-recoveryandGPT-6.1 431e0fce84 test: observe latest cleanup message without iterating a live log list
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / verify (pull_request) Successful in 50s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (pull_request) Successful in 11s
QRun-IO/ci-policy: Shared Java Maven CI / gate (pull_request) Successful in 7s
QRun-IO/ci-policy: ci / java-maven-0 (pull_request) Successful in 1m10s
QRun-IO/ci-policy: ci / node-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
CodeQL / analyze (pull_request) Canceled after 0s
QRun-IO/ci-policy: ci / ci-result (pull_request) Successful in 4s
Release candidate / release-rc (pull_request) Skipped
QRun-IO/ci-policy: ci / source-config-2 (pull_request) Skipped
QBit CodeQL / Analyze branch source (pull_request) Canceled after 0s
Co-Authored-By: GPT-6.1 <noreply@openai.com>
2026-10-31 12:36:43 +00:00
forge-recoveryandGPT-6.1 bd50f5a760 test: wait for scheduled trace cleanup within a bounded deadline
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / verify (pull_request) Successful in 50s
QRun-IO/ci-policy: Shared Java Maven CI / gate (pull_request) Canceled after 0s
QRun-IO/ci-policy: Shared Java Maven CI / secrets (pull_request) Canceled after 5s
QRun-IO/ci-policy: ci / java-maven-0 (pull_request) Canceled after 55s
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
QRun-IO/ci-policy: ci / node-1 (pull_request) Skipped
CodeQL / analyze (pull_request) Canceled after 0s
QRun-IO/ci-policy: ci / ci-result (pull_request) Canceled after 0s
QRun-IO/ci-policy: ci / source-config-2 (pull_request) Skipped
QBit CodeQL / Analyze branch source (pull_request) Canceled after 0s
Preserve the cleanup assertions while removing the fixed 50ms scheduling assumption.

Co-Authored-By: GPT-6.1 <noreply@openai.com>
2026-10-31 12:35:48 +00:00
forge-recovery 5f245adf59 test(ci): retry application qualification with public shared tooling
QRun-IO/ci-policy: ci / fork-guard (pull_request) Skipped
QRun-IO/ci-policy: Shared Java Maven CI / secrets (pull_request) Successful in 11s
QRun-IO/ci-policy: Shared Java Maven CI / verify (pull_request) Failing after 47s
QRun-IO/ci-policy: Shared Java Maven CI / gate (pull_request) Failing after 9s
QRun-IO/ci-policy: ci / java-maven-0 (pull_request) Failing after 1m16s
QRun-IO/ci-policy: ci / node-1 (pull_request) Skipped
QRun-IO/ci-policy: ci / not-enrolled (pull_request) Skipped
CodeQL / analyze (pull_request) Canceled after 0s
QRun-IO/ci-policy: ci / ci-result (pull_request) Failing after 3s
QRun-IO/ci-policy: ci / source-config-2 (pull_request) Skipped
QBit CodeQL / Analyze branch source (pull_request) Canceled after 0s
2026-10-31 12:32:17 +00:00
forge-recoveryandGPT-6.1 1a78ac8976 test(ci): qualify central organization Actions on application source
CodeQL / analyze (pull_request) Canceled after 0s
QBit CodeQL / Analyze branch source (pull_request) Canceled after 0s
Co-Authored-By: GPT-6.1 <noreply@openai.com>
2026-10-31 12:23:27 +00:00
james.maesandCodex efc8dc0567 ci: adopt managed orb 0.8 patch track (#19)
Co-authored-by: Codex <noreply@openai.com>
2026-10-21 15:29:17 -05:00
james.maesandCodex 767141e956 ci: adopt shared orb 0.7.18 (#18)
Co-authored-by: Codex <noreply@openai.com>
2026-10-21 14:47:09 -05:00
james.maesandCodex 2e8baba5fb ci: adopt shared orb 0.7.17 (#17)
Co-authored-by: Codex <noreply@openai.com>
2026-10-21 14:16:54 -05:00
james.maesandCodex d3ed64202f ci: centralize QBit source scanning (#15)
Co-authored-by: Codex <noreply@openai.com>
2026-10-21 10:40:52 -05:00
james.maesandCodex e8d8210532 ci: adopt central orb 0.7.12 and stable gates (#14)
Co-authored-by: Codex <noreply@openai.com>
2026-10-21 08:52:15 -05:00
james.maesandCodex 0129f1822b ci: centralize rc release completion in qqq orb (#13)
Co-authored-by: Codex <noreply@openai.com>
2026-10-21 02:24:07 -05:00
james.maesandCodex 48afbdd4d7 ci: publish GA artifacts only from stable tags (#12)
Co-authored-by: Codex <noreply@openai.com>
2026-10-21 01:09:20 -05:00
james.maesandCodex a7becd6f0b ci: adopt shared orb 0.7.11 (#11)
Co-authored-by: Codex <noreply@openai.com>
2026-10-21 00:56:07 -05:00
james.maes 8ed9fd5096 Merge pull request #10 from QRun-IO/feature/central-ci
ci: adopt shared QQQ orb 0.7.4
2026-10-20 19:30:46 -05:00
james.maesandCodex 060ee0d7d8 ci: adopt central QQQ orb 0.7.4
Co-Authored-By: Codex <noreply@openai.com>
2026-10-20 19:26:52 -05:00
james.maes 4c82d5b4f0 Merge pull request #9 from QRun-IO/ci/release-rc-secrets-inherit
ci(release-rc): pass secrets so the shared workflow can sign the RC tag
2026-10-16 08:03:53 -05:00
james.maesandClaude Opus 5.5 671fd10d45 ci(release-rc): pass secrets to the shared workflow so it can sign the RC tag
QRun-IO/.github#2 adds a tag-and-release job that signs vX with the
QRUN_TAG_SIGNING_KEY secret after Maven Central confirms the artifact.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 16:22:26 -05:00
james.maes d1a04a385f Merge pull request #5 from QRun-IO/feature/GH-10-public-stack-prep
build: prepare QQQ releases and align Apache-2.0
2026-10-14 13:54:23 -05:00
james.maesandClaude Sonnet 5.5 bfe8892f71 Merge origin/develop into feature/GH-10-public-stack-prep
Keep develop's release automation workflows; carry the orb 0.6.8 pin.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 12:47:53 -05:00
james.maes 680f76e3a3 Merge pull request #8 from QRun-IO/feature/release-rc-automation
ci: automate RC releases; hotfix/* pushes no longer publish
2026-10-13 15:00:51 -05:00
james.maesandClaude Opus 5.5 e0c81bf68d ci: automate RC releases; hotfix/* pushes no longer publish
Removes publish_hotfix_release: a hotfix/* branch push published a GA
release to Maven Central with no tag or review (QRun-IO/qbit-cdn#12,
QRun-IO/qqq#1121). hotfix/* branches now run test_only; a hotfix
release publishes from a reviewed vX.Y.Z tag through publish_release.
Adds .github/workflows/release-rc.yml (reusable QRun-IO/.github
qbit-release-rc): a manual dispatch, or merging a develop PR labelled
release:rc, merges develop into the release line and the release/* push
runs the existing CircleCI RC publish.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:32:49 -05:00
james.maes e071cca97e Merge pull request #7 from QRun-IO/feature/ga-only-release-tags
ci: publish only GA tags (vX.Y.Z), never RC tags
2026-10-12 16:08:05 -05:00
james.maesandClaude Opus 5.5 187a56825a ci: publish only GA tags (vX.Y.Z), never RC tags
An RC tag matched /v.*/ and the orb republished it as the next RC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:00:19 -05:00
james.maes 22fae54689 build(#921): update AssertJ test dependency to 3.27.7
Apply the independently reviewed one-pin test dependency update. Preserve revision, parent, CI and production source. Full verify passes; tracking: QRun-IO/qqq#921.
2026-10-01 20:56:19 -05:00
james.maes 90be19f913 fix: align first-party Apache-2.0 declarations
Refs QRun-IO/qqq#923.
2026-09-30 20:17:47 -05:00
james.maes 2db99e17ea ci: pin settings-safe QQQ orb 0.6.8
Use the reviewed immutable release tooling for QRun-IO/qqq-all#10.
2026-09-30 10:44:17 -05:00
james.maes d85789c16f ci: retain main tests and gate GA publication on tags
Refs QRun-IO/qqq-all#10. Preserve reviewed selectors; version pins remain separate and publication remains gated.
2026-09-28 20:59:35 -05:00
CircleCI d7a186f78b Bump version to 0.3.0-SNAPSHOT [skip ci] 2026-09-26 06:10:32 +00:00
james.maes 4ba088fb81 build: re-pin to qbit-build-parent 2.0.0 (#4)
Closes QRun-IO/qqq#766. Part of QRun-IO/qqq-all#1.
2026-09-26 01:08:42 -05:00
james.maes 5328b07af3 docs: update CLAUDE.md notes for the parent 2.0.0 re-pin
Part of QRun-IO/qqq-all#1
2026-09-25 20:44:05 -05:00
james.maes 7084430141 ci: keep package installs unattended on CircleCI machines
Adds the configure-package-install pre-step from qbit-quick-search so needrestart stops blocking install_java (QRun-IO/qqq-orb#11).

Part of QRun-IO/qqq-all#1
2026-09-25 20:43:51 -05:00
james.maes 746a3642d7 build: add opt-in qqq-snapshot profile
Imports qqq-bom-pom ${qqq.snapshot.version} (default 4.1.0-SNAPSHOT) ahead of the parent's BOM and carries the Central snapshots repository.

Part of QRun-IO/qqq-all#1
2026-09-25 20:43:09 -05:00
james.maes eef914d881 build: re-pin to qbit-build-parent 2.0.0 (qqq 4.0.0)
Drops the child qqq-bom-pom 0.40.0-SNAPSHOT import that shadowed the parent and moves the test app off the removed QInstance.setAuthentication.

Part of QRun-IO/qqq-all#1
2026-09-25 20:42:32 -05:00
james.maes 389e366f5e chore: merge main into develop
Brings main's Java 21 parent, 0.2.0 release, Apache-2.0 LICENSE and NOTICE, README, and release notes config onto develop.

Part of QRun-IO/qqq-all#1
2026-09-25 20:41:26 -05:00
James MaesandClaude Opus 4.8 0ee8782881 docs: add CLAUDE.md pointer to QQQ knowledge-base dossier + hub
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 05:31:17 -05:00
james.maesandClaude Opus 4.7 0f2ec03390 chore: add CODEOWNERS pointing to sole maintainer
Architecture audit pass 2026-05-21. Minimal CODEOWNERS file
making the implicit ownership explicit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 17:14:20 -05:00
Darin Kelkhoff 1374795e68 chore: update junit version to 6.0.2 to align with base 2026-02-18 16:00:14 -06:00
Darin Kelkhoff c3640b90d7 chore: update to kingsrook/qqq-orb@0.6 2026-02-18 15:51:32 -06:00
james.maes e8f0f2b88e chore: bump qqq to 0.40.0-SNAPSHOT 2026-01-10 21:05:16 -06:00
james.maes 33051ae997 chore(license): add AGPL-3.0 license with QRun-IO copyright 2025-12-25 19:32:44 -06:00
Darin Kelkhoff 6c261c7458 chore: Update qqq-orb version to 0.3.12, and version of this module to 0.1.3-snapshot, which it's supposed to be 2025-11-21 09:24:12 -06:00
CircleCI 68d25205d1 Bump version to 0.1.2 [skip ci] 2025-11-21 15:19:25 +00:00
Darin Kelkhoff 32720776f2 Merge pull request #1 from QRun-IO/feature/backend-activity-stats
Feature/backend activity stats
2025-11-21 09:17:59 -06:00
Darin Kelkhoff 3f8d113f61 chore: Update qqq version to develop build 2025-11-19 09:13:51 -06:00
Darin Kelkhoff 0e556e09e4 chore: update version (0.1.3-SNAPSHOT) and otherwise make work
also:
- downgrade qbit-build-parent - 1.4.0 doesn't seem to exist
- update qqq to build for this feature dev
- add sonatype repo for qqq snapshots
- remove rdbms (not used in here)
2025-11-04 16:57:39 -06:00
Darin Kelkhoff 5a44c5d1a8 test: Add some tests (ya, this qbit had none...) 2025-11-04 16:56:08 -06:00
Darin Kelkhoff 6d283a2ca4 refactor: update this qbit config and producer to follow current interfaces from qqq
also add config for includeBackendActivityStats
2025-11-04 16:55:44 -06:00
Darin Kelkhoff 77d8d90ebb feat(backendActivities): Update standard process traces to optionally also store backend activity stats
That is: number of queries, inserts, etc, per table, used within a process.

This feature does depend on the application server having the QueryStatManager started, and adding this package's StandardProcessTraceQueryStatConsumer to QueryStatManager's queryStatConsumers list, and this qbit being configured with includeBackendActivityStats = true
2025-11-04 16:55:07 -06:00
CircleCI 212fc98365 Bump version to 0.1.2 [skip ci] 2025-09-29 20:33:46 +00:00
james.maes 77328af459 ci(circleci): updated to latest production qqq orb - 0.3.8 2025-09-29 15:32:50 -05:00
james.maes 6dc7c12613 build(pom): updated snapshot version to 0.2.0 - looks like an admin error skipping 2025-09-29 08:44:18 -05:00
james.maes d29af04c03 chore(deps): Updated parent pom and qqq-orb to latest published versions 2025-09-18 12:14:29 -05:00
james.maes c50afb87ab chore: bump snapshot version after release 2025-09-16 16:35:33 -05:00
35 changed files with 2231 additions and 229 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
Acceptance: a fresh protected-branch build must publish through the scheduled trusted publisher and resolve from an empty Maven cache.
+3
View File
@@ -0,0 +1,3 @@
Organization-scoped CI qualification. Workflow implementation lives in platform-ci; enrollment lives in ci-policy.
Shared CI tooling visibility approved and public; qualify public consumer access.
+3 -3
View File
@@ -1,14 +1,14 @@
version: 2.1
setup: true
orbs:
qqq-orb: kingsrook/qqq-orb@0.7.13
qqq-orb: kingsrook/qqq-orb@0.8
workflows:
setup:
jobs:
- qqq-orb/generate_pipeline:
orb_version: 0.7.13
orb_version: '0.8'
filters:
branches:
ignore: automation/qbit-rc-pending
tags:
only: /v.*/
only: /^v\d+\.\d+\.\d+$/
+7
View File
@@ -0,0 +1,7 @@
name: build
on:
push:
jobs:
maven:
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@v1
secrets: inherit
+1
View File
@@ -0,0 +1 @@
* @KofTwentyTwo
+74
View File
@@ -0,0 +1,74 @@
name: Release candidate
'on':
workflow_dispatch:
inputs:
dry_run:
description: Plan a cut without changing release state
type: boolean
default: false
finalize:
description: Retry finalization of an existing cut; do not cut another RC
type: boolean
default: false
recover:
description: Recover the same published RC after a deployment timeout; never
reupload
type: boolean
default: false
circle_build:
description: Original Circle publisher build number (recovery only)
type: string
default: ''
merge_sha:
description: Existing cut merge_sha (finalize retry only)
type: string
default: ''
bump_sha:
description: Existing cut bump_sha (finalize retry only)
type: string
default: ''
branch:
description: Existing cut branch (finalize retry only)
type: string
default: ''
version:
description: Existing cut version (finalize retry only)
type: string
default: ''
pull_request:
types:
- closed
branches:
- develop
repository_dispatch:
types:
- qbit-rc-published
permissions:
contents: write
actions: read
checks: read
jobs:
release-rc:
if: github.event_name == 'workflow_dispatch' || github.event_name == 'repository_dispatch'
|| (github.event_name == 'pull_request' && github.event.pull_request.merged
== true && github.event.pull_request.base.ref == 'develop' && contains(github.event.pull_request.labels.*.name,
'release:rc'))
uses: QRun-IO/qqq-orb/.github/workflows/qbit-release-rc.yml@cc30f599e63c8e41629c99d23395bac1adb8bc44
with:
dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false
}}
finalize: ${{ github.event_name == 'repository_dispatch' || (github.event_name
== 'workflow_dispatch' && inputs.finalize) }}
recover: ${{ github.event_name == 'workflow_dispatch' && inputs.recover || false
}}
circle_build: ${{ github.event_name == 'workflow_dispatch' && inputs.circle_build
|| '' }}
merge_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.merge_sha
|| inputs.merge_sha || '' }}
bump_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.bump_sha
|| inputs.bump_sha || '' }}
branch: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch
|| inputs.branch || '' }}
version: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.version
|| inputs.version || '' }}
secrets: inherit
+5 -5
View File
@@ -1,16 +1,16 @@
profile: qbit
orb_version: 0.7.13
orb_version: '0.8'
default_branch: develop
java_version: '21'
browsers: true
api_checks: false
docs_only: false
release_on_main: true
release_tag_policy: v-prefix
test_branch_policy: feature-no-hotfix
release_on_main: false
release_tag_policy: stable
test_branch_policy: non-release
publish:
snapshot: true
release_candidate: true
release: true
hotfix: true
asciidoc: false
release_completion: true
+22
View File
@@ -0,0 +1,22 @@
# qbit-standard-process-trace
## Knowledge base
A reviewed dossier for this repo lives in the second-brain vault:
- Hub: `$SECOND_BRAIN_VAULT/knowledge/qqq/qqq-hub.md` — map of the whole QQQ knowledge base
- Dossier: `$SECOND_BRAIN_VAULT/knowledge/qqq/repos/qbit-standard-process-trace.md`
(purpose, API surface, data model, QBit contract usage, licensing state, v4.0 impact)
Reviewed at commit `0f2ec0339048` (branch `develop`, 2026-07-04).
Key facts worth knowing before editing:
- `main` was merged back into `develop` for QRun-IO/qqq#766, so `develop` has main's
Java 21 + Apache-2.0 LICENSE/NOTICE and the backend-activity-stats feature. Current
first-party pom, source-header and README license declarations also use Apache-2.0.
- The qqq version comes only from `qbit-build-parent` 2.0.0 (qqq 4.0.0); do not re-add a
`qqq-bom-pom` import. `mvn -B verify -Pqqq-snapshot` checks against qqq `4.1.0-SNAPSHOT`
(override with `-Dqqq.snapshot.version=...`).
- CHANGELOG.md text is copy-pasted from a different QBit; README code examples have drift
(nonexistent `producer.withTableMetaDataCustomizer`, table-name typos).
+2 -2
View File
@@ -24,7 +24,7 @@ This QBit provides automatic tracing for QQQ processes. Every execution is logge
### Prerequisites
- QQQ application (v0.35.0+)
- QQQ application (v4.1.0+)
- Database backend configured
### Installation
@@ -132,4 +132,4 @@ Stable and production-ready.
## License
Proprietary - QRun.IO
Apache-2.0 - See [LICENSE](LICENSE) and [NOTICE](NOTICE).
+23 -26
View File
@@ -6,27 +6,30 @@
~ contact@kingsrook.com
~ https://github.com/Kingsrook/
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Affero General Public License as
~ published by the Free Software Foundation, either version 3 of the
~ License, or (at your option) any later version.
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU Affero General Public License for more details.
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
~ 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.
-->
<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>
<properties>
<revision>0.3.0-SNAPSHOT</revision>
<qqq.version>4.1.0-RC.26</qqq.version>
</properties>
<parent>
<groupId>com.kingsrook</groupId>
<artifactId>qbit-build-parent</artifactId>
<version>1.5.1</version>
<version>2.2.1</version>
</parent>
<groupId>com.kingsrook.qbits</groupId>
@@ -38,10 +41,12 @@
<description>Standard process tracing module for the QBit framework, providing process trace tables and StandardProcessTracer implementation</description>
<url>https://github.com/Kingsrook/qbit-standard-process-trace</url>
<modelVersion>4.0.0</modelVersion>
<licenses>
<license>
<name>GNU Affero General Public License v3.0</name>
<url>https://www.gnu.org/licenses/agpl-3.0.html</url>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
@@ -63,24 +68,20 @@
<tag>HEAD</tag>
</scm>
<properties>
<revision>0.2.0</revision>
</properties>
<dependencyManagement>
<dependencies>
<!-- Published QQQ 4.1 core (minimum QQQ 4.1), imported ahead of the parent's BOM. -->
<dependency>
<groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-bom-pom</artifactId>
<version>0.27.9</version>
<version>${qqq.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.10.0</version>
<version>6.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -92,10 +93,6 @@
<groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-backend-core</artifactId>
</dependency>
<dependency>
<groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-backend-module-rdbms</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
@@ -105,7 +102,7 @@
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.27.3</version>
<version>3.27.7</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -5,33 +5,41 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace;
import com.kingsrook.qqq.backend.core.model.metadata.producers.MetaDataCustomizerInterface;
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitConfig;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
/*******************************************************************************
** ad-hoc Configuration data for this qbit.
** TODO - when qbit configs become formalized in qqq, adapt this qbit to that interface.
* Configuration data for this qbit.
*
*******************************************************************************/
public class StandardProcessTraceQBitConfig
public class StandardProcessTraceQBitConfig implements QBitConfig
{
private MetaDataCustomizerInterface<QTableMetaData> tableMetaDataCustomizer;
private String userIdPossibleValueSourceName;
private String userTableName;
private String userIdReferenceFieldName;
private boolean includeBackendActivityStats = false;
/*******************************************************************************
@@ -125,4 +133,82 @@ public class StandardProcessTraceQBitConfig
}
/*******************************************************************************
* Getter for includeBackendActivityStats
* @see #withIncludeBackendActivityStats(boolean)
*******************************************************************************/
public boolean getIncludeBackendActivityStats()
{
return (this.includeBackendActivityStats);
}
/*******************************************************************************
* Setter for includeBackendActivityStats
* @see #withIncludeBackendActivityStats(boolean)
*******************************************************************************/
public void setIncludeBackendActivityStats(boolean includeBackendActivityStats)
{
this.includeBackendActivityStats = includeBackendActivityStats;
}
/*******************************************************************************
* Fluent setter for includeBackendActivityStats
*
* @param includeBackendActivityStats
* indicate whether or not the ProcessTraceBackendActivityStats table should be
* included in the QInstance (and records should be built in that table under
* processTrace records).
* <p>Note that using this feature requires the QQQ QueryStatManager to be active
* in the application server.</p>
* @return this
*******************************************************************************/
public StandardProcessTraceQBitConfig withIncludeBackendActivityStats(boolean includeBackendActivityStats)
{
this.includeBackendActivityStats = includeBackendActivityStats;
return (this);
}
/*******************************************************************************
* Getter for tableMetaDataCustomizer
* @see #withTableMetaDataCustomizer(MetaDataCustomizerInterface)
*******************************************************************************/
public MetaDataCustomizerInterface<QTableMetaData> getTableMetaDataCustomizer()
{
return (this.tableMetaDataCustomizer);
}
/*******************************************************************************
* Setter for tableMetaDataCustomizer
* @see #withTableMetaDataCustomizer(MetaDataCustomizerInterface)
*******************************************************************************/
public void setTableMetaDataCustomizer(MetaDataCustomizerInterface<QTableMetaData> tableMetaDataCustomizer)
{
this.tableMetaDataCustomizer = tableMetaDataCustomizer;
}
/*******************************************************************************
* Fluent setter for tableMetaDataCustomizer
*
* @param tableMetaDataCustomizer
* table meta data customizer to be applied to all tables produced by this qbit.
* @return this
*******************************************************************************/
public StandardProcessTraceQBitConfig withTableMetaDataCustomizer(MetaDataCustomizerInterface<QTableMetaData> tableMetaDataCustomizer)
{
this.tableMetaDataCustomizer = tableMetaDataCustomizer;
return (this);
}
}
@@ -5,33 +5,32 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTrace;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceBackendActivityStats;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceSummaryLine;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceSummaryLineRecordInt;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerHelper;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
import com.kingsrook.qqq.backend.core.model.metadata.producers.MetaDataCustomizerInterface;
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitMetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
@@ -39,10 +38,9 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
/*******************************************************************************
**
*******************************************************************************/
public class StandardProcessTraceQBitProducer implements MetaDataProducerInterface<MetaDataProducerMultiOutput>
public class StandardProcessTraceQBitProducer implements QBitMetaDataProducer<StandardProcessTraceQBitConfig>
{
private StandardProcessTraceQBitConfig standardProcessTraceQBitConfig;
private MetaDataCustomizerInterface<QTableMetaData> tableMetaDataCustomizer;
private StandardProcessTraceQBitConfig standardProcessTraceQBitConfig;
@@ -51,35 +49,71 @@ public class StandardProcessTraceQBitProducer implements MetaDataProducerInterfa
***************************************************************************/
public static QAppSection getAppSection(QInstance qInstance)
{
return (new QAppSection().withName("processTrace")
QBitMetaData qBitMetaData = qInstance.getQBits().get(new StandardProcessTraceQBitProducer().getQBitMetaData().getName());
QAppSection section = new QAppSection().withName("processTrace")
.withTable(ProcessTrace.TABLE_NAME)
.withTable(ProcessTraceSummaryLine.TABLE_NAME)
.withTable(ProcessTraceSummaryLineRecordInt.TABLE_NAME));
.withTable(ProcessTraceSummaryLineRecordInt.TABLE_NAME);
if(qBitMetaData != null && qBitMetaData.getConfig() instanceof StandardProcessTraceQBitConfig config)
{
if(config.getIncludeBackendActivityStats())
{
section.withTable(ProcessTraceBackendActivityStats.TABLE_NAME);
}
}
return (section);
}
/***************************************************************************
**
*
***************************************************************************/
@Override
public MetaDataProducerMultiOutput produce(QInstance qInstance) throws QException
public StandardProcessTraceQBitConfig getQBitConfig()
{
MetaDataProducerHelper.processAllMetaDataProducersInPackage(qInstance, ProcessTrace.class.getPackageName(), tableMetaDataCustomizer);
MetaDataProducerHelper.processAllMetaDataProducersInPackage(qInstance, getClass().getPackageName() + ".metadata", tableMetaDataCustomizer);
return (standardProcessTraceQBitConfig);
}
//////////////////////////////////////////////////////////////////////////////////////
// set the possible value source on the processTrace.userId field, if so configured //
//////////////////////////////////////////////////////////////////////////////////////
if(standardProcessTraceQBitConfig != null && StringUtils.hasContent(standardProcessTraceQBitConfig.getUserIdPossibleValueSourceName()))
/***************************************************************************
*
***************************************************************************/
@Override
public QBitMetaData getQBitMetaData()
{
QBitMetaData qBitMetaData = new QBitMetaData()
.withGroupId("com.kingsrook.qbits")
.withArtifactId("standard-process-trace")
.withVersion("0.1.3")
.withNamespace(getNamespace())
.withConfig(getQBitConfig());
return qBitMetaData;
}
/***************************************************************************
*
***************************************************************************/
@Override
public void postProduceActions(MetaDataProducerMultiOutput metaDataProducerMultiOutput, QInstance qInstance) throws QException
{
if(standardProcessTraceQBitConfig != null)
{
qInstance.getTable(ProcessTrace.TABLE_NAME).getField("userId").setPossibleValueSourceName(standardProcessTraceQBitConfig.getUserIdPossibleValueSourceName());
//////////////////////////////////////////////////////////////////////////////////////
// set the possible value source on the processTrace.userId field, if so configured //
//////////////////////////////////////////////////////////////////////////////////////
if(StringUtils.hasContent(standardProcessTraceQBitConfig.getUserIdPossibleValueSourceName()))
{
metaDataProducerMultiOutput.get(QTableMetaData.class, ProcessTrace.TABLE_NAME).getField("userId").setPossibleValueSourceName(standardProcessTraceQBitConfig.getUserIdPossibleValueSourceName());
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// we actually called MetaDataProducerHelper ourselves, so, don't have anything to return here... //
////////////////////////////////////////////////////////////////////////////////////////////////////
return new MetaDataProducerMultiOutput();
}
@@ -113,35 +147,4 @@ public class StandardProcessTraceQBitProducer implements MetaDataProducerInterfa
return (this);
}
/*******************************************************************************
** Getter for tableMetaDataCustomizer
*******************************************************************************/
public MetaDataCustomizerInterface<QTableMetaData> getTableMetaDataCustomizer()
{
return (this.tableMetaDataCustomizer);
}
/*******************************************************************************
** Setter for tableMetaDataCustomizer
*******************************************************************************/
public void setTableMetaDataCustomizer(MetaDataCustomizerInterface<QTableMetaData> tableMetaDataCustomizer)
{
this.tableMetaDataCustomizer = tableMetaDataCustomizer;
}
/*******************************************************************************
** Fluent setter for tableMetaDataCustomizer
*******************************************************************************/
public StandardProcessTraceQBitProducer withTableMetaDataCustomizer(MetaDataCustomizerInterface<QTableMetaData> tableMetaDataCustomizer)
{
this.tableMetaDataCustomizer = tableMetaDataCustomizer;
return (this);
}
}
@@ -0,0 +1,61 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.metadata;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceBackendActivityStatsAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
/*******************************************************************************
* Meta Data Producer for ProcessTraceBackendActivityStatsAction
* Note: not done via annotations, so it can be disabled via config
*******************************************************************************/
public class ProcessTraceBackendActivityStatsActionMetaDataProducer extends MetaDataProducer<QPossibleValueSource>
{
public static final String NAME = ProcessTraceBackendActivityStatsAction.NAME;
/***************************************************************************
*
***************************************************************************/
@Override
public boolean isEnabled()
{
return (new ProcessTraceBackendActivityStatsMetaDataProducer().isEnabled());
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public QPossibleValueSource produce(QInstance qInstance) throws QException
{
return (QPossibleValueSource.newForEnum(NAME, ProcessTraceBackendActivityStatsAction.values()));
}
}
@@ -0,0 +1,92 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.metadata;
import java.util.List;
import com.kingsrook.qbits.standardprocesstrace.StandardProcessTraceQBitConfig;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTrace;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceBackendActivityStats;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitConfig;
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitProductionContext;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.SectionFactory;
/*******************************************************************************
* Meta Data Producer for ProcessTraceBackendActivityStats
* Note: not done via annotations, so it can be disabled via config
*******************************************************************************/
public class ProcessTraceBackendActivityStatsMetaDataProducer extends MetaDataProducer<QTableMetaData>
{
/***************************************************************************
*
***************************************************************************/
@Override
public boolean isEnabled()
{
QBitConfig qBitConfig = QBitProductionContext.peekQBitConfig();
if(qBitConfig instanceof StandardProcessTraceQBitConfig config)
{
if(!config.getIncludeBackendActivityStats())
{
return (false);
}
}
return (true);
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public QTableMetaData produce(QInstance qInstance) throws QException
{
QTableMetaData table = new QTableMetaData()
.withName(ProcessTraceBackendActivityStats.TABLE_NAME)
.withIcon(new QIcon().withName("query_stats"))
.withRecordLabelFormat("%s %s %s %s")
.withRecordLabelFields("processTraceId", "qqqBackendId", "qqqTableId", "actionName")
.withPrimaryKeyField("id")
.withFieldsFromEntity(ProcessTraceBackendActivityStats.class)
.withSection(SectionFactory.defaultT1("id", "processTraceId", "qqqBackendId", "qqqTableId", "actionName"))
.withSection(SectionFactory.defaultT2("callCount", "recordCount", "runtimeMillis"))
.withExposedJoin(new ExposedJoin().withLabel("Process Trace").withJoinPath(List.of(ProcessTraceJoinBackendActivityStatsMetaDataProducer.NAME)).withJoinTable(ProcessTrace.TABLE_NAME));
table.withoutCapabilities(Capability.allWriteCapabilities());
QInstanceEnricher.setInferredFieldBackendNames(table);
return (table);
}
}
@@ -0,0 +1,70 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.metadata;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTrace;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceBackendActivityStats;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
/*******************************************************************************
* Meta Data Producer for ProcessTraceJoinBackendActivityStats
* Note: not done via annotations, so it can be disabled via config
*******************************************************************************/
public class ProcessTraceJoinBackendActivityStatsMetaDataProducer extends MetaDataProducer<QJoinMetaData>
{
public static final String NAME = "ProcessTraceJoinBackendActivityStats";
/***************************************************************************
*
***************************************************************************/
@Override
public boolean isEnabled()
{
return (new ProcessTraceBackendActivityStatsMetaDataProducer().isEnabled());
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public QJoinMetaData produce(QInstance qInstance) throws QException
{
return (new QJoinMetaData()
.withName(NAME)
.withLeftTable(ProcessTrace.TABLE_NAME)
.withRightTable(ProcessTraceBackendActivityStats.TABLE_NAME)
.withType(JoinType.ONE_TO_MANY)
.withJoinOn(new JoinOn("id", "processTraceId"))
);
}
}
@@ -0,0 +1,71 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.metadata;
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.ChildRecordListRenderer;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducer;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitProductionContext;
/*******************************************************************************
* Meta Data Producer for ProcessTraceJoinBackendActivityStats
* Note: not done via annotations, so it can be disabled via config
*******************************************************************************/
public class ProcessTraceJoinBackendActivityStatsWidgetMetaDataProducer extends MetaDataProducer<QWidgetMetaData>
{
public static final String NAME = "ProcessTraceJoinBackendActivityStatsWidget";
/***************************************************************************
*
***************************************************************************/
@Override
public boolean isEnabled()
{
return (new ProcessTraceBackendActivityStatsMetaDataProducer().isEnabled());
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public QWidgetMetaData produce(QInstance qInstance) throws QException
{
MetaDataProducerMultiOutput metaDataProducerMultiOutput = QBitProductionContext.peekMetaDataProducerMultiOutput();
QJoinMetaData joinMetaData = metaDataProducerMultiOutput.get(QJoinMetaData.class, ProcessTraceJoinBackendActivityStatsMetaDataProducer.NAME);
return ChildRecordListRenderer.widgetMetaDataBuilder(joinMetaData)
.withName(NAME)
.withLabel("Backend Activity Stats")
.withMaxRows(250)
.getWidgetMetaData();
}
}
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.metadata;
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.metadata;
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.model;
@@ -24,6 +23,9 @@ package com.kingsrook.qbits.standardprocesstrace.model;
import java.time.Instant;
import java.util.List;
import com.kingsrook.qbits.standardprocesstrace.metadata.ProcessTraceBackendActivityStatsMetaDataProducer;
import com.kingsrook.qbits.standardprocesstrace.metadata.ProcessTraceJoinBackendActivityStatsMetaDataProducer;
import com.kingsrook.qbits.standardprocesstrace.metadata.ProcessTraceJoinBackendActivityStatsWidgetMetaDataProducer;
import com.kingsrook.qbits.standardprocesstrace.metadata.ProcessTraceJoinKeyRecordQQQTableMetaDataProducer;
import com.kingsrook.qbits.standardprocesstrace.metadata.ProcessTraceJoinQQQProcessMetaDataProducer;
import com.kingsrook.qbits.standardprocesstrace.utils.ProcessTraceTableCustomizer;
@@ -66,7 +68,7 @@ import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
childTableEntityClass = ProcessTraceSummaryLine.class,
joinFieldName = "processTraceSummaryLineId",
childJoin = @ChildJoin(enabled = true),
childRecordListWidget = @ChildRecordListWidget(label = "Summary Lines", enabled = true, maxRows = 250))
childRecordListWidget = @ChildRecordListWidget(label = "Summary Lines", enabled = true, maxRows = 250)),
}
)
public class ProcessTrace extends QRecordEntity
@@ -87,7 +89,7 @@ public class ProcessTrace extends QRecordEntity
@Override
public QTableMetaData customizeMetaData(QInstance qInstance, QTableMetaData table) throws QException
{
String childJoinName = QJoinMetaData.makeInferredJoinName(TABLE_NAME, ProcessTraceSummaryLine.TABLE_NAME);
String summaryLinesChildJoinName = QJoinMetaData.makeInferredJoinName(TABLE_NAME, ProcessTraceSummaryLine.TABLE_NAME);
table
.withUniqueKey(new UniqueKey("processUUID"))
@@ -96,14 +98,25 @@ public class ProcessTrace extends QRecordEntity
.withRecordLabelFields("qqqProcessId", "processUUID")
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "qqqProcessId", "processUUID", "userId")))
.withSection(new QFieldSection("data", new QIcon().withName("text_snippet"), Tier.T2, List.of("startTimestamp", "endTimestamp", "runtimeMillis", "keyRecordQqqTableId", "keyRecordId", "recordCount", "exceptionMessage")))
.withSection(new QFieldSection("summaryLines", new QIcon().withName("horizontal_rule"), Tier.T2).withWidgetName(childJoinName))
.withExposedJoin(new ExposedJoin().withLabel("Summary Lines").withJoinPath(List.of(childJoinName)).withJoinTable(ProcessTraceSummaryLine.TABLE_NAME))
.withSection(new QFieldSection("summaryLines", new QIcon().withName("horizontal_rule"), Tier.T2).withWidgetName(summaryLinesChildJoinName))
.withExposedJoin(new ExposedJoin().withLabel("Summary Lines").withJoinPath(List.of(summaryLinesChildJoinName)).withJoinTable(ProcessTraceSummaryLine.TABLE_NAME))
.withExposedJoin(new ExposedJoin().withLabel("Key Record Table").withJoinPath(List.of(ProcessTraceJoinKeyRecordQQQTableMetaDataProducer.NAME)).withJoinTable(QQQTable.TABLE_NAME))
.withExposedJoin(new ExposedJoin().withLabel("Process").withJoinPath(List.of(ProcessTraceJoinQQQProcessMetaDataProducer.NAME)).withJoinTable(QQQProcess.TABLE_NAME));
table.withCustomizer(TableCustomizers.POST_QUERY_RECORD, new QCodeReference(ProcessTraceTableCustomizer.class));
table.getField("keyRecordId").withFieldAdornment(new FieldAdornment(AdornmentType.LINK).withValue(AdornmentType.LinkValues.TO_RECORD_FROM_TABLE_DYNAMIC, true));
if(new ProcessTraceBackendActivityStatsMetaDataProducer().isEnabled())
{
table.getSections().add(new QFieldSection("backendActivityStats", new QIcon().withName("query_stats"), Tier.T2)
.withWidgetName(ProcessTraceJoinBackendActivityStatsWidgetMetaDataProducer.NAME));
table.withExposedJoin(new ExposedJoin()
.withLabel("Backend Activity Stats")
.withJoinPath(List.of(ProcessTraceJoinBackendActivityStatsMetaDataProducer.NAME))
.withJoinTable(ProcessTraceBackendActivityStats.TABLE_NAME));
}
return (table);
}
}
@@ -0,0 +1,348 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.model;
import com.kingsrook.qqq.backend.core.model.backends.QQQBackend;
import com.kingsrook.qqq.backend.core.model.data.QField;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
/*******************************************************************************
* QRecord Entity for ProcessTraceBackendActivityStats table
* Note: not using meta-data producing annotations, so it can be disabled via config
*******************************************************************************/
public class ProcessTraceBackendActivityStats extends QRecordEntity
{
public static final String TABLE_NAME = "processTraceBackendActivityStats";
@QField(isEditable = false, isPrimaryKey = true)
private Long id;
@QField(possibleValueSourceName = ProcessTrace.TABLE_NAME)
private Long processTraceId;
@QField(label = "Backend", possibleValueSourceName = QQQBackend.TABLE_NAME)
private Integer qqqBackendId;
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
private Integer qqqTableId;
@QField(label = "Action", possibleValueSourceName = ProcessTraceBackendActivityStatsAction.NAME)
private String actionName;
@QField(displayFormat = DisplayFormat.COMMAS)
private Integer callCount;
@QField(displayFormat = DisplayFormat.COMMAS)
private Integer recordCount;
@QField(displayFormat = DisplayFormat.COMMAS)
private Integer runtimeMillis;
/*******************************************************************************
** Default constructor
*******************************************************************************/
public ProcessTraceBackendActivityStats()
{
}
/*******************************************************************************
** Constructor that takes a QRecord
*******************************************************************************/
public ProcessTraceBackendActivityStats(QRecord record)
{
populateFromQRecord(record);
}
/*******************************************************************************
* Getter for id
* @see #withId(Long)
*******************************************************************************/
public Long getId()
{
return (this.id);
}
/*******************************************************************************
* Setter for id
* @see #withId(Long)
*******************************************************************************/
public void setId(Long id)
{
this.id = id;
}
/*******************************************************************************
* Fluent setter for id
*******************************************************************************/
public ProcessTraceBackendActivityStats withId(Long id)
{
this.id = id;
return (this);
}
/*******************************************************************************
* Getter for processTraceId
* @see #withProcessTraceId(Long)
*******************************************************************************/
public Long getProcessTraceId()
{
return (this.processTraceId);
}
/*******************************************************************************
* Setter for processTraceId
* @see #withProcessTraceId(Long)
*******************************************************************************/
public void setProcessTraceId(Long processTraceId)
{
this.processTraceId = processTraceId;
}
/*******************************************************************************
* Fluent setter for processTraceId
*******************************************************************************/
public ProcessTraceBackendActivityStats withProcessTraceId(Long processTraceId)
{
this.processTraceId = processTraceId;
return (this);
}
/*******************************************************************************
* Getter for actionName
* @see #withActionName(String)
*******************************************************************************/
public String getActionName()
{
return (this.actionName);
}
/*******************************************************************************
* Setter for actionName
* @see #withActionName(String)
*******************************************************************************/
public void setActionName(String actionName)
{
this.actionName = actionName;
}
/*******************************************************************************
* Fluent setter for actionName
*******************************************************************************/
public ProcessTraceBackendActivityStats withActionName(String actionName)
{
this.actionName = actionName;
return (this);
}
/*******************************************************************************
* Getter for callCount
* @see #withCallCount(Integer)
*******************************************************************************/
public Integer getCallCount()
{
return (this.callCount);
}
/*******************************************************************************
* Setter for callCount
* @see #withCallCount(Integer)
*******************************************************************************/
public void setCallCount(Integer callCount)
{
this.callCount = callCount;
}
/*******************************************************************************
* Fluent setter for callCount
*******************************************************************************/
public ProcessTraceBackendActivityStats withCallCount(Integer callCount)
{
this.callCount = callCount;
return (this);
}
/*******************************************************************************
* Getter for recordCount
* @see #withRecordCount(Integer)
*******************************************************************************/
public Integer getRecordCount()
{
return (this.recordCount);
}
/*******************************************************************************
* Setter for recordCount
* @see #withRecordCount(Integer)
*******************************************************************************/
public void setRecordCount(Integer recordCount)
{
this.recordCount = recordCount;
}
/*******************************************************************************
* Fluent setter for recordCount
*******************************************************************************/
public ProcessTraceBackendActivityStats withRecordCount(Integer recordCount)
{
this.recordCount = recordCount;
return (this);
}
/*******************************************************************************
* Getter for runtimeMillis
* @see #withRuntimeMillis(Integer)
*******************************************************************************/
public Integer getRuntimeMillis()
{
return (this.runtimeMillis);
}
/*******************************************************************************
* Setter for runtimeMillis
* @see #withRuntimeMillis(Integer)
*******************************************************************************/
public void setRuntimeMillis(Integer runtimeMillis)
{
this.runtimeMillis = runtimeMillis;
}
/*******************************************************************************
* Fluent setter for runtimeMillis
*******************************************************************************/
public ProcessTraceBackendActivityStats withRuntimeMillis(Integer runtimeMillis)
{
this.runtimeMillis = runtimeMillis;
return (this);
}
/*******************************************************************************
* Getter for qqqBackendId
* @see #withQqqBackendId(Integer)
*******************************************************************************/
public Integer getQqqBackendId()
{
return (this.qqqBackendId);
}
/*******************************************************************************
* Setter for qqqBackendId
* @see #withQqqBackendId(Integer)
*******************************************************************************/
public void setQqqBackendId(Integer qqqBackendId)
{
this.qqqBackendId = qqqBackendId;
}
/*******************************************************************************
* Fluent setter for qqqBackendId
*******************************************************************************/
public ProcessTraceBackendActivityStats withQqqBackendId(Integer qqqBackendId)
{
this.qqqBackendId = qqqBackendId;
return (this);
}
/*******************************************************************************
* Getter for qqqTableId
* @see #withQqqTableId(Integer)
*******************************************************************************/
public Integer getQqqTableId()
{
return (this.qqqTableId);
}
/*******************************************************************************
* Setter for qqqTableId
* @see #withQqqTableId(Integer)
*******************************************************************************/
public void setQqqTableId(Integer qqqTableId)
{
this.qqqTableId = qqqTableId;
}
/*******************************************************************************
* Fluent setter for qqqTableId
*******************************************************************************/
public ProcessTraceBackendActivityStats withQqqTableId(Integer qqqTableId)
{
this.qqqTableId = qqqTableId;
return (this);
}
}
@@ -0,0 +1,131 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.model;
import java.util.Objects;
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
import com.kingsrook.qqq.backend.core.model.actions.audits.AuditDetailAccumulator;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum;
/*******************************************************************************
** ProcessTraceBackendActivityStatsAction - possible value enum
*******************************************************************************/
public enum ProcessTraceBackendActivityStatsAction implements PossibleValueEnum<String>
{
INSERT(InsertAction.class.getSimpleName(), "Insert"),
UPDATE(UpdateAction.class.getSimpleName(), "Update"),
DELETE(DeleteAction.class.getSimpleName(), "Delete"),
QUERY(QueryAction.class.getSimpleName(), "Query"),
GET(GetAction.class.getSimpleName(), "Get"),
COUNT(CountAction.class.getSimpleName(), "Count"),
AGGREGATE(AuditDetailAccumulator.class.getSimpleName(), "Aggregate");
private final String id;
private final String label;
public static final String NAME = "ProcessTraceBackendActivityStatsAction";
/*******************************************************************************
**
*******************************************************************************/
ProcessTraceBackendActivityStatsAction(String id, String label)
{
this.id = id;
this.label = label;
}
/*******************************************************************************
** Get instance by id
**
*******************************************************************************/
public static ProcessTraceBackendActivityStatsAction getById(String id)
{
if(id == null)
{
return (null);
}
for(ProcessTraceBackendActivityStatsAction value : ProcessTraceBackendActivityStatsAction.values())
{
if(Objects.equals(value.id, id))
{
return (value);
}
}
return (null);
}
/*******************************************************************************
** Getter for id
**
*******************************************************************************/
public String getId()
{
return id;
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getPossibleValueId()
{
return (getId());
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getPossibleValueLabel()
{
return (getLabel());
}
}
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.model;
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.model;
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* 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.
*/
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* 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.
*/
@@ -0,0 +1,297 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.utils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceBackendActivityStats;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.backends.QQQBackendTableManager;
import com.kingsrook.qqq.backend.core.model.tables.QQQTableTableManager;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.PrefixedDefaultThreadFactory;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
/*******************************************************************************
* Singleton that collects QueryStats, associated with a processTraceId.
*
* <p>Runs a scheduled executor thread to clean itself up - e.g., in case processes
* leak (e.g., started in a frontend but exited before final step). Configured by
* {@link #setCleanupIntervalMillis(long)} and {@link #setCleanupMaxAge(long)}</p>
*******************************************************************************/
public class ProcessTraceBackendActivityStatsManager
{
private static final QLogger LOG = QLogger.getLogger(ProcessTraceBackendActivityStatsManager.class);
private static ProcessTraceBackendActivityStatsManager processTraceBackendActivityStatsManager = null;
private Map<Long, ProcessStats> stats = Collections.synchronizedMap(new HashMap<>());
private long CLEANUP_INTERVAL_MILLIS = 60 * 60 * 1000;
private long CLEANUP_MAX_AGE = 12 * 60 * 60 * 1000;
private ScheduledExecutorService scheduledExecutorService;
/***************************************************************************
* container for all data collected for a particular process trace.
*
* stores start milliseconds (for cleanup), plus map of keys to stats.
***************************************************************************/
private record ProcessStats(long startMillis, Map<Key, Stats> stats)
{
/*******************************************************************************
** Constructor that uses default values
**
*******************************************************************************/
public ProcessStats()
{
this(System.currentTimeMillis(), new LinkedHashMap<>());
}
}
/***************************************************************************
* what backend/table/action a set of stats are associated with.
*
* e.g., memoryBackend, personTable, insertAction
***************************************************************************/
public record Key(String backendName, String tableName, String actionName) implements Serializable
{
}
/***************************************************************************
* the stats collected for individual keys.
***************************************************************************/
private record Stats(Integer calls, Integer records, Integer millis)
{
}
/*******************************************************************************
** Singleton constructor
*******************************************************************************/
private ProcessTraceBackendActivityStatsManager()
{
scheduleCleanupJob();
}
/***************************************************************************
*
***************************************************************************/
private void scheduleCleanupJob()
{
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new PrefixedDefaultThreadFactory("ProcessTraceBackendActivityStatsManager-cleanup"));
scheduledExecutorService.scheduleAtFixedRate(this::cleanup, CLEANUP_INTERVAL_MILLIS, CLEANUP_INTERVAL_MILLIS, TimeUnit.MILLISECONDS);
}
/*******************************************************************************
** Singleton accessor
*******************************************************************************/
public static ProcessTraceBackendActivityStatsManager getInstance()
{
if(processTraceBackendActivityStatsManager == null)
{
processTraceBackendActivityStatsManager = new ProcessTraceBackendActivityStatsManager();
}
return (processTraceBackendActivityStatsManager);
}
/***************************************************************************
* shutdown the scheduled job. also clear stats.
***************************************************************************/
public void shutdown()
{
scheduledExecutorService.shutdown();
stats.clear();
}
/***************************************************************************
* initialize storage for stats for a specific process trace id
***************************************************************************/
public void initProcess(Long processTraceId)
{
stats.put(processTraceId, new ProcessStats());
}
/***************************************************************************
* clean up stat entries for processes that were originally created before
* CLEANUP_MAX_AGE (per {@link #setCleanupMaxAge(long)} milliseconds ago.
***************************************************************************/
private void cleanup()
{
long limitStartMillis = System.currentTimeMillis() - CLEANUP_MAX_AGE;
LOG.info("Starting cleanup of ProcessTraceBackendActivityStatsManager", logPair("size", stats.size()));
stats.entrySet().removeIf(entry -> entry.getValue().startMillis < limitStartMillis);
LOG.info("Finished cleanup of ProcessTraceBackendActivityStatsManager", logPair("size", stats.size()));
}
/***************************************************************************
* when storing a processTrace (e.g., after the process has finished running),
* convert the stats stored in this object to a list of {@link ProcessTraceBackendActivityStats}
* entities. Also remove the stats from this object's internal storage.
***************************************************************************/
public List<ProcessTraceBackendActivityStats> getAndRemoveStats(Long processTraceId) throws QException
{
List<ProcessTraceBackendActivityStats> rs = new ArrayList<>();
ProcessStats processStats = stats.remove(processTraceId);
if(processStats != null)
{
for(Map.Entry<Key, Stats> entry : CollectionUtils.nonNullMap(processStats.stats()).entrySet())
{
rs.add(new ProcessTraceBackendActivityStats()
.withProcessTraceId(processTraceId)
.withQqqBackendId(getBackendId(entry.getKey().backendName()))
.withQqqTableId(getTableId(entry.getKey().tableName()))
.withActionName(entry.getKey().actionName())
.withCallCount(entry.getValue().calls())
.withRecordCount(entry.getValue().records())
.withRuntimeMillis(entry.getValue().millis())
);
}
}
return (rs);
}
/***************************************************************************
*
***************************************************************************/
private Integer getBackendId(String backendName)
{
if(!StringUtils.hasContent(backendName))
{
return (null);
}
try
{
return QQQBackendTableManager.getQQQBackendId(QContext.getQInstance(), backendName);
}
catch(Exception e)
{
return (null);
}
}
/***************************************************************************
*
***************************************************************************/
private Integer getTableId(String tableName)
{
if(!StringUtils.hasContent(tableName))
{
return (null);
}
try
{
return QQQTableTableManager.getQQQTableId(QContext.getQInstance(), tableName);
}
catch(Exception e)
{
return (null);
}
}
/***************************************************************************
* increment the stat values for a given process.
*
* @param key the backend, table, and action
* @param addCalls the number of calls to add (typically 1 at a time?)
* @param addRecords the number of records to add
* @param addMillis the number of milliseconds of runtime to add
***************************************************************************/
public void add(Long processTraceId, Key key, Integer addCalls, Integer addRecords, Integer addMillis)
{
ProcessStats processStats = stats.computeIfAbsent(processTraceId, k -> new ProcessStats());
Map<Key, Stats> countsForProcess = processStats.stats();
if(countsForProcess != null)
{
Stats statsForKey = countsForProcess.computeIfAbsent(key, (k) -> new Stats(0, 0, 0));
countsForProcess.put(key, new Stats(statsForKey.calls + addCalls, statsForKey.records + addRecords, statsForKey.millis + addMillis));
}
}
/***************************************************************************
* sets the cleanup job's scheduled interval. Also reschedules the job.
*
* @param cleanupIntervalMillis milliseconds between runs of the cleanup job
***************************************************************************/
public void setCleanupIntervalMillis(long cleanupIntervalMillis)
{
CLEANUP_INTERVAL_MILLIS = cleanupIntervalMillis;
scheduledExecutorService.shutdown();
scheduleCleanupJob();
}
/***************************************************************************
* @param cleanupMaxAge max age (in millis) that a process is allowed to be
* before it gets cleaned up when a cleanup job runs.
***************************************************************************/
public void setCleanupMaxAge(long cleanupMaxAge)
{
CLEANUP_MAX_AGE = cleanupMaxAge;
}
}
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.utils;
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.utils;
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.utils;
@@ -0,0 +1,66 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.utils;
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatConsumerInterface;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
/*******************************************************************************
* Standard Process Trace's implementation of a QueryStat consumer.
*
* <p>That is, to accumulate them in the {@link ProcessTraceBackendActivityStatsManager},
* associated with the processTraceId in the user's session, so they can be stored
* when the process trace is stored.</p>
*******************************************************************************/
public class StandardProcessTraceQueryStatConsumer implements QueryStatConsumerInterface
{
private static final QLogger LOG = QLogger.getLogger(StandardProcessTraceQueryStatConsumer.class);
/***************************************************************************
*
***************************************************************************/
@Override
public void accept(QueryStat queryStat)
{
try
{
String processTraceId = QContext.getQSession().getValue(StandardProcessTracer.PROCESS_TRACE_ID_SESSION_KEY);
if(processTraceId == null)
{
return;
}
String backendName = QContext.getQInstance().getBackendForTable(queryStat.getTableName()).getName();
String backendAction = queryStat.getBackendAction();
ProcessTraceBackendActivityStatsManager.Key key = new ProcessTraceBackendActivityStatsManager.Key(backendName, queryStat.getTableName(), backendAction);
ProcessTraceBackendActivityStatsManager.getInstance().add(Long.valueOf(processTraceId), key, 1, queryStat.getRecordCount(), queryStat.getFirstResultMillis());
}
catch(Exception e)
{
LOG.warn("Error accepting query stats for process trace", e);
}
}
}
@@ -5,18 +5,17 @@
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* https://www.apache.org/licenses/LICENSE-2.0
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
* 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.
*/
@@ -30,6 +29,7 @@ import java.util.List;
import java.util.Map;
import com.kingsrook.qbits.standardprocesstrace.StandardProcessTraceQBitConfig;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTrace;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceBackendActivityStats;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceSummaryLine;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceSummaryLineRecordInt;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
@@ -75,6 +75,8 @@ public class StandardProcessTracer implements ProcessTracerInterface
private static StandardProcessTraceQBitConfig standardProcessTraceQBitConfig;
public static final String PROCESS_TRACE_ID_SESSION_KEY = StandardProcessTracer.class.getName() + ".processTraceId";
private Instant startTime;
private Long processTraceId;
@@ -98,6 +100,17 @@ public class StandardProcessTracer implements ProcessTracerInterface
.withProcessUUID(runProcessInput.getProcessUUID())));
processTraceId = insertOutput.getRecords().get(0).getValueLong("id");
////////////////////////////////////////////////////////////////////////////////////
// store the processTraceId in the session. this will be used by the //
// ProcessTraceQueryStatConsumer to associated QueryStats with this process trace //
////////////////////////////////////////////////////////////////////////////////////
if(QContext.getQSession() != null)
{
QContext.getQSession().setValue(PROCESS_TRACE_ID_SESSION_KEY, String.valueOf(processTraceId));
}
ProcessTraceBackendActivityStatsManager.getInstance().initProcess(processTraceId);
}
catch(QException e)
{
@@ -113,9 +126,29 @@ public class StandardProcessTracer implements ProcessTracerInterface
@Override
public void handleProcessResume(RunProcessInput runProcessInput)
{
/////////////////////////////////
// noop in this implementation //
/////////////////////////////////
try
{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// if a user is resuming a process, e.g., from a frontend, then we want to put that processTraceId in their session //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(processTraceId == null)
{
QRecord processTrace = GetAction.execute(ProcessTrace.TABLE_NAME, Map.of("processUUID", runProcessInput.getProcessUUID()));
if(processTrace != null)
{
processTraceId = processTrace.getValueLong("id");
}
}
if(processTraceId != null && QContext.getQSession() != null)
{
QContext.getQSession().setValue(PROCESS_TRACE_ID_SESSION_KEY, String.valueOf(processTraceId));
}
}
catch(Exception e)
{
LOG.warn("Error resuming process trace", e, logPair("processUUID", () -> runProcessInput.getProcessUUID()));
}
}
@@ -328,12 +361,28 @@ public class StandardProcessTracer implements ProcessTracerInterface
new InsertAction().execute(new InsertInput(ProcessTraceSummaryLine.TABLE_NAME).withRecordEntities(summaryLines));
}
//////////////////////////////////////////
// build and store backend stat records //
//////////////////////////////////////////
if(QContext.getQInstance().getTable(ProcessTraceBackendActivityStats.TABLE_NAME) != null)
{
List<ProcessTraceBackendActivityStats> stats = ProcessTraceBackendActivityStatsManager.getInstance().getAndRemoveStats(processTraceId);
new InsertAction().execute(new InsertInput(ProcessTraceBackendActivityStats.TABLE_NAME).withRecordEntities(stats));
}
}
}
catch(Exception e)
{
LOG.warn("Error completing store of processTrace records.", e);
}
finally
{
if(QContext.getQSession() != null)
{
QContext.getQSession().removeValue(PROCESS_TRACE_ID_SESSION_KEY);
}
}
}
@@ -0,0 +1,85 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace;
import java.util.List;
import com.kingsrook.qbits.standardprocesstrace.utils.StandardProcessTraceQueryStatConsumer;
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatManager;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
/*******************************************************************************
* base class for tests for this QBit.
*******************************************************************************/
public class BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
void baseBeforeEach() throws Exception
{
QInstance qInstance = defineQInstance();
new QInstanceValidator().validate(qInstance);
QContext.init(qInstance, new QSystemUserSession());
QueryStatManager queryStatManager = QueryStatManager.getInstance();
queryStatManager.start(qInstance, () -> new QSystemUserSession());
queryStatManager.setQueryStatConsumers(List.of(new StandardProcessTraceQueryStatConsumer()));
MemoryRecordStore.fullReset();
}
/*******************************************************************************
**
*******************************************************************************/
@AfterEach
void afterEach()
{
MemoryRecordStore.fullReset();
QueryStatManager.getInstance().stop();
}
/***************************************************************************
**
***************************************************************************/
private QInstance defineQInstance() throws QException
{
return new StandardProcessTraceTestApplication().defineQInstance();
}
}
@@ -0,0 +1,233 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.kingsrook.qbits.standardprocesstrace.utils.StandardProcessTracer;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.instances.AbstractQQQApplication;
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.actions.processes.Status;
import com.kingsrook.qqq.backend.core.model.backends.QQQBackendsMetaDataProvider;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.audits.AuditLevel;
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
import com.kingsrook.qqq.backend.core.model.metadata.authentication.QAuthenticationMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.processes.QQQProcessesMetaDataProvider;
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryBackendModule;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractTransformStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.LoadViaInsertStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
/*******************************************************************************
* test application (e.g., QInstance) for this QBit's tests.
*******************************************************************************/
public class StandardProcessTraceTestApplication extends AbstractQQQApplication
{
public static final String MEMORY_BACKEND_NAME = "memory";
public static final String TABLE_NAME_PERSON = "person";
public static final String TABLE_NAME_ORDER = "order";
public static final String PROCESS_NAME_PLACE_ORDERS = "placeOrders";
/***************************************************************************
**
***************************************************************************/
@Override
public QInstance defineQInstance() throws QException
{
QInstance qInstance = new QInstance();
qInstance.withInstanceDefaultAuthentication(new QAuthenticationMetaData().withType(QAuthenticationType.FULLY_ANONYMOUS));
qInstance.addBackend(new QBackendMetaData()
.withName(MEMORY_BACKEND_NAME)
.withBackendType(MemoryBackendModule.class));
//////////////////////////////
// add tables table and PVS //
//////////////////////////////
new QQQTablesMetaDataProvider().defineAll(qInstance, MEMORY_BACKEND_NAME, MEMORY_BACKEND_NAME, null);
new QQQProcessesMetaDataProvider().defineAll(qInstance, MEMORY_BACKEND_NAME, MEMORY_BACKEND_NAME, null);
new QQQBackendsMetaDataProvider().defineAll(qInstance, MEMORY_BACKEND_NAME, MEMORY_BACKEND_NAME, null);
//////////////////////
// produce our qbit //
//////////////////////
StandardProcessTraceQBitProducer producer = new StandardProcessTraceQBitProducer()
.withStandardProcessTraceQBitConfig(new StandardProcessTraceQBitConfig()
.withTableMetaDataCustomizer((QInstance instance, QTableMetaData table) ->
{
table.setBackendName(MEMORY_BACKEND_NAME);
return (table);
})
.withIncludeBackendActivityStats(true));
MetaDataProducerMultiOutput allQBitMetaData = producer.produce(qInstance);
allQBitMetaData.addSelfToInstance(qInstance);
////////////////////////////////////////
// produce some test tables //
// note - nice to be a qbit this too? //
////////////////////////////////////////
qInstance.addTable(new QTableMetaData()
.withName(TABLE_NAME_PERSON)
.withBackendName(MEMORY_BACKEND_NAME)
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
.withField(new QFieldMetaData("firstName", QFieldType.STRING).withIsEditable(true))
.withField(new QFieldMetaData("lastName", QFieldType.STRING).withIsEditable(true))
.withCapability(Capability.QUERY_STATS)
);
qInstance.addTable(new QTableMetaData()
.withName(TABLE_NAME_ORDER)
.withBackendName(MEMORY_BACKEND_NAME)
.withPrimaryKeyField("id")
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
.withField(new QFieldMetaData("orderNo", QFieldType.STRING).withIsEditable(true))
.withField(new QFieldMetaData("storeId", QFieldType.INTEGER).withIsEditable(true))
.withField(new QFieldMetaData("shipTo", QFieldType.STRING).withIsEditable(true))
.withCapability(Capability.QUERY_STATS)
);
qInstance.addProcess(StreamedETLWithFrontendProcess.processMetaDataBuilder()
.withName(PROCESS_NAME_PLACE_ORDERS)
.withTableName(TABLE_NAME_PERSON)
.withSourceTable(TABLE_NAME_PERSON)
.withDestinationTable(TABLE_NAME_ORDER)
.withExtractStepClass(ExtractViaQueryStep.class)
.withTransformStepClass(PlaceOrdersProcessTransformStep.class)
.withLoadStepClass(LoadViaInsertStep.class)
.getProcessMetaData()
.withProcessTracerCodeReference(new QCodeReference(StandardProcessTracer.class))
);
///////////////////////////////////////////
// turn off audits (why on by default??) //
///////////////////////////////////////////
qInstance.getTables().values().forEach(t -> t.setAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE)));
/////////////////
// create apps //
/////////////////
qInstance.addApp(new QAppMetaData()
.withName("logs")
.withIcon(new QIcon("history_edu"))
.withSection(StandardProcessTraceQBitProducer.getAppSection(qInstance)));
qInstance.addApp(new QAppMetaData()
.withName("testData")
.withIcon(new QIcon("dataset"))
.withSection(new QAppSection()
.withName("people")
.withTables(List.of(TABLE_NAME_PERSON))));
return qInstance;
}
/***************************************************************************
* test process transformer - for each input person, creates an output order (to insert)
***************************************************************************/
public static class PlaceOrdersProcessTransformStep extends AbstractTransformStep
{
private ProcessSummaryLine okLine = new ProcessSummaryLine(Status.OK)
.withMessageSuffix("")
.withSingularFutureMessage("will get a new order")
.withPluralFutureMessage("will get new orders")
.withSingularPastMessage("got a new order")
.withPluralPastMessage("got new orders");
private ProcessSummaryLine errorLine = new ProcessSummaryLine(Status.ERROR)
.withMessageSuffix("")
.withSingularFutureMessage("will not get a new order")
.withPluralFutureMessage("will not get new orders")
.withSingularPastMessage("did not get a new order")
.withPluralPastMessage("did not get new orders");
/***************************************************************************
*
***************************************************************************/
@Override
public ArrayList<ProcessSummaryLineInterface> getProcessSummary(RunBackendStepOutput runBackendStepOutput, boolean isForResultScreen)
{
ArrayList<ProcessSummaryLineInterface> rs = new ArrayList<>();
okLine.addSelfToListIfAnyCount(rs);
errorLine.addSelfToListIfAnyCount(rs);
return rs;
}
/***************************************************************************
*
***************************************************************************/
@Override
public void runOnePage(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
{
for(QRecord personRecord : runBackendStepInput.getRecords())
{
////////////////////////////////////////
// don't let Mr. Burns order anything //
////////////////////////////////////////
if("Burns".equals(personRecord.getValueString("lastName")))
{
errorLine.incrementCountAndAddPrimaryKey(personRecord.getValue("id"));
continue;
}
QRecord orderRecord = new QRecord()
.withValue("orderNo", UUID.randomUUID().toString())
.withValue("shipTo", personRecord.getValueString("firstName") + " " + personRecord.getValueString("lastName"));
runBackendStepOutput.addRecord(orderRecord);
okLine.incrementCountAndAddPrimaryKey(personRecord.getValue("id"));
}
}
}
}
@@ -0,0 +1,125 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.utils;
import java.util.List;
import java.util.concurrent.TimeUnit;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceBackendActivityStats;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QCollectingLogger;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*******************************************************************************
** Unit test for ProcessTraceBackendActivityStatsManager
*******************************************************************************/
class ProcessTraceBackendActivityStatsManagerTest
{
ProcessTraceBackendActivityStatsManager.Key key = new ProcessTraceBackendActivityStatsManager.Key("b", "t", "a");
/*******************************************************************************
**
*******************************************************************************/
@AfterEach
void afterEach()
{
ProcessTraceBackendActivityStatsManager manager = ProcessTraceBackendActivityStatsManager.getInstance();
manager.shutdown();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testMaxAgeLarger() throws QException
{
long processTraceId = 1L;
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(ProcessTraceBackendActivityStatsManager.class);
ProcessTraceBackendActivityStatsManager manager = ProcessTraceBackendActivityStatsManager.getInstance();
manager.initProcess(processTraceId);
manager.add(processTraceId, key, 1, 2, 3);
manager.setCleanupIntervalMillis(10);
manager.setCleanupMaxAge(100);
SleepUtils.sleep(50, TimeUnit.MILLISECONDS);
QLogger.deactivateCollectingLoggerForClass(ProcessTraceBackendActivityStatsManager.class);
assertThat(collectingLogger.getCollectedMessages()).allMatch(clm -> clm.getMessage().contains("\"size\":1"));
List<ProcessTraceBackendActivityStats> stats = manager.getAndRemoveStats(processTraceId);
assertEquals(1, stats.size());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testMaxAgeSmaller() throws QException
{
long processTraceId = 2L;
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(ProcessTraceBackendActivityStatsManager.class);
ProcessTraceBackendActivityStatsManager manager = ProcessTraceBackendActivityStatsManager.getInstance();
manager.initProcess(processTraceId);
manager.add(processTraceId, key, 1, 2, 3);
manager.setCleanupIntervalMillis(5);
manager.setCleanupMaxAge(20);
// Wait for the scheduled cleanup rather than assuming a shared CI runner
// will schedule the background thread within 50 milliseconds.
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(2);
while(System.nanoTime() < deadline)
{
var messages = collectingLogger.getCollectedMessages();
if(!messages.isEmpty() && messages.get(messages.size() - 1).getMessage().contains("\"size\":0"))
{
break;
}
SleepUtils.sleep(5, TimeUnit.MILLISECONDS);
}
QLogger.deactivateCollectingLoggerForClass(ProcessTraceBackendActivityStatsManager.class);
assertThat(collectingLogger.getCollectedMessages()).anyMatch(clm -> clm.getMessage().contains("\"size\":1"));
assertThat(collectingLogger.getCollectedMessages()).anyMatch(clm -> clm.getMessage().contains("\"size\":0"));
List<ProcessTraceBackendActivityStats> stats = manager.getAndRemoveStats(processTraceId);
assertEquals(0, stats.size());
////////////////////////////////////////////////////////////////////////////////////////////
// make sure even after a cleanup, we don't fail if we do try to add values for a process //
////////////////////////////////////////////////////////////////////////////////////////////
manager.add(processTraceId, key, 1, 2, 3);
stats = manager.getAndRemoveStats(processTraceId);
assertEquals(1, stats.size());
}
}
@@ -0,0 +1,176 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.standardprocesstrace.utils;
import java.util.List;
import com.kingsrook.qbits.standardprocesstrace.BaseTest;
import com.kingsrook.qbits.standardprocesstrace.StandardProcessTraceTestApplication;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTrace;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceBackendActivityStats;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceSummaryLine;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceSummaryLineRecordInt;
import com.kingsrook.qbits.standardprocesstrace.model.ProcessTraceSummaryStatusEnum;
import com.kingsrook.qqq.backend.core.actions.processes.QProcessCallbackFactory;
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.tables.QQQTableTableManager;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
/*******************************************************************************
** Unit test for StandardProcessTracer
*******************************************************************************/
class StandardProcessTracerTest extends BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test() throws QException
{
insertHomerMargeAndMrBurns();
////////////////////////////////////////
// run process skipping backend steps //
////////////////////////////////////////
RunProcessInput input = new RunProcessInput();
input.setProcessName(StandardProcessTraceTestApplication.PROCESS_NAME_PLACE_ORDERS);
input.setCallback(QProcessCallbackFactory.forFilter(new QQueryFilter()));
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.SKIP);
new RunProcessAction().execute(input);
baseAssertionsAfterProcess();
Integer personTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), StandardProcessTraceTestApplication.TABLE_NAME_PERSON);
Integer orderTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), StandardProcessTraceTestApplication.TABLE_NAME_ORDER);
List<QRecord> processTraceBackendActivityStatRecords = QueryAction.execute(ProcessTraceBackendActivityStats.TABLE_NAME, new QQueryFilter());
assertThat(processTraceBackendActivityStatRecords)
.hasSizeGreaterThanOrEqualTo(2)
.anyMatch(r -> r.getValue("qqqTableId").equals(personTableId) && r.getValue("actionName").equals(QueryAction.class.getSimpleName()) && r.getValueInteger("callCount").equals(1) && r.getValueInteger("recordCount").equals(3))
.anyMatch(r -> r.getValue("qqqTableId").equals(orderTableId) && r.getValue("actionName").equals(InsertAction.class.getSimpleName()) && r.getValueInteger("callCount").equals(1) && r.getValueInteger("recordCount").equals(2));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testResumingFrontendProcess() throws QException
{
insertHomerMargeAndMrBurns();
//////////////////////////////////////
// start process - break at preview //
//////////////////////////////////////
RunProcessInput input = new RunProcessInput();
input.setProcessName(StandardProcessTraceTestApplication.PROCESS_NAME_PLACE_ORDERS);
input.setCallback(QProcessCallbackFactory.forFilter(new QQueryFilter()));
input.setFrontendStepBehavior(RunProcessInput.FrontendStepBehavior.BREAK);
RunProcessOutput output = new RunProcessAction().execute(input);
/////////////////////////////////////
// continue to execute the process //
/////////////////////////////////////
input.setProcessUUID(output.getProcessUUID());
input.setStartAfterStep(output.getProcessState().getNextStepName().get());
new RunProcessAction().execute(input);
baseAssertionsAfterProcess();
Integer personTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), StandardProcessTraceTestApplication.TABLE_NAME_PERSON);
Integer orderTableId = QQQTableTableManager.getQQQTableId(QContext.getQInstance(), StandardProcessTraceTestApplication.TABLE_NAME_ORDER);
List<QRecord> processTraceBackendActivityStatRecords = QueryAction.execute(ProcessTraceBackendActivityStats.TABLE_NAME, new QQueryFilter());
assertThat(processTraceBackendActivityStatRecords)
.hasSizeGreaterThanOrEqualTo(2)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// this time, there'll be 2 calls to the query (for preview, and execute steps), for twice as many records total //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
.anyMatch(r -> r.getValue("qqqTableId").equals(personTableId) && r.getValue("actionName").equals(QueryAction.class.getSimpleName()) && r.getValueInteger("callCount").equals(2) && r.getValueInteger("recordCount").equals(6))
.anyMatch(r -> r.getValue("qqqTableId").equals(orderTableId) && r.getValue("actionName").equals(InsertAction.class.getSimpleName()) && r.getValueInteger("callCount").equals(1) && r.getValueInteger("recordCount").equals(2));
}
/***************************************************************************
*
***************************************************************************/
private static void insertHomerMargeAndMrBurns() throws QException
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// insert 3 people to run through the process. //
// the process will insert an order for Homer & Marge, but is hard-coded to make an error for Mr. Burns. //
///////////////////////////////////////////////////////////////////////////////////////////////////////////
new InsertAction().execute(new InsertInput(StandardProcessTraceTestApplication.TABLE_NAME_PERSON).withRecords(List.of(
new QRecord().withValue("id", 100).withValue("firstName", "Homer").withValue("lastName", "Simpson"),
new QRecord().withValue("id", 101).withValue("firstName", "Marge").withValue("lastName", "Simpson"),
new QRecord().withValue("id", 102).withValue("firstName", "Mr.").withValue("lastName", "Burns")
)));
}
/***************************************************************************
*
***************************************************************************/
private static void baseAssertionsAfterProcess() throws QException
{
List<QRecord> processTraceRecords = QueryAction.execute(ProcessTrace.TABLE_NAME, new QQueryFilter());
assertEquals(1, processTraceRecords.size());
List<QRecord> processTraceSummaryLineRecords = QueryAction.execute(ProcessTraceSummaryLine.TABLE_NAME, new QQueryFilter());
assertEquals(2, processTraceSummaryLineRecords.size());
QRecord okSummaryLineRecord = processTraceSummaryLineRecords.stream().filter(r -> r.getValueString("status").equals(ProcessTraceSummaryStatusEnum.OK.getPossibleValueId())).findFirst().get();
assertNotNull(okSummaryLineRecord);
assertEquals(2, okSummaryLineRecord.getValueInteger("recordCount"));
List<QRecord> okRecordInts = QueryAction.execute(ProcessTraceSummaryLineRecordInt.TABLE_NAME, new QQueryFilter(new QFilterCriteria("processTraceSummaryLineId", QCriteriaOperator.EQUALS, okSummaryLineRecord.getValueLong("id"))));
assertEquals(2, okRecordInts.size());
assertThat(okRecordInts).allMatch(r -> r.getValueInteger("recordId").equals(100) || r.getValueInteger("recordId").equals(101));
QRecord errorSummaryLineRecord = processTraceSummaryLineRecords.stream().filter(r -> r.getValueString("status").equals(ProcessTraceSummaryStatusEnum.ERROR.getPossibleValueId())).findFirst().get();
assertNotNull(errorSummaryLineRecord);
assertEquals(1, errorSummaryLineRecord.getValueInteger("recordCount"));
List<QRecord> errorRecordInts = QueryAction.execute(ProcessTraceSummaryLineRecordInt.TABLE_NAME, new QQueryFilter(new QFilterCriteria("processTraceSummaryLineId", QCriteriaOperator.EQUALS, errorSummaryLineRecord.getValueLong("id"))));
assertEquals(1, errorRecordInts.size());
assertEquals(102, errorRecordInts.get(0).getValueInteger("recordId"));
}
}