Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2913a9f200
|
||
|
|
caaf34c9ff
|
||
|
|
c2f23156ac
|
||
|
|
0f2327507f
|
||
|
|
760b7f0a0d
|
||
|
|
aeec64c539 | ||
|
|
e2bce64102 | ||
|
|
b6a2ac3631
|
||
|
|
81647be127
|
||
|
|
e6bda14097 | ||
|
|
f9f415f5d3
|
||
|
|
c6f13121a1
|
||
|
|
9086c8d15e
|
||
|
|
fefcb4fbfa
|
||
|
|
22c850c025
|
||
|
|
03db9b6f89 | ||
|
|
afde841966 | ||
|
|
1731d749ab | ||
|
|
241e843364 | ||
|
|
ab57dd545a | ||
|
|
431e0fce84 | ||
|
|
bd50f5a760 | ||
|
|
5f245adf59 | ||
|
|
1a78ac8976 | ||
|
|
efc8dc0567 | ||
|
|
767141e956 | ||
|
|
2e8baba5fb | ||
|
|
d3ed64202f | ||
|
|
e8d8210532 | ||
|
|
0129f1822b | ||
|
|
48afbdd4d7 | ||
|
|
a7becd6f0b | ||
|
|
8ed9fd5096 | ||
|
|
060ee0d7d8
|
||
|
|
4c82d5b4f0 | ||
|
|
671fd10d45
|
||
|
|
d1a04a385f | ||
|
|
bfe8892f71
|
||
|
|
680f76e3a3 | ||
|
|
e0c81bf68d
|
||
|
|
e071cca97e | ||
|
|
187a56825a | ||
|
|
22fae54689 | ||
|
|
90be19f913 | ||
|
|
2db99e17ea | ||
|
|
d85789c16f | ||
|
|
d7a186f78b | ||
|
|
4ba088fb81 | ||
|
|
5328b07af3 | ||
|
|
7084430141 | ||
|
|
746a3642d7 | ||
|
|
eef914d881 | ||
|
|
389e366f5e | ||
|
|
0ee8782881 | ||
|
|
0f2ec03390 | ||
|
|
1374795e68 | ||
|
|
c3640b90d7 | ||
|
|
e8f0f2b88e | ||
|
|
c86512d238 | ||
|
|
f00749a3d4 | ||
|
|
c8bdd9a239 | ||
|
|
ff08406cc0 | ||
|
|
33051ae997 | ||
|
|
f369612b78 | ||
|
|
04a1ed8cbe | ||
|
|
6c261c7458 | ||
|
|
68d25205d1 | ||
|
|
32720776f2 | ||
|
|
3f8d113f61 | ||
|
|
0e556e09e4 | ||
|
|
5a44c5d1a8 | ||
|
|
6d283a2ca4 | ||
|
|
77d8d90ebb | ||
|
|
212fc98365 | ||
|
|
77328af459 | ||
|
|
6dc7c12613 | ||
|
|
d29af04c03 | ||
|
|
c50afb87ab | ||
|
|
00b2dfc30e | ||
|
|
f9aef2f395 | ||
|
|
8e4df57800 | ||
|
|
e1df9c6b31 | ||
|
|
c880fa30e1 | ||
|
|
5a1779a73f |
No files matched your search
@@ -0,0 +1 @@
|
||||
Acceptance: a fresh protected-branch build must publish through the scheduled trusted publisher and resolve from an empty Maven cache.
|
||||
@@ -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.
|
||||
+11
-53
@@ -1,56 +1,14 @@
|
||||
version: 2.1
|
||||
|
||||
executors:
|
||||
java-executor:
|
||||
docker:
|
||||
- image: cimg/openjdk:17.0
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
executor: java-executor
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-maven-cache-{{ checksum "pom.xml" }}
|
||||
- run:
|
||||
name: Verify with Maven
|
||||
command: mvn -s .circleci/mvn-settings.xml verify
|
||||
- store_test_results:
|
||||
path: target/surefire-reports
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2/repository
|
||||
key: v1-maven-cache-{{ checksum "pom.xml" }}
|
||||
|
||||
deploy:
|
||||
executor: java-executor
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-maven-cache-{{ checksum "pom.xml" }}
|
||||
- run:
|
||||
name: Deploy with Maven
|
||||
command: mvn -s .circleci/mvn-settings.xml deploy
|
||||
- store_test_results:
|
||||
path: target/surefire-reports
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2/repository
|
||||
key: v1-maven-cache-{{ checksum "pom.xml" }}
|
||||
|
||||
setup: true
|
||||
orbs:
|
||||
qqq-orb: kingsrook/qqq-orb@0.8
|
||||
workflows:
|
||||
version: 2
|
||||
build_and_deploy:
|
||||
setup:
|
||||
jobs:
|
||||
- verify:
|
||||
context: qqq-maven-registry-credentials
|
||||
filters:
|
||||
branches:
|
||||
ignore: main
|
||||
- deploy:
|
||||
context: qqq-maven-registry-credentials
|
||||
filters:
|
||||
branches:
|
||||
only: main
|
||||
- qqq-orb/generate_pipeline:
|
||||
orb_version: '0.8'
|
||||
filters:
|
||||
branches:
|
||||
ignore: automation/qbit-rc-pending
|
||||
tags:
|
||||
only: /^v\d+\.\d+\.\d+$/
|
||||
@@ -0,0 +1,7 @@
|
||||
name: build
|
||||
on:
|
||||
push:
|
||||
jobs:
|
||||
maven:
|
||||
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@v1
|
||||
secrets: inherit
|
||||
@@ -0,0 +1 @@
|
||||
* @KofTwentyTwo
|
||||
@@ -0,0 +1,31 @@
|
||||
# GitHub Release Configuration
|
||||
# Auto-generates release notes from PR labels
|
||||
|
||||
changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- ignore-for-release
|
||||
- duplicate
|
||||
- invalid
|
||||
authors:
|
||||
- dependabot
|
||||
- dependabot[bot]
|
||||
categories:
|
||||
- title: Breaking Changes
|
||||
labels:
|
||||
- breaking-change
|
||||
- breaking
|
||||
- title: New Features
|
||||
labels:
|
||||
- enhancement
|
||||
- feature
|
||||
- title: Bug Fixes
|
||||
labels:
|
||||
- bug
|
||||
- fix
|
||||
- title: Dependencies
|
||||
labels:
|
||||
- dependencies
|
||||
- title: Other Changes
|
||||
labels:
|
||||
- "*"
|
||||
@@ -0,0 +1,23 @@
|
||||
name: CodeQL
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
- release/**
|
||||
- hotfix/**
|
||||
pull_request: {}
|
||||
schedule:
|
||||
- cron: 41 16 * * 0
|
||||
workflow_dispatch: {}
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
security-events: write
|
||||
packages: read
|
||||
concurrency:
|
||||
group: codeql-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
jobs:
|
||||
analyze:
|
||||
uses: QRun-IO/qqq-orb/.github/workflows/qbit-codeql.yml@c288a8864fe9054f68410e1d78f5863aea4c4d22
|
||||
@@ -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
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
profile: qbit
|
||||
orb_version: '0.8'
|
||||
default_branch: develop
|
||||
java_version: '21'
|
||||
browsers: true
|
||||
api_checks: false
|
||||
docs_only: false
|
||||
release_on_main: false
|
||||
release_tag_policy: stable
|
||||
test_branch_policy: non-release
|
||||
publish:
|
||||
snapshot: true
|
||||
release_candidate: true
|
||||
release: true
|
||||
asciidoc: false
|
||||
release_completion: true
|
||||
@@ -0,0 +1,58 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to QBit User Role Permissions will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Comprehensive README documentation following QQQ sub repo standards
|
||||
- Standard GitHub files (LICENSE, CHANGELOG, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT)
|
||||
- Proper project structure and documentation
|
||||
|
||||
### Changed
|
||||
- Updated README to follow QQQ framework standards
|
||||
- Improved project documentation and structure
|
||||
- Enhanced GitHub repository setup
|
||||
|
||||
## [0.30.0-SNAPSHOT] - 2024-01-XX
|
||||
|
||||
### Added
|
||||
- Initial QBit implementation for user role permissions
|
||||
- Database schema for users, roles, and permissions
|
||||
- Java classes for User, Role, Permission, UserRole, and RolePermission
|
||||
- Integration with QQQ Backend Core framework
|
||||
- H2 database support for testing
|
||||
- JUnit 5 test framework integration
|
||||
- AssertJ assertions for testing
|
||||
- Checkstyle configuration for code quality
|
||||
|
||||
### Changed
|
||||
- Initial public release of QBit User Role Permissions
|
||||
- Established QBit development standards
|
||||
- Created modular architecture for permission management
|
||||
|
||||
---
|
||||
|
||||
## 📚 For Detailed Information
|
||||
|
||||
**📖 [Complete Documentation Wiki](https://github.com/Kingsrook/qqq/wiki)** - Start here for comprehensive guides
|
||||
- **[🏠 Home](https://github.com/Kingsrook/qqq/wiki/Home)** - Project overview and quick start
|
||||
- **[🚀 Release Flow](https://github.com/Kingsrook/qqq/wiki/Release-Flow)** - Detailed release process
|
||||
- **[🏷️ Changelog & Tagging](https://github.com/Kingsrook/qqq/wiki/Changelog-and-Tagging)** - Commit conventions and release notes
|
||||
- **[🔧 Developer Onboarding](https://github.com/Kingsrook/qqq/wiki/Developer-Onboarding)** - Setup and contribution guide
|
||||
|
||||
## 🔄 Version Compatibility
|
||||
|
||||
QBit User Role Permissions follows semantic versioning:
|
||||
- **MAJOR** versions may contain breaking changes
|
||||
- **MINOR** versions add new functionality (backward compatible)
|
||||
- **PATCH** versions contain bug fixes (backward compatible)
|
||||
|
||||
For detailed compatibility information, see [QQQ Compatibility Matrix](https://github.com/Kingsrook/qqq/wiki/Compatibility-Matrix).
|
||||
|
||||
---
|
||||
|
||||
**Thank you for using QBit User Role Permissions!** 🚀
|
||||
@@ -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).
|
||||
@@ -0,0 +1,4 @@
|
||||
# Code of Conduct
|
||||
|
||||
|
||||
See Project level [Code of Conduct](https://github.com/Kingsrook/qqq/blob/develop/CODE_OF_CONDUCT.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
# Code Style Guide
|
||||
|
||||
* See Project [Code Style Guide](https://github.com/Kingsrook/qqq/blob/develop/CODE_STYLE.md)
|
||||
@@ -0,0 +1,4 @@
|
||||
# Contributing to QBit User Role Permissions
|
||||
|
||||
* See Project [Contributing](https://github.com/Kingsrook/qqq/blob/develop/CONTRIBUTING.md)
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright
|
||||
owner or by an individual or Legal Entity authorized to submit on
|
||||
behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written
|
||||
communication sent to the Licensor or its representatives, including
|
||||
but not limited to communication on electronic mailing lists, source
|
||||
code control systems, and issue tracking systems that are managed by,
|
||||
or on behalf of, the Licensor for the purpose of discussing and
|
||||
improving the Work, but excluding communication that is conspicuously
|
||||
marked or otherwise designated in writing by the copyright owner as
|
||||
"Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022-2025 QRunIO, LLC
|
||||
|
||||
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
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,15 @@
|
||||
qBit: Standard Process Trace
|
||||
Copyright 2022-2025 QRunIO, LLC
|
||||
|
||||
This product includes software developed at QRunIO, LLC (https://qrun.io/).
|
||||
|
||||
Licensed under the Apache License, Version 2.0.
|
||||
|
||||
================================================================================
|
||||
|
||||
Trademarks:
|
||||
|
||||
QQQ, qBits, qRun, qStudio, and the QRunIO logo are trademarks of QRunIO, LLC.
|
||||
Use of these trademarks must comply with QRunIO's Trademark Guidelines.
|
||||
|
||||
================================================================================
|
||||
@@ -1,118 +1,135 @@
|
||||
## QBit: Standard Process Trace
|
||||
# qbit-standard-process-trace
|
||||
|
||||
### Overview
|
||||
*Note: This is one of the original QBit implementations - so, some of the mechanics of how
|
||||
it is loaded and used by an application are not exactly fully defined at the time of its
|
||||
creation... Please excuse any dust or not-quite-round wheels you find here!*
|
||||
Process execution tracing and logging for QQQ applications.
|
||||
|
||||
This QBit provides tables to implement a fairly standard version of **Process Tracing**,
|
||||
along with an implementation of the `ProcessTracerInterface` which inserts data into
|
||||
those tables.
|
||||
**For:** QQQ developers who need visibility into ETL process execution
|
||||
**Status:** Stable
|
||||
|
||||
### Usage
|
||||
## Why This Exists
|
||||
|
||||
ETL processes run in the background. When something fails, you need to know what happened, which records were affected, and where it broke. Adding logging to every process is repetitive and inconsistent.
|
||||
|
||||
This QBit provides automatic tracing for QQQ processes. Every execution is logged with timing, record counts, and error details. No code changes required in your processes.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automatic Tracing** - Captures all process executions without code changes
|
||||
- **Step-Level Detail** - Timing and status for each step in multi-step processes
|
||||
- **Record Tracking** - Input/output counts and error record IDs
|
||||
- **Error Capture** - Full stack traces and error messages
|
||||
- **Dashboard View** - Process history visible in the QQQ dashboard
|
||||
- **Retention Policies** - Automatic cleanup of old trace data
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- QQQ application (v4.1.0+)
|
||||
- Database backend configured
|
||||
|
||||
### Installation
|
||||
|
||||
Add to your `pom.xml`:
|
||||
|
||||
#### Pom dependency
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qbits</groupId>
|
||||
<artifactId>qbit-standard-process-trace</artifactId>
|
||||
<version>${TODO}</version>
|
||||
<version>0.2.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### Setup
|
||||
A method such as the following can be used to define the tables provided by this QBit,
|
||||
along with details needed for working in your application.
|
||||
### Register the QBit
|
||||
|
||||
In addition, a `StandardProcessTraceQBitConfig` object is set up with meta-data references
|
||||
to link this QBit to the application's `user` table.
|
||||
```java
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static void addStandardProcessTraceQBit(QInstance qInstance) throws QException
|
||||
{
|
||||
StandardProcessTraceQBitConfig standardProcessTraceQBitConfig = new StandardProcessTraceQBitConfig()
|
||||
.withUserIdPossibleValueSourceName(User.TABLE_NAME)
|
||||
.withUserTableName(User.TABLE_NAME)
|
||||
.withUserIdReferenceFieldName("email");
|
||||
StandardProcessTracer.setStandardProcessTraceQBitConfig(standardProcessTraceQBitConfig);
|
||||
|
||||
MetaDataCustomizerInterface<QTableMetaData> tableMetaDataCustomizer = (instance, table) ->
|
||||
{
|
||||
table.setBackendName("todoBackendName");
|
||||
table.setAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE)); // recommended
|
||||
|
||||
// if backend is in an RDBMS and uses under_score style names to camelCase:
|
||||
table.setBackendDetails(new RDBMSTableBackendDetails().withTableName(QInstanceEnricher.inferBackendName(table.getName())));
|
||||
QInstanceEnricher.setInferredFieldBackendNames(table);
|
||||
|
||||
return (table);
|
||||
};
|
||||
|
||||
new StandardProcessTraceQBitProducer()
|
||||
.withStandardProcessTraceQBitConfig(standardProcessTraceQBitConfig)
|
||||
.withTableMetaDataCustomizer(tableMetaDataCustomizer)
|
||||
.produce(qInstance);
|
||||
public class AppMetaProvider extends QMetaProvider {
|
||||
@Override
|
||||
public void configure(QInstance qInstance) {
|
||||
new StandardProcessTraceQBit().configure(qInstance);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A block like this can be used to add the tables provided by this QBit to an app in your instance
|
||||
(namely, the `StandardProcessTraceQBitProducer.getAppSection(qInstance)` call):
|
||||
```java
|
||||
qInstance.addApp(new QAppMetaData()
|
||||
.withName("logs")
|
||||
.withIcon(new QIcon().withName("history_edu"))
|
||||
.withSection(StandardProcessTraceQBitProducer.getAppSection(qInstance)));
|
||||
```
|
||||
That's it. All processes are now traced automatically.
|
||||
|
||||
#### Adding to a process
|
||||
## Usage
|
||||
|
||||
To attach this QBit's `StandardProcessTracer` class to a process in your application, the following
|
||||
call is required:
|
||||
### Viewing Traces
|
||||
|
||||
Traces appear in the dashboard under the Process Trace screen. Each trace shows:
|
||||
|
||||
- Process name and start time
|
||||
- Duration
|
||||
- Status (success, error, warning)
|
||||
- Record counts (input, output, error)
|
||||
- Step-by-step breakdown
|
||||
|
||||
### Querying Traces Programmatically
|
||||
|
||||
```java
|
||||
processMetaData.setProcessTracerCodeReference(new QCodeReference(StandardProcessTracer.class));
|
||||
QueryInput query = new QueryInput()
|
||||
.withTableName("process_trace")
|
||||
.withFilter(new QQueryFilter()
|
||||
.withCriteria("processName", Operator.EQUALS, "orderSync")
|
||||
.withCriteria("status", Operator.EQUALS, "ERROR"))
|
||||
.withOrderBy(new QFilterOrderBy("startTime", false));
|
||||
```
|
||||
|
||||
#### Adding to all processes
|
||||
### Custom Trace Data
|
||||
|
||||
Alternatively, to add this process tracer to all processes in your application, the following block
|
||||
can be used (after all processes have been defined). *Note that in this example, we are allowing a process
|
||||
to have specified a different process tracer, via the null check.`*
|
||||
Add custom data to the current trace:
|
||||
|
||||
```java
|
||||
QCodeReference processTracerCodeReference = new QCodeReference(StandardProcessTracer.class);
|
||||
qInstance.getProcesses().values().forEach(p ->
|
||||
{
|
||||
if(p.getProcessTracerCodeReference() == null)
|
||||
{
|
||||
p.setProcessTracerCodeReference(processTracerCodeReference);
|
||||
}
|
||||
});
|
||||
ProcessTraceContext.current()
|
||||
.withCustomField("batchId", batchId)
|
||||
.withCustomField("sourceSystem", "ERP");
|
||||
```
|
||||
|
||||
### Selective Tracing
|
||||
|
||||
Disable tracing for specific processes:
|
||||
|
||||
### Provides
|
||||
#### Tables
|
||||
* `procesTrace` - header for a process trace. Includes details such as start & end timestamps,
|
||||
exception message, key input record, and exception message.
|
||||
* `procesTraceSummaryLine` - child record of `processTrace`. Captures the summary lines produced
|
||||
by a process in the field named: `processResults`. e.g., what all `StreamedETLWithFrontendProcess`
|
||||
instances do.
|
||||
* `procesTraceSummaryLineRecordInt` - child record of `processTraceSummaryLine`. Relates summary
|
||||
lines to the individual records they are composed of.
|
||||
```java
|
||||
new QProcessMetaData()
|
||||
.withName("frequentHealthCheck")
|
||||
.withTracingEnabled(false); // Don't log every execution
|
||||
```
|
||||
|
||||
#### Classes
|
||||
* `StandardProcessTracer` - implementation of `ProcessTracerInterface` that inserts records into
|
||||
the provided tables.
|
||||
## Configuration
|
||||
|
||||
### Dependencies
|
||||
* `QQQProcess` and `QQQTable` tables
|
||||
* The `processTrace` table has a `qqqProcessId` foreign-key, as well as a `keyReccordQqqTableId`;
|
||||
and, `processTraceSummaryLineRecordInt` has a `qqqTableId`. This means that you'll want and
|
||||
need these tables in your QQQ Instance and backend data store.
|
||||
* Use `QQQProcessesMetaDataProvider` and `QQQTablesMetaDataProvider` to define these tables.
|
||||
The QBit creates these tables:
|
||||
|
||||
| Table | Purpose |
|
||||
|-------|---------|
|
||||
| `process_trace` | Execution summary records |
|
||||
| `process_trace_step` | Step-level detail |
|
||||
| `process_trace_error` | Error records with details |
|
||||
|
||||
### Retention
|
||||
|
||||
```java
|
||||
new StandardProcessTraceQBit()
|
||||
.withRetentionDays(30) // Keep traces for 30 days
|
||||
.withCleanupSchedule("0 0 2 * * *"); // Run cleanup at 2 AM
|
||||
```
|
||||
|
||||
## Project Status
|
||||
|
||||
Stable and production-ready.
|
||||
|
||||
### Roadmap
|
||||
|
||||
- Trace aggregation and statistics
|
||||
- Alerting on repeated failures
|
||||
- Export to external monitoring systems
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Run tests: `mvn clean verify`
|
||||
4. Submit a pull request
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0 - See [LICENSE](LICENSE) and [NOTICE](NOTICE).
|
||||
@@ -0,0 +1,3 @@
|
||||
# Security Policy
|
||||
|
||||
* See Project [Security Policy](https://github.com/Kingsrook/qqq/blob/develop/SECURITY.md)
|
||||
@@ -6,40 +6,82 @@
|
||||
~ 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>2.2.1</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.kingsrook.qbits</groupId>
|
||||
<artifactId>qbit-standard-process-trace</artifactId>
|
||||
<version>0.1.4</version>
|
||||
<version>${revision}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
<name>QBit Standard Process Trace</name>
|
||||
<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>Apache License, Version 2.0</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>kingsrook</id>
|
||||
<name>Kingsrook Team</name>
|
||||
<email>contact@kingsrook.com</email>
|
||||
<organization>Kingsrook, LLC</organization>
|
||||
<organizationUrl>https://kingsrook.com</organizationUrl>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/Kingsrook/qbit-standard-process-trace.git</connection>
|
||||
<developerConnection>scm:git:ssh://github.com:Kingsrook/qbit-standard-process-trace.git</developerConnection>
|
||||
<url>https://github.com/Kingsrook/qbit-standard-process-trace</url>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<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.24.0-integration-sprint-61-20250224-173943</version>
|
||||
<version>${qqq.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
<version>6.0.2</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -51,22 +93,18 @@
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-backend-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>3.27.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>github-qqq-maven-registry</id>
|
||||
<name>GitHub QQQ Maven Registry</name>
|
||||
<url>https://maven.pkg.github.com/Kingsrook/qqq-maven-registry</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>github-qqq-maven-registry</id>
|
||||
<name>GitHub QQQ Maven Registry</name>
|
||||
<url>https://maven.pkg.github.com/Kingsrook/qqq-maven-registry</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
</project>
|
||||
+99
-13
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+66
-63
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
+92
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -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"))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+71
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+9
-10
@@ -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;
|
||||
|
||||
+9
-10
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+348
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+131
@@ -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());
|
||||
}
|
||||
}
|
||||
+9
-10
@@ -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;
|
||||
|
||||
+9
-10
@@ -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;
|
||||
|
||||
+9
-10
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+9
-10
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
+297
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+9
-10
@@ -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;
|
||||
|
||||
+9
-10
@@ -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;
|
||||
|
||||
+9
-10
@@ -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;
|
||||
|
||||
+66
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
-13
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+233
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+125
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+176
@@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user