From 56b6f48a7b6b14a4f49efeebb49c67a9bf1ad70d Mon Sep 17 00:00:00 2001 From: James Maes Date: Thu, 15 Oct 2026 01:33:09 -0500 Subject: [PATCH 01/19] fix: the qbit metadata version comes from the build, not a hardcoded constant The producer read a literal version, so released artifacts reported a stale or -SNAPSHOT version. qbit-slack.properties (version=${project.version}) is Maven-filtered and the producer loads it; tests assert producer == bundle == artifact version. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 3 ++ pom.xml | 11 +++++++ .../qbits/slack/SlackQBitProducer.java | 33 ++++++++++++++++++- src/main/resources/qbit-slack.properties | 1 + .../qbits/slack/SlackQBitProducerTest.java | 2 ++ .../qbits/slack/SlackTablesGuardrailTest.java | 2 ++ 6 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/qbit-slack.properties diff --git a/CHANGELOG.md b/CHANGELOG.md index 293f957..5d7dc82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- QBit metadata now reports the Maven artifact version, including release versions, from a filtered properties resource. + ### Added - Complete host-neutral in-app help bundle with live labels and core `HelpBundleAssertions` coverage; registered through QQQ 4.1.0-RC.7 `HelpBundles`, replacing the temporary loader. - "Check channels" action on Registered Channels; readiness now writes the channel's Slack name, private, bot is member, archived, purpose and checked time back to the row. diff --git a/pom.xml b/pom.xml index b2376f3..8d01b73 100644 --- a/pom.xml +++ b/pom.xml @@ -127,6 +127,7 @@ true help/*.yaml + qbit-slack.properties @@ -134,10 +135,20 @@ false help/*.yaml + qbit-slack.properties + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.version} + + + org.apache.maven.plugins maven-checkstyle-plugin diff --git a/src/main/java/com/kingsrook/qbits/slack/SlackQBitProducer.java b/src/main/java/com/kingsrook/qbits/slack/SlackQBitProducer.java index d249279..2334439 100644 --- a/src/main/java/com/kingsrook/qbits/slack/SlackQBitProducer.java +++ b/src/main/java/com/kingsrook/qbits/slack/SlackQBitProducer.java @@ -21,8 +21,11 @@ package com.kingsrook.qbits.slack; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.Properties; import com.kingsrook.qbits.slack.bridge.SlackOutboxMessagingProviderMetaData; import com.kingsrook.qbits.slack.delivery.SlackAlerts; import com.kingsrook.qbits.slack.metadata.SlackTables; @@ -43,13 +46,41 @@ public class SlackQBitProducer implements QBitMetaDataProducer { public static final String GROUP_ID = "com.kingsrook.qbits"; public static final String ARTIFACT_ID = "qbit-slack"; - public static final String VERSION = "0.1.0"; + public static final String VERSION = loadVersion(); private SlackQBitConfig qBitConfig; private String namespace; + /******************************************************************************* + ** Read the artifact version from the Maven-filtered properties resource. + *******************************************************************************/ + private static String loadVersion() + { + try(InputStream inputStream = SlackQBitProducer.class.getClassLoader().getResourceAsStream("qbit-slack.properties")) + { + if(inputStream == null) + { + throw (new IllegalStateException("Missing qbit-slack.properties; build with Maven to supply the artifact version")); + } + Properties properties = new Properties(); + properties.load(inputStream); + String version = properties.getProperty("version"); + if(version == null || version.isBlank() || version.contains("${")) + { + throw (new IllegalStateException("Missing or unfiltered version in qbit-slack.properties; build with Maven")); + } + return (version); + } + catch(IOException e) + { + throw (new IllegalStateException("Could not read artifact version from qbit-slack.properties", e)); + } + } + + + /******************************************************************************* ** Fluent setter for the config. *******************************************************************************/ diff --git a/src/main/resources/qbit-slack.properties b/src/main/resources/qbit-slack.properties new file mode 100644 index 0000000..defbd48 --- /dev/null +++ b/src/main/resources/qbit-slack.properties @@ -0,0 +1 @@ +version=${project.version} diff --git a/src/test/java/com/kingsrook/qbits/slack/SlackQBitProducerTest.java b/src/test/java/com/kingsrook/qbits/slack/SlackQBitProducerTest.java index 6632b41..14a2298 100644 --- a/src/test/java/com/kingsrook/qbits/slack/SlackQBitProducerTest.java +++ b/src/test/java/com/kingsrook/qbits/slack/SlackQBitProducerTest.java @@ -45,6 +45,8 @@ class SlackQBitProducerTest SlackQBitProducer producer = new SlackQBitProducer().withQBitConfig(config).withNamespace("ns"); QBitMetaData metaData = producer.getQBitMetaData(); + assertThat(System.getProperty("qbit.artifactVersion")).isNotBlank(); + assertThat(metaData.getVersion()).isEqualTo(System.getProperty("qbit.artifactVersion")).isEqualTo(SlackQBitProducer.VERSION); assertThat(metaData.getArtifactId()).isEqualTo("qbit-slack"); assertThat(metaData.getGroupId()).isEqualTo("com.kingsrook.qbits"); assertThat(producer.getNamespace()).isEqualTo("ns"); diff --git a/src/test/java/com/kingsrook/qbits/slack/SlackTablesGuardrailTest.java b/src/test/java/com/kingsrook/qbits/slack/SlackTablesGuardrailTest.java index d458e69..7f0c775 100644 --- a/src/test/java/com/kingsrook/qbits/slack/SlackTablesGuardrailTest.java +++ b/src/test/java/com/kingsrook/qbits/slack/SlackTablesGuardrailTest.java @@ -160,6 +160,8 @@ class SlackTablesGuardrailTest var bundles = HelpBundles.of(instance).getBundles(); assertThat(bundles).hasSize(1); assertThat(bundles.get(0).getId()).isEqualTo("qbit-slack"); + assertThat(System.getProperty("qbit.artifactVersion")).isNotBlank(); + assertThat(bundles.get(0).getVersion()).isEqualTo(System.getProperty("qbit.artifactVersion")).isEqualTo(SlackQBitProducer.VERSION); for(String table : SlackTables.ALL) { assertThat(bundles.get(0).getEntries()).filteredOn(entry -> entry.getKey().equals("table:" + table + ";slot:help")) -- 2.54.0 From 33d993f414a92bf011a29b0fbebe0c595692eccf Mon Sep 17 00:00:00 2001 From: James Maes Date: Thu, 15 Oct 2026 16:20:23 -0500 Subject: [PATCH 02/19] ci(release-rc): pass secrets to the shared workflow so it can sign the RC tag QRun-IO/.github#2 adds a tag-and-release job that signs vX with the QRUN_TAG_SIGNING_KEY secret after Maven Central confirms the artifact. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release-rc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-rc.yml b/.github/workflows/release-rc.yml index dd332a0..a342ba8 100644 --- a/.github/workflows/release-rc.yml +++ b/.github/workflows/release-rc.yml @@ -21,5 +21,6 @@ jobs: release-rc: if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release:rc')) uses: QRun-IO/.github/.github/workflows/qbit-release-rc.yml@main + secrets: inherit with: dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }} -- 2.54.0 From fb4ae6db074cb114fed962248e385e9139fbc0e1 Mon Sep 17 00:00:00 2001 From: James Maes Date: Tue, 20 Oct 2026 19:26:57 -0500 Subject: [PATCH 03/19] ci: adopt central QQQ orb 0.7.4 Co-Authored-By: Codex --- .circleci/config.yml | 71 +++++++------------------------------------- .qqq-ci.yml | 16 ++++++++++ 2 files changed, 26 insertions(+), 61 deletions(-) create mode 100644 .qqq-ci.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 14dd169..6cbcc25 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,65 +1,14 @@ version: 2.1 - +setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.6.9 - -commands: - configure-package-install: - steps: - - run: - name: Keep package installation unattended - command: | - # These build machines are discarded after the job. - sudo install -d /etc/needrestart/conf.d - printf '%s\n' '$nrconf{restart} = "l";' | sudo tee /etc/needrestart/conf.d/99-qqq-ci.conf >/dev/null - + qqq-orb: kingsrook/qqq-orb@0.7.4 workflows: - - test_only: + setup: jobs: - - qqq-orb/mvn_test_only: - pre-steps: - - configure-package-install - context: [ qqq-maven-registry-credentials ] - filters: - branches: - ignore: /(develop|release/.*|integration.*)/ - tags: - only: [] - - publish_snapshot: - jobs: - - qqq-orb/mvn_publish: - pre-steps: - - configure-package-install - context: [ qqq-maven-registry-credentials ] - branch_type: snapshot - filters: - branches: - only: - - develop - - publish_release_candidate: - jobs: - - qqq-orb/mvn_publish: - pre-steps: - - configure-package-install - context: [ qqq-maven-registry-credentials ] - branch_type: release_candidate - filters: - branches: - only: - - /release\/.*/ - - publish_release: - jobs: - - qqq-orb/mvn_publish: - pre-steps: - - configure-package-install - context: [ qqq-maven-registry-credentials ] - branch_type: release - filters: - branches: - ignore: /.*/ - tags: - only: [ /^v\d+\.\d+\.\d+$/ ] + - qqq-orb/generate_pipeline: + orb_version: 0.7.4 + filters: + branches: + ignore: automation/qbit-rc-pending + tags: + only: /^v\d+\.\d+\.\d+$/ diff --git a/.qqq-ci.yml b/.qqq-ci.yml new file mode 100644 index 0000000..b9b9fc4 --- /dev/null +++ b/.qqq-ci.yml @@ -0,0 +1,16 @@ +profile: qbit +orb_version: 0.7.4 +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-no-integration +publish: + snapshot: true + release_candidate: true + release: true + asciidoc: false +release_completion: false -- 2.54.0 From 4dcabdffedc380cf03c6e8bc91cb786993749fcb Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 00:57:21 -0500 Subject: [PATCH 04/19] ci: adopt shared orb 0.7.11 (#17) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .qqq-ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6cbcc25..def7f95 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.4 + qqq-orb: kingsrook/qqq-orb@0.7.11 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.4 + orb_version: 0.7.11 filters: branches: ignore: automation/qbit-rc-pending diff --git a/.qqq-ci.yml b/.qqq-ci.yml index b9b9fc4..34d6648 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.4 +orb_version: 0.7.11 default_branch: develop java_version: '21' browsers: true -- 2.54.0 From f0b70d7da70f2a90b72b39233598d9f206cd8c4c Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 02:23:37 -0500 Subject: [PATCH 05/19] ci: centralize rc release completion in qqq orb (#18) Co-authored-by: Codex --- .github/workflows/release-rc.yml | 76 ++++++++++++++++++++++++++------ .qqq-ci.yml | 2 +- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release-rc.yml b/.github/workflows/release-rc.yml index a342ba8..0158aa2 100644 --- a/.github/workflows/release-rc.yml +++ b/.github/workflows/release-rc.yml @@ -1,26 +1,74 @@ -# Cut the next release candidate (QRun-IO/qqq docs/QRUN-QBIT-REPO-STANDARD.md, C6): -# run this workflow by hand, or merge a develop PR labelled `release:rc`. It merges develop into -# the release line and the release/* push runs CircleCI publish_release_candidate. name: Release candidate - -on: +'on': workflow_dispatch: inputs: dry_run: - description: Print the plan without merging or creating branches + 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] - + 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.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release:rc')) - uses: QRun-IO/.github/.github/workflows/qbit-release-rc.yml@main - secrets: inherit + 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@7d73ac90b6b5a6acb61b4d3fa56625c5b6dcc652 with: - dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }} + 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 diff --git a/.qqq-ci.yml b/.qqq-ci.yml index 34d6648..d983335 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -13,4 +13,4 @@ publish: release_candidate: true release: true asciidoc: false -release_completion: false +release_completion: true -- 2.54.0 From a40eade67b26bd67dab8892e611d15649c704d01 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 08:50:01 -0500 Subject: [PATCH 06/19] ci: adopt central orb 0.7.12 and stable gates (#20) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .github/workflows/release-rc.yml | 2 +- .qqq-ci.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index def7f95..c9cb461 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.11 + qqq-orb: kingsrook/qqq-orb@0.7.12 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.11 + orb_version: 0.7.12 filters: branches: ignore: automation/qbit-rc-pending diff --git a/.github/workflows/release-rc.yml b/.github/workflows/release-rc.yml index 0158aa2..b48a32a 100644 --- a/.github/workflows/release-rc.yml +++ b/.github/workflows/release-rc.yml @@ -53,7 +53,7 @@ jobs: || (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@7d73ac90b6b5a6acb61b4d3fa56625c5b6dcc652 + uses: QRun-IO/qqq-orb/.github/workflows/qbit-release-rc.yml@cc30f599e63c8e41629c99d23395bac1adb8bc44 with: dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false }} diff --git a/.qqq-ci.yml b/.qqq-ci.yml index d983335..3a681a5 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.11 +orb_version: 0.7.12 default_branch: develop java_version: '21' browsers: true -- 2.54.0 From 42aaf3c9c83006dd18a02b812a0d53c54a46c9d4 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 10:43:15 -0500 Subject: [PATCH 07/19] ci: use the shared branch-aware CodeQL worker (#22) Co-authored-by: Codex --- .github/workflows/codeql.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..bd3ceaf --- /dev/null +++ b/.github/workflows/codeql.yml @@ -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 -- 2.54.0 From 2682c2fa949ddcc698ca79bb45f0fc896e088586 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 14:16:09 -0500 Subject: [PATCH 08/19] ci: adopt shared orb 0.7.17 (#24) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .qqq-ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c9cb461..1d4fa0d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.12 + qqq-orb: kingsrook/qqq-orb@0.7.17 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.12 + orb_version: 0.7.17 filters: branches: ignore: automation/qbit-rc-pending diff --git a/.qqq-ci.yml b/.qqq-ci.yml index 3a681a5..b634788 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.12 +orb_version: 0.7.17 default_branch: develop java_version: '21' browsers: true -- 2.54.0 From 7a0fd3762a2933fcc2e317b3df838b52676d30d4 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 14:46:34 -0500 Subject: [PATCH 09/19] ci: adopt shared orb 0.7.18 (#25) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .qqq-ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1d4fa0d..42b73df 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.17 + qqq-orb: kingsrook/qqq-orb@0.7.18 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.17 + orb_version: 0.7.18 filters: branches: ignore: automation/qbit-rc-pending diff --git a/.qqq-ci.yml b/.qqq-ci.yml index b634788..7976133 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.17 +orb_version: 0.7.18 default_branch: develop java_version: '21' browsers: true -- 2.54.0 From 33ba26521552f22be376d341e2cbef5f0e369996 Mon Sep 17 00:00:00 2001 From: James Maes Date: Wed, 21 Oct 2026 15:28:28 -0500 Subject: [PATCH 10/19] ci: adopt managed orb 0.8 patch track (#26) Co-authored-by: Codex --- .circleci/config.yml | 4 ++-- .qqq-ci.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 42b73df..f890c28 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,12 +1,12 @@ version: 2.1 setup: true orbs: - qqq-orb: kingsrook/qqq-orb@0.7.18 + qqq-orb: kingsrook/qqq-orb@0.8 workflows: setup: jobs: - qqq-orb/generate_pipeline: - orb_version: 0.7.18 + orb_version: '0.8' filters: branches: ignore: automation/qbit-rc-pending diff --git a/.qqq-ci.yml b/.qqq-ci.yml index 7976133..fa97e65 100644 --- a/.qqq-ci.yml +++ b/.qqq-ci.yml @@ -1,5 +1,5 @@ profile: qbit -orb_version: 0.7.18 +orb_version: '0.8' default_branch: develop java_version: '21' browsers: true -- 2.54.0 From 0a03bfb50110d07b2d96b0607f7747212142644b Mon Sep 17 00:00:00 2001 From: Forge Recovery Date: Mon, 2 Nov 2026 04:15:28 +0000 Subject: [PATCH 11/19] ci: add the identical QRun release caller Co-Authored-By: Codex --- .gitea/workflows/qrun-release.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .gitea/workflows/qrun-release.yaml diff --git a/.gitea/workflows/qrun-release.yaml b/.gitea/workflows/qrun-release.yaml new file mode 100644 index 0000000..122f7e9 --- /dev/null +++ b/.gitea/workflows/qrun-release.yaml @@ -0,0 +1,12 @@ +# Identical in every QRun Maven application: do not edit. It only delegates to the protected qrun-release-v1 channel. +name: qrun-release +on: + push: + branches: [main, develop, 'release/**', 'hotfix/**'] +permissions: + contents: read +jobs: + release: + uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/java-maven.yaml@qrun-release-v1 + with: + runner: linux-small -- 2.54.0 From a5bae34659238cecc2a8ce71e65321246d8348f9 Mon Sep 17 00:00:00 2001 From: Forge Recovery Date: Mon, 2 Nov 2026 06:21:08 +0000 Subject: [PATCH 12/19] ci: move the QRun release caller to the thin v2 contract Co-Authored-By: Claude Sonnet 5.5 Co-Authored-By: Codex --- .gitea/workflows/qrun-release.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/qrun-release.yaml b/.gitea/workflows/qrun-release.yaml index 122f7e9..711441c 100644 --- a/.gitea/workflows/qrun-release.yaml +++ b/.gitea/workflows/qrun-release.yaml @@ -1,12 +1,15 @@ -# Identical in every QRun Maven application: do not edit. It only delegates to the protected qrun-release-v1 channel. +# Standard QRun release caller (v2, thin contract): every push and manual dispatch delegates to the protected qrun-release-v1 channel. Do not edit per repository. name: qrun-release on: - push: - branches: [main, develop, 'release/**', 'hotfix/**'] + push: {} + workflow_dispatch: {} permissions: contents: read + actions: read jobs: release: uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/java-maven.yaml@qrun-release-v1 with: runner: linux-small + browsers: true + browser-runner: browser-build -- 2.54.0 From c4312ea7a1cf5ab9b7e93bbaf7a483207592e61e Mon Sep 17 00:00:00 2001 From: Forge Recovery Date: Mon, 2 Nov 2026 15:53:53 +0000 Subject: [PATCH 13/19] ci: add the isolated postpublish job to the QRun release caller Co-Authored-By: Claude Sonnet 5.5 Co-Authored-By: Codex --- .gitea/workflows/qrun-release.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitea/workflows/qrun-release.yaml b/.gitea/workflows/qrun-release.yaml index 711441c..cb745b2 100644 --- a/.gitea/workflows/qrun-release.yaml +++ b/.gitea/workflows/qrun-release.yaml @@ -13,3 +13,12 @@ jobs: runner: linux-small browsers: true browser-runner: browser-build + postpublish: + if: ${{ needs.release.outputs.capture == 'true' }} + needs: release + permissions: + contents: write + actions: write + uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/qrun-source-version.yaml@qrun-release-v1 + with: + runner: linux-small -- 2.54.0 From d74e6a1a718a580a604f1e8c5c145f119279fb05 Mon Sep 17 00:00:00 2001 From: Forge Recovery Date: Mon, 2 Nov 2026 20:21:07 +0000 Subject: [PATCH 14/19] ci: enroll shared Gitea release-candidate lifecycle Co-Authored-By: Codex --- .gitea/workflows/release-rc.yaml | 72 ++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .gitea/workflows/release-rc.yaml diff --git a/.gitea/workflows/release-rc.yaml b/.gitea/workflows/release-rc.yaml new file mode 100644 index 0000000..12ec9fc --- /dev/null +++ b/.gitea/workflows/release-rc.yaml @@ -0,0 +1,72 @@ +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 release run id (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 +permissions: + contents: read +jobs: + release-rc: + # The old qbit-rc-published repository_dispatch is replaced by the already-proven workflow_dispatch + # completion: the publisher's own ActionsWRITE job dispatches this workflow with finalize=true plus + # merge_sha/bump_sha/branch/version (same inputs as the manual finalize retry). + permissions: + contents: write + actions: read + pull-requests: write + if: github.event_name == 'workflow_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: self:KofTwentyTwo/platform-ci/.gitea/workflows/qbit-release-rc.yaml@qrun-release-v1 + with: + dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false + }} + finalize: ${{ 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: ${{ inputs.merge_sha || '' }} + bump_sha: ${{ inputs.bump_sha || '' }} + branch: ${{ inputs.branch || '' }} + version: ${{ inputs.version || '' }} + secrets: + QRUN_TAG_SIGNING_KEY: ${{ secrets.QRUN_TAG_SIGNING_KEY }} -- 2.54.0 From 48a2387ac6f9ffe448d888e5a96ee6a79738737a Mon Sep 17 00:00:00 2001 From: James Maes Date: Thu, 5 Nov 2026 22:08:36 +0000 Subject: [PATCH 15/19] ci: use shared ci-workflows Maven workflow Replace the platform-ci release callers with one caller of KofTwentyTwo/ci-workflows maven.yaml (qqq-orb port). Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/build.yaml | 7 +++ .gitea/workflows/qrun-release.yaml | 24 ---------- .gitea/workflows/release-rc.yaml | 72 ------------------------------ 3 files changed, 7 insertions(+), 96 deletions(-) create mode 100644 .gitea/workflows/build.yaml delete mode 100644 .gitea/workflows/qrun-release.yaml delete mode 100644 .gitea/workflows/release-rc.yaml diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..ed42608 --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,7 @@ +name: build +on: + push: +jobs: + maven: + uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@main + secrets: inherit diff --git a/.gitea/workflows/qrun-release.yaml b/.gitea/workflows/qrun-release.yaml deleted file mode 100644 index cb745b2..0000000 --- a/.gitea/workflows/qrun-release.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Standard QRun release caller (v2, thin contract): every push and manual dispatch delegates to the protected qrun-release-v1 channel. Do not edit per repository. -name: qrun-release -on: - push: {} - workflow_dispatch: {} -permissions: - contents: read - actions: read -jobs: - release: - uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/java-maven.yaml@qrun-release-v1 - with: - runner: linux-small - browsers: true - browser-runner: browser-build - postpublish: - if: ${{ needs.release.outputs.capture == 'true' }} - needs: release - permissions: - contents: write - actions: write - uses: self:KofTwentyTwo/platform-ci/.gitea/workflows/qrun-source-version.yaml@qrun-release-v1 - with: - runner: linux-small diff --git a/.gitea/workflows/release-rc.yaml b/.gitea/workflows/release-rc.yaml deleted file mode 100644 index 12ec9fc..0000000 --- a/.gitea/workflows/release-rc.yaml +++ /dev/null @@ -1,72 +0,0 @@ -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 release run id (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 -permissions: - contents: read -jobs: - release-rc: - # The old qbit-rc-published repository_dispatch is replaced by the already-proven workflow_dispatch - # completion: the publisher's own ActionsWRITE job dispatches this workflow with finalize=true plus - # merge_sha/bump_sha/branch/version (same inputs as the manual finalize retry). - permissions: - contents: write - actions: read - pull-requests: write - if: github.event_name == 'workflow_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: self:KofTwentyTwo/platform-ci/.gitea/workflows/qbit-release-rc.yaml@qrun-release-v1 - with: - dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false - }} - finalize: ${{ 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: ${{ inputs.merge_sha || '' }} - bump_sha: ${{ inputs.bump_sha || '' }} - branch: ${{ inputs.branch || '' }} - version: ${{ inputs.version || '' }} - secrets: - QRUN_TAG_SIGNING_KEY: ${{ secrets.QRUN_TAG_SIGNING_KEY }} -- 2.54.0 From bf57535353ed8897bce9166f7f8fc6fed337d418 Mon Sep 17 00:00:00 2001 From: CI Date: Thu, 5 Nov 2026 16:27:14 -0600 Subject: [PATCH 16/19] ci: rerun shared workflow -- 2.54.0 From 7804c6aed78cb7fb020c56caeb3254630d8a788d Mon Sep 17 00:00:00 2001 From: James Maes Date: Fri, 6 Nov 2026 14:13:59 +0000 Subject: [PATCH 17/19] ci: pin shared workflow to v1 Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index ed42608..ce00351 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -3,5 +3,5 @@ on: push: jobs: maven: - uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@main + uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@v1 secrets: inherit -- 2.54.0 From cd3205e249f217a34b0a6aff1855623f2928b250 Mon Sep 17 00:00:00 2001 From: James Maes Date: Sat, 7 Nov 2026 18:09:23 +0000 Subject: [PATCH 18/19] build(deps): align with Gitea registry (parent 2.2.1, QQQ 4.1.0-RC.26) pom.xml: parent -> 2.2.1 pom.xml: qqq.snapshot.version: 4.1.0-RC.7 -> 4.1.0-RC.26 pom.xml: qqq-frontend-next: 1.0.0-RC.19 -> 1.0.0-RC.38 pom.xml: added qqq.version=4.1.0-RC.26 (parent BOM import) Co-Authored-By: Claude Opus 5.5 --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 8d01b73..541496a 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ + 4.1.0-RC.26 0.1.0-SNAPSHOT 21 21 @@ -34,7 +35,7 @@ com.kingsrook qbit-build-parent - 2.1.1 + 2.2.1 com.kingsrook.qbits @@ -77,7 +78,7 @@ com.kingsrook.qqq qqq-frontend-next - 1.0.0-RC.19 + 1.0.0-RC.38 test @@ -241,7 +242,7 @@ true - 4.1.0-RC.7 + 4.1.0-RC.26 -- 2.54.0 From 65f256351cdb920d566a1643350a069d208ed9d0 Mon Sep 17 00:00:00 2001 From: James Maes Date: Sat, 7 Nov 2026 13:49:04 -0600 Subject: [PATCH 19/19] refactor(delivery): adopt the core delivery SPI and classified values Replace the qqq#1205 STAND-IN copy of the delivery SPI with the QQQ 4.1 core types (com.kingsrook.qqq.backend.core.delivery). The producer now registers SlackDeliveryDestination as "slack" in the core DeliveryDestinationsMetaData registry instead of deferring host registration; a new test proves registration is idempotent and that core loads the implementation from the registry. Replace the qqq#1142 STAND-IN in SlackPayloadGuard: field classification comes from core ClassifiedValues (honoring a host audit-value protector) and tokenized restricted values use core ClassifiedValueTokens.changed(). Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 4 + .../qbits/slack/SlackQBitProducer.java | 2 +- .../delivery/SlackDeliveryDestination.java | 65 ++- .../delivery/standin/DeliveryAttachment.java | 193 -------- .../standin/DeliveryDestinationContext.java | 65 --- .../standin/DeliveryDestinationInterface.java | 69 --- .../delivery/standin/DeliveryRequest.java | 453 ------------------ .../delivery/standin/DeliveryResult.java | 139 ------ .../standin/DeliveryTargetOption.java | 129 ----- .../delivery/standin/DeliveryTargetStep.java | 226 --------- .../standin/QDeliveryDestinationMetaData.java | 396 --------------- .../qbits/slack/phi/SlackPayloadGuard.java | 22 +- .../slack/SlackDeliveryDestinationTest.java | 29 +- 13 files changed, 86 insertions(+), 1706 deletions(-) delete mode 100644 src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryAttachment.java delete mode 100644 src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryDestinationContext.java delete mode 100644 src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryDestinationInterface.java delete mode 100644 src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryRequest.java delete mode 100644 src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryResult.java delete mode 100644 src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryTargetOption.java delete mode 100644 src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryTargetStep.java delete mode 100644 src/main/java/com/kingsrook/qbits/slack/delivery/standin/QDeliveryDestinationMetaData.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d7dc82..f8ebb5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- `SlackDeliveryDestination` now implements the QQQ 4.1 core delivery SPI (`com.kingsrook.qqq.backend.core.delivery`, qqq#1205) and the producer registers it as `slack` in the core `DeliveryDestinationsMetaData` registry; the stand-in SPI copy is removed. +- `SlackPayloadGuard` uses core `ClassifiedValues` for field classification and `ClassifiedValueTokens.changed()` for tokenized restricted values (qqq#1142), replacing its stand-in logic. + ### Fixed - QBit metadata now reports the Maven artifact version, including release versions, from a filtered properties resource. diff --git a/src/main/java/com/kingsrook/qbits/slack/SlackQBitProducer.java b/src/main/java/com/kingsrook/qbits/slack/SlackQBitProducer.java index 2334439..bd56357 100644 --- a/src/main/java/com/kingsrook/qbits/slack/SlackQBitProducer.java +++ b/src/main/java/com/kingsrook/qbits/slack/SlackQBitProducer.java @@ -179,7 +179,7 @@ public class SlackQBitProducer implements QBitMetaDataProducer output.add(new SlackOutboxMessagingProviderMetaData().withName(qBitConfig.getBridgeProviderName())); output.addSelfToInstance(qInstance); - com.kingsrook.qbits.slack.delivery.SlackDeliveryDestination.register(); + com.kingsrook.qbits.slack.delivery.SlackDeliveryDestination.register(qInstance, getQBitConfig()); qInstance.addApp(com.kingsrook.qbits.slack.app.SlackApp.app(qInstance.getTables())); com.kingsrook.qbits.slack.app.SlackApp.widgets().forEach(qInstance::addWidget); if(isPreferencesAvailable()) diff --git a/src/main/java/com/kingsrook/qbits/slack/delivery/SlackDeliveryDestination.java b/src/main/java/com/kingsrook/qbits/slack/delivery/SlackDeliveryDestination.java index d9372df..40ccad8 100644 --- a/src/main/java/com/kingsrook/qbits/slack/delivery/SlackDeliveryDestination.java +++ b/src/main/java/com/kingsrook/qbits/slack/delivery/SlackDeliveryDestination.java @@ -26,15 +26,9 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import com.kingsrook.qbits.slack.SlackQBitConfig; import com.kingsrook.qbits.slack.api.SlackApi; import com.kingsrook.qbits.slack.api.SlackApiMessage; -import com.kingsrook.qbits.slack.delivery.standin.DeliveryDestinationContext; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) -import com.kingsrook.qbits.slack.delivery.standin.DeliveryDestinationInterface; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) -import com.kingsrook.qbits.slack.delivery.standin.DeliveryRequest; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) -import com.kingsrook.qbits.slack.delivery.standin.DeliveryResult; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) -import com.kingsrook.qbits.slack.delivery.standin.DeliveryTargetOption; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) -import com.kingsrook.qbits.slack.delivery.standin.DeliveryTargetStep; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) -import com.kingsrook.qbits.slack.delivery.standin.QDeliveryDestinationMetaData; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) import com.kingsrook.qbits.slack.metadata.SlackTables; import com.kingsrook.qbits.slack.outbox.SlackSendRequest; import com.kingsrook.qbits.slack.outbox.SlackSendResult; @@ -45,45 +39,72 @@ import com.kingsrook.qbits.slack.support.SlackRuntime; import com.kingsrook.qbits.slack.support.SlackStore; import com.kingsrook.qqq.backend.core.actions.ai.DataClassification; import com.kingsrook.qqq.backend.core.context.QContext; +import com.kingsrook.qqq.backend.core.delivery.DeliveryDestinationContext; +import com.kingsrook.qqq.backend.core.delivery.DeliveryDestinationInterface; +import com.kingsrook.qqq.backend.core.delivery.DeliveryDestinationsMetaData; +import com.kingsrook.qqq.backend.core.delivery.DeliveryRequest; +import com.kingsrook.qqq.backend.core.delivery.DeliveryResult; +import com.kingsrook.qqq.backend.core.delivery.DeliveryTargetOption; +import com.kingsrook.qqq.backend.core.delivery.DeliveryTargetStep; +import com.kingsrook.qqq.backend.core.delivery.QDeliveryDestinationMetaData; import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession; /******************************************************************************* - ** Host-callable Slack output destination until core's delivery SPI is released. - ** Targets are workspaceId and where (me or a channel address from SlackApi). + ** Slack output destination for the QQQ core delivery SPI + ** (DeliveryDestinationInterface). The producer registers it in the core + ** DeliveryDestinationsMetaData registry, so hosts that install core delivery + ** offer it in their pickers. Targets are workspaceId and where (me or a + ** channel address from SlackApi). *******************************************************************************/ public class SlackDeliveryDestination implements DeliveryDestinationInterface { + public static final String NAME = "slack"; + + + /******************************************************************************* - ** Describe the policy hosts must apply before invoking this destination. + ** Describe the policy core applies before invoking this destination, using + ** the Slack config of the current instance. *******************************************************************************/ public QDeliveryDestinationMetaData metadata() { - return (new QDeliveryDestinationMetaData().withName("slack").withLabel("Slack") + return (metadata(SlackRuntime.config())); + } + + + + /******************************************************************************* + ** Describe the policy core applies before invoking this destination. + *******************************************************************************/ + public static QDeliveryDestinationMetaData metadata(SlackQBitConfig config) + { + return (new QDeliveryDestinationMetaData().withName(NAME).withLabel("Slack") .withPermissionName("slack.send").withCodeReference(new QCodeReference(SlackDeliveryDestination.class)) .withMaxClassification(DataClassification.CONFIDENTIAL) .withAboveMaxClassification(QDeliveryDestinationMetaData.AboveMaxClassification.REFUSE) - .withMaxAttachmentBytes(SlackRuntime.config().getMaxAttachmentBytes()).withAllowBearerLinks(false)); + .withMaxAttachmentBytes(config.getMaxAttachmentBytes()).withAllowBearerLinks(false)); } + + /******************************************************************************* - ** Detect the real SPI without registering an incompatible temporary instance. + ** Register with the core delivery registry; production may run repeatedly, + ** and a destination already registered under this name is kept. *******************************************************************************/ - public static void register() + public static void register(QInstance instance, SlackQBitConfig config) { - try + DeliveryDestinationsMetaData registry = DeliveryDestinationsMetaData.ofOrWithNew(instance); + if(registry.getDestinations().stream().noneMatch(destination -> NAME.equals(destination.getName()))) { - Class.forName("com.kingsrook.qqq.backend.core.delivery.DeliveryDestinationsMetaData"); - com.kingsrook.qqq.backend.core.logging.QLogger.getLogger(SlackDeliveryDestination.class) - .info("Slack delivery stand-in will be replaced after RC.8; host registration is deferred"); // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - } - catch(ClassNotFoundException e) - { - // RC.6 hosts use this destination directly. + registry.withDestination(metadata(config)); } } + + /******************************************************************************* ** Offer only active connections and membership-checked targets of this user. *******************************************************************************/ diff --git a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryAttachment.java b/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryAttachment.java deleted file mode 100644 index 43339ce..0000000 --- a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryAttachment.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2026. 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.slack.delivery.standin; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - -// STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - - - - -/******************************************************************************* - ** DeliveryAttachment. - *******************************************************************************/ -public class DeliveryAttachment implements java.io.Serializable -{ - private String fileName; - private String mimeType; - private Long size; - private String storageTableName; - private String storageReference; - - - - /******************************************************************************* - ** Get fileName. - *******************************************************************************/ - public String getFileName() - { - return (fileName); - } - - - - /******************************************************************************* - ** Set fileName. - *******************************************************************************/ - public void setFileName(String fileName) - { - this.fileName = fileName; - } - - - - /******************************************************************************* - ** With fileName. - *******************************************************************************/ - public DeliveryAttachment withFileName(String fileName) - { - this.fileName = fileName; - return (this); - } - - - - /******************************************************************************* - ** Get mimeType. - *******************************************************************************/ - public String getMimeType() - { - return (mimeType); - } - - - - /******************************************************************************* - ** Set mimeType. - *******************************************************************************/ - public void setMimeType(String mimeType) - { - this.mimeType = mimeType; - } - - - - /******************************************************************************* - ** With mimeType. - *******************************************************************************/ - public DeliveryAttachment withMimeType(String mimeType) - { - this.mimeType = mimeType; - return (this); - } - - - - /******************************************************************************* - ** Get size. - *******************************************************************************/ - public Long getSize() - { - return (size); - } - - - - /******************************************************************************* - ** Set size. - *******************************************************************************/ - public void setSize(Long size) - { - this.size = size; - } - - - - /******************************************************************************* - ** With size. - *******************************************************************************/ - public DeliveryAttachment withSize(Long size) - { - this.size = size; - return (this); - } - - - - /******************************************************************************* - ** Get storageTableName. - *******************************************************************************/ - public String getStorageTableName() - { - return (storageTableName); - } - - - - /******************************************************************************* - ** Set storageTableName. - *******************************************************************************/ - public void setStorageTableName(String storageTableName) - { - this.storageTableName = storageTableName; - } - - - - /******************************************************************************* - ** With storageTableName. - *******************************************************************************/ - public DeliveryAttachment withStorageTableName(String storageTableName) - { - this.storageTableName = storageTableName; - return (this); - } - - - - /******************************************************************************* - ** Get storageReference. - *******************************************************************************/ - public String getStorageReference() - { - return (storageReference); - } - - - - /******************************************************************************* - ** Set storageReference. - *******************************************************************************/ - public void setStorageReference(String storageReference) - { - this.storageReference = storageReference; - } - - - - /******************************************************************************* - ** With storageReference. - *******************************************************************************/ - public DeliveryAttachment withStorageReference(String storageReference) - { - this.storageReference = storageReference; - return (this); - } -} diff --git a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryDestinationContext.java b/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryDestinationContext.java deleted file mode 100644 index 7dbb561..0000000 --- a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryDestinationContext.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2026. 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.slack.delivery.standin; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - -// STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - - - - -/******************************************************************************* - ** DeliveryDestinationContext. - *******************************************************************************/ -public class DeliveryDestinationContext implements java.io.Serializable -{ - private QDeliveryDestinationMetaData destination; - - - - /******************************************************************************* - ** Get destination. - *******************************************************************************/ - public QDeliveryDestinationMetaData getDestination() - { - return (destination); - } - - - - /******************************************************************************* - ** Set destination. - *******************************************************************************/ - public void setDestination(QDeliveryDestinationMetaData destination) - { - this.destination = destination; - } - - - - /******************************************************************************* - ** With destination. - *******************************************************************************/ - public DeliveryDestinationContext withDestination(QDeliveryDestinationMetaData destination) - { - this.destination = destination; - return (this); - } -} diff --git a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryDestinationInterface.java b/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryDestinationInterface.java deleted file mode 100644 index a5d5563..0000000 --- a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryDestinationInterface.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2026. 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.slack.delivery.standin; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - -// STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - - -import java.io.Serializable; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import com.kingsrook.qqq.backend.core.exceptions.QException; - - -/******************************************************************************* - ** Owner-scoped output delivery provider. - *******************************************************************************/ -public interface DeliveryDestinationInterface -{ - /******************************************************************************* - ** Describe the next target in the current owner session. - *******************************************************************************/ - DeliveryTargetStep describeTargets(DeliveryDestinationContext context, Map chosen) throws QException; - - - - /******************************************************************************* - ** Validate a complete target before sending. - *******************************************************************************/ - default List validate(DeliveryRequest request) throws QException - { - return (List.of()); - } - - - - /******************************************************************************* - ** Deliver idempotently using the request key. - *******************************************************************************/ - DeliveryResult deliver(DeliveryRequest request) throws QException; - - - - /******************************************************************************* - ** Reconcile a previous delivery when supported. - *******************************************************************************/ - default Optional lookup(String idempotencyKey) throws QException - { - return (Optional.empty()); - } -} diff --git a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryRequest.java b/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryRequest.java deleted file mode 100644 index 2e171c8..0000000 --- a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryRequest.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2026. 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.slack.delivery.standin; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - -// STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - - -import java.io.Serializable; -import java.util.List; -import java.util.Map; -import com.kingsrook.qqq.backend.core.actions.ai.DataClassification; - - -/******************************************************************************* - ** DeliveryRequest. - *******************************************************************************/ -public class DeliveryRequest implements java.io.Serializable -{ - private String destinationName; - private String ownerUserId; - private String sourceType; - private String sourceKey; - private String title; - private String summary; - private String link; - private String idempotencyKey; - private Map targetValues = Map.of(); - private List attachments = List.of(); - private DataClassification contentClassification; - private List restrictedFieldLabels = List.of(); - private boolean leaveOutRestricted; - - - - /******************************************************************************* - ** Get destinationName. - *******************************************************************************/ - public String getDestinationName() - { - return (destinationName); - } - - - - /******************************************************************************* - ** Set destinationName. - *******************************************************************************/ - public void setDestinationName(String destinationName) - { - this.destinationName = destinationName; - } - - - - /******************************************************************************* - ** With destinationName. - *******************************************************************************/ - public DeliveryRequest withDestinationName(String destinationName) - { - this.destinationName = destinationName; - return (this); - } - - - - /******************************************************************************* - ** Get ownerUserId. - *******************************************************************************/ - public String getOwnerUserId() - { - return (ownerUserId); - } - - - - /******************************************************************************* - ** Set ownerUserId. - *******************************************************************************/ - public void setOwnerUserId(String ownerUserId) - { - this.ownerUserId = ownerUserId; - } - - - - /******************************************************************************* - ** With ownerUserId. - *******************************************************************************/ - public DeliveryRequest withOwnerUserId(String ownerUserId) - { - this.ownerUserId = ownerUserId; - return (this); - } - - - - /******************************************************************************* - ** Get sourceType. - *******************************************************************************/ - public String getSourceType() - { - return (sourceType); - } - - - - /******************************************************************************* - ** Set sourceType. - *******************************************************************************/ - public void setSourceType(String sourceType) - { - this.sourceType = sourceType; - } - - - - /******************************************************************************* - ** With sourceType. - *******************************************************************************/ - public DeliveryRequest withSourceType(String sourceType) - { - this.sourceType = sourceType; - return (this); - } - - - - /******************************************************************************* - ** Get sourceKey. - *******************************************************************************/ - public String getSourceKey() - { - return (sourceKey); - } - - - - /******************************************************************************* - ** Set sourceKey. - *******************************************************************************/ - public void setSourceKey(String sourceKey) - { - this.sourceKey = sourceKey; - } - - - - /******************************************************************************* - ** With sourceKey. - *******************************************************************************/ - public DeliveryRequest withSourceKey(String sourceKey) - { - this.sourceKey = sourceKey; - return (this); - } - - - - /******************************************************************************* - ** Get title. - *******************************************************************************/ - public String getTitle() - { - return (title); - } - - - - /******************************************************************************* - ** Set title. - *******************************************************************************/ - public void setTitle(String title) - { - this.title = title; - } - - - - /******************************************************************************* - ** With title. - *******************************************************************************/ - public DeliveryRequest withTitle(String title) - { - this.title = title; - return (this); - } - - - - /******************************************************************************* - ** Get summary. - *******************************************************************************/ - public String getSummary() - { - return (summary); - } - - - - /******************************************************************************* - ** Set summary. - *******************************************************************************/ - public void setSummary(String summary) - { - this.summary = summary; - } - - - - /******************************************************************************* - ** With summary. - *******************************************************************************/ - public DeliveryRequest withSummary(String summary) - { - this.summary = summary; - return (this); - } - - - - /******************************************************************************* - ** Get link. - *******************************************************************************/ - public String getLink() - { - return (link); - } - - - - /******************************************************************************* - ** Set link. - *******************************************************************************/ - public void setLink(String link) - { - this.link = link; - } - - - - /******************************************************************************* - ** With link. - *******************************************************************************/ - public DeliveryRequest withLink(String link) - { - this.link = link; - return (this); - } - - - - /******************************************************************************* - ** Get idempotencyKey. - *******************************************************************************/ - public String getIdempotencyKey() - { - return (idempotencyKey); - } - - - - /******************************************************************************* - ** Set idempotencyKey. - *******************************************************************************/ - public void setIdempotencyKey(String idempotencyKey) - { - this.idempotencyKey = idempotencyKey; - } - - - - /******************************************************************************* - ** With idempotencyKey. - *******************************************************************************/ - public DeliveryRequest withIdempotencyKey(String idempotencyKey) - { - this.idempotencyKey = idempotencyKey; - return (this); - } - - - - /******************************************************************************* - ** Get targetValues. - *******************************************************************************/ - public Map getTargetValues() - { - return (targetValues); - } - - - - /******************************************************************************* - ** Set targetValues. - *******************************************************************************/ - public void setTargetValues(Map targetValues) - { - this.targetValues = targetValues; - } - - - - /******************************************************************************* - ** With targetValues. - *******************************************************************************/ - public DeliveryRequest withTargetValues(Map targetValues) - { - this.targetValues = targetValues; - return (this); - } - - - - /******************************************************************************* - ** Get attachments. - *******************************************************************************/ - public List getAttachments() - { - return (attachments); - } - - - - /******************************************************************************* - ** Set attachments. - *******************************************************************************/ - public void setAttachments(List attachments) - { - this.attachments = attachments; - } - - - - /******************************************************************************* - ** With attachments. - *******************************************************************************/ - public DeliveryRequest withAttachments(List attachments) - { - this.attachments = attachments; - return (this); - } - - - - /******************************************************************************* - ** Get contentClassification. - *******************************************************************************/ - public DataClassification getContentClassification() - { - return (contentClassification); - } - - - - /******************************************************************************* - ** Set contentClassification. - *******************************************************************************/ - public void setContentClassification(DataClassification contentClassification) - { - this.contentClassification = contentClassification; - } - - - - /******************************************************************************* - ** With contentClassification. - *******************************************************************************/ - public DeliveryRequest withContentClassification(DataClassification contentClassification) - { - this.contentClassification = contentClassification; - return (this); - } - - - - /******************************************************************************* - ** Get restrictedFieldLabels. - *******************************************************************************/ - public List getRestrictedFieldLabels() - { - return (restrictedFieldLabels); - } - - - - /******************************************************************************* - ** Set restrictedFieldLabels. - *******************************************************************************/ - public void setRestrictedFieldLabels(List restrictedFieldLabels) - { - this.restrictedFieldLabels = restrictedFieldLabels; - } - - - - /******************************************************************************* - ** With restrictedFieldLabels. - *******************************************************************************/ - public DeliveryRequest withRestrictedFieldLabels(List restrictedFieldLabels) - { - this.restrictedFieldLabels = restrictedFieldLabels; - return (this); - } - - - - /******************************************************************************* - ** Get leaveOutRestricted. - *******************************************************************************/ - public boolean getLeaveOutRestricted() - { - return (leaveOutRestricted); - } - - - - /******************************************************************************* - ** Set leaveOutRestricted. - *******************************************************************************/ - public void setLeaveOutRestricted(boolean leaveOutRestricted) - { - this.leaveOutRestricted = leaveOutRestricted; - } - - - - /******************************************************************************* - ** With leaveOutRestricted. - *******************************************************************************/ - public DeliveryRequest withLeaveOutRestricted(boolean leaveOutRestricted) - { - this.leaveOutRestricted = leaveOutRestricted; - return (this); - } -} diff --git a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryResult.java b/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryResult.java deleted file mode 100644 index b378d73..0000000 --- a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryResult.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2026. 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.slack.delivery.standin; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - -// STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - - -import java.util.List; - - -/******************************************************************************* - ** DeliveryResult. - *******************************************************************************/ -public class DeliveryResult implements java.io.Serializable -{ - /******************************************************************************* - ** Recorded delivery outcome. - *******************************************************************************/ - // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - public enum Status - { - DELIVERED, QUEUED, REFUSED, FAILED_RETRYABLE, FAILED, UNAUTHORIZED - } - - private Status status; - private String externalReference; - private List reasons = List.of(); - - - - /******************************************************************************* - ** Get status. - *******************************************************************************/ - public Status getStatus() - { - return (status); - } - - - - /******************************************************************************* - ** Set status. - *******************************************************************************/ - public void setStatus(Status status) - { - this.status = status; - } - - - - /******************************************************************************* - ** With status. - *******************************************************************************/ - public DeliveryResult withStatus(Status status) - { - this.status = status; - return (this); - } - - - - /******************************************************************************* - ** Get externalReference. - *******************************************************************************/ - public String getExternalReference() - { - return (externalReference); - } - - - - /******************************************************************************* - ** Set externalReference. - *******************************************************************************/ - public void setExternalReference(String externalReference) - { - this.externalReference = externalReference; - } - - - - /******************************************************************************* - ** With externalReference. - *******************************************************************************/ - public DeliveryResult withExternalReference(String externalReference) - { - this.externalReference = externalReference; - return (this); - } - - - - /******************************************************************************* - ** Get reasons. - *******************************************************************************/ - public List getReasons() - { - return (reasons); - } - - - - /******************************************************************************* - ** Set reasons. - *******************************************************************************/ - public void setReasons(List reasons) - { - this.reasons = reasons; - } - - - - /******************************************************************************* - ** With reasons. - *******************************************************************************/ - public DeliveryResult withReasons(List reasons) - { - this.reasons = reasons; - return (this); - } -} diff --git a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryTargetOption.java b/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryTargetOption.java deleted file mode 100644 index 80aa21f..0000000 --- a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryTargetOption.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2026. 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.slack.delivery.standin; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - -// STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - - - - -/******************************************************************************* - ** DeliveryTargetOption. - *******************************************************************************/ -public class DeliveryTargetOption implements java.io.Serializable -{ - private String value; - private String label; - private String description; - - - - /******************************************************************************* - ** Get value. - *******************************************************************************/ - public String getValue() - { - return (value); - } - - - - /******************************************************************************* - ** Set value. - *******************************************************************************/ - public void setValue(String value) - { - this.value = value; - } - - - - /******************************************************************************* - ** With value. - *******************************************************************************/ - public DeliveryTargetOption withValue(String value) - { - this.value = value; - return (this); - } - - - - /******************************************************************************* - ** Get label. - *******************************************************************************/ - public String getLabel() - { - return (label); - } - - - - /******************************************************************************* - ** Set label. - *******************************************************************************/ - public void setLabel(String label) - { - this.label = label; - } - - - - /******************************************************************************* - ** With label. - *******************************************************************************/ - public DeliveryTargetOption withLabel(String label) - { - this.label = label; - return (this); - } - - - - /******************************************************************************* - ** Get description. - *******************************************************************************/ - public String getDescription() - { - return (description); - } - - - - /******************************************************************************* - ** Set description. - *******************************************************************************/ - public void setDescription(String description) - { - this.description = description; - } - - - - /******************************************************************************* - ** With description. - *******************************************************************************/ - public DeliveryTargetOption withDescription(String description) - { - this.description = description; - return (this); - } -} diff --git a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryTargetStep.java b/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryTargetStep.java deleted file mode 100644 index e0ca14b..0000000 --- a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/DeliveryTargetStep.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2026. 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.slack.delivery.standin; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - -// STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - - -import java.util.List; - - -/******************************************************************************* - ** DeliveryTargetStep. - *******************************************************************************/ -public class DeliveryTargetStep implements java.io.Serializable -{ - private String name; - private String label; - private List options = List.of(); - private boolean freeText; - private boolean complete; - private String summary; - - - - /******************************************************************************* - ** Get name. - *******************************************************************************/ - public String getName() - { - return (name); - } - - - - /******************************************************************************* - ** Set name. - *******************************************************************************/ - public void setName(String name) - { - this.name = name; - } - - - - /******************************************************************************* - ** With name. - *******************************************************************************/ - public DeliveryTargetStep withName(String name) - { - this.name = name; - return (this); - } - - - - /******************************************************************************* - ** Get label. - *******************************************************************************/ - public String getLabel() - { - return (label); - } - - - - /******************************************************************************* - ** Set label. - *******************************************************************************/ - public void setLabel(String label) - { - this.label = label; - } - - - - /******************************************************************************* - ** With label. - *******************************************************************************/ - public DeliveryTargetStep withLabel(String label) - { - this.label = label; - return (this); - } - - - - /******************************************************************************* - ** Get options. - *******************************************************************************/ - public List getOptions() - { - return (options); - } - - - - /******************************************************************************* - ** Set options. - *******************************************************************************/ - public void setOptions(List options) - { - this.options = options; - } - - - - /******************************************************************************* - ** With options. - *******************************************************************************/ - public DeliveryTargetStep withOptions(List options) - { - this.options = options; - return (this); - } - - - - /******************************************************************************* - ** Get freeText. - *******************************************************************************/ - public boolean getFreeText() - { - return (freeText); - } - - - - /******************************************************************************* - ** Set freeText. - *******************************************************************************/ - public void setFreeText(boolean freeText) - { - this.freeText = freeText; - } - - - - /******************************************************************************* - ** With freeText. - *******************************************************************************/ - public DeliveryTargetStep withFreeText(boolean freeText) - { - this.freeText = freeText; - return (this); - } - - - - /******************************************************************************* - ** Get complete. - *******************************************************************************/ - public boolean getComplete() - { - return (complete); - } - - - - /******************************************************************************* - ** Set complete. - *******************************************************************************/ - public void setComplete(boolean complete) - { - this.complete = complete; - } - - - - /******************************************************************************* - ** With complete. - *******************************************************************************/ - public DeliveryTargetStep withComplete(boolean complete) - { - this.complete = complete; - return (this); - } - - - - /******************************************************************************* - ** Get summary. - *******************************************************************************/ - public String getSummary() - { - return (summary); - } - - - - /******************************************************************************* - ** Set summary. - *******************************************************************************/ - public void setSummary(String summary) - { - this.summary = summary; - } - - - - /******************************************************************************* - ** With summary. - *******************************************************************************/ - public DeliveryTargetStep withSummary(String summary) - { - this.summary = summary; - return (this); - } -} diff --git a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/QDeliveryDestinationMetaData.java b/src/main/java/com/kingsrook/qbits/slack/delivery/standin/QDeliveryDestinationMetaData.java deleted file mode 100644 index 1efc14e..0000000 --- a/src/main/java/com/kingsrook/qbits/slack/delivery/standin/QDeliveryDestinationMetaData.java +++ /dev/null @@ -1,396 +0,0 @@ -/* - * QQQ - Low-code Application Framework for Engineers. - * Copyright (C) 2021-2026. 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.slack.delivery.standin; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - -// STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - - -import com.kingsrook.qqq.backend.core.actions.ai.DataClassification; -import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; - - -/******************************************************************************* - ** QDeliveryDestinationMetaData. - *******************************************************************************/ -public class QDeliveryDestinationMetaData implements java.io.Serializable -{ - /******************************************************************************* - ** Policy for content above the destination ceiling. - *******************************************************************************/ - // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) - public enum AboveMaxClassification - { - REFUSE, LINK_ONLY - } - - private String name; - private String label; - private String icon; - private String permissionName; - private String emailProviderName; - private Integer order = 0; - private QCodeReference codeReference; - private DataClassification maxClassification = DataClassification.PUBLIC; - private Long maxAttachmentBytes; - private boolean allowBearerLinks; - private AboveMaxClassification aboveMaxClassification = AboveMaxClassification.REFUSE; - - - - /******************************************************************************* - ** Get name. - *******************************************************************************/ - public String getName() - { - return (name); - } - - - - /******************************************************************************* - ** Set name. - *******************************************************************************/ - public void setName(String name) - { - this.name = name; - } - - - - /******************************************************************************* - ** With name. - *******************************************************************************/ - public QDeliveryDestinationMetaData withName(String name) - { - this.name = name; - return (this); - } - - - - /******************************************************************************* - ** Get label. - *******************************************************************************/ - public String getLabel() - { - return (label); - } - - - - /******************************************************************************* - ** Set label. - *******************************************************************************/ - public void setLabel(String label) - { - this.label = label; - } - - - - /******************************************************************************* - ** With label. - *******************************************************************************/ - public QDeliveryDestinationMetaData withLabel(String label) - { - this.label = label; - return (this); - } - - - - /******************************************************************************* - ** Get icon. - *******************************************************************************/ - public String getIcon() - { - return (icon); - } - - - - /******************************************************************************* - ** Set icon. - *******************************************************************************/ - public void setIcon(String icon) - { - this.icon = icon; - } - - - - /******************************************************************************* - ** With icon. - *******************************************************************************/ - public QDeliveryDestinationMetaData withIcon(String icon) - { - this.icon = icon; - return (this); - } - - - - /******************************************************************************* - ** Get permissionName. - *******************************************************************************/ - public String getPermissionName() - { - return (permissionName); - } - - - - /******************************************************************************* - ** Set permissionName. - *******************************************************************************/ - public void setPermissionName(String permissionName) - { - this.permissionName = permissionName; - } - - - - /******************************************************************************* - ** With permissionName. - *******************************************************************************/ - public QDeliveryDestinationMetaData withPermissionName(String permissionName) - { - this.permissionName = permissionName; - return (this); - } - - - - /******************************************************************************* - ** Get emailProviderName. - *******************************************************************************/ - public String getEmailProviderName() - { - return (emailProviderName); - } - - - - /******************************************************************************* - ** Set emailProviderName. - *******************************************************************************/ - public void setEmailProviderName(String emailProviderName) - { - this.emailProviderName = emailProviderName; - } - - - - /******************************************************************************* - ** With emailProviderName. - *******************************************************************************/ - public QDeliveryDestinationMetaData withEmailProviderName(String emailProviderName) - { - this.emailProviderName = emailProviderName; - return (this); - } - - - - /******************************************************************************* - ** Get order. - *******************************************************************************/ - public Integer getOrder() - { - return (order); - } - - - - /******************************************************************************* - ** Set order. - *******************************************************************************/ - public void setOrder(Integer order) - { - this.order = order; - } - - - - /******************************************************************************* - ** With order. - *******************************************************************************/ - public QDeliveryDestinationMetaData withOrder(Integer order) - { - this.order = order; - return (this); - } - - - - /******************************************************************************* - ** Get codeReference. - *******************************************************************************/ - public QCodeReference getCodeReference() - { - return (codeReference); - } - - - - /******************************************************************************* - ** Set codeReference. - *******************************************************************************/ - public void setCodeReference(QCodeReference codeReference) - { - this.codeReference = codeReference; - } - - - - /******************************************************************************* - ** With codeReference. - *******************************************************************************/ - public QDeliveryDestinationMetaData withCodeReference(QCodeReference codeReference) - { - this.codeReference = codeReference; - return (this); - } - - - - /******************************************************************************* - ** Get maxClassification. - *******************************************************************************/ - public DataClassification getMaxClassification() - { - return (maxClassification); - } - - - - /******************************************************************************* - ** Set maxClassification. - *******************************************************************************/ - public void setMaxClassification(DataClassification maxClassification) - { - this.maxClassification = maxClassification; - } - - - - /******************************************************************************* - ** With maxClassification. - *******************************************************************************/ - public QDeliveryDestinationMetaData withMaxClassification(DataClassification maxClassification) - { - this.maxClassification = maxClassification; - return (this); - } - - - - /******************************************************************************* - ** Get maxAttachmentBytes. - *******************************************************************************/ - public Long getMaxAttachmentBytes() - { - return (maxAttachmentBytes); - } - - - - /******************************************************************************* - ** Set maxAttachmentBytes. - *******************************************************************************/ - public void setMaxAttachmentBytes(Long maxAttachmentBytes) - { - this.maxAttachmentBytes = maxAttachmentBytes; - } - - - - /******************************************************************************* - ** With maxAttachmentBytes. - *******************************************************************************/ - public QDeliveryDestinationMetaData withMaxAttachmentBytes(Long maxAttachmentBytes) - { - this.maxAttachmentBytes = maxAttachmentBytes; - return (this); - } - - - - /******************************************************************************* - ** Get allowBearerLinks. - *******************************************************************************/ - public boolean getAllowBearerLinks() - { - return (allowBearerLinks); - } - - - - /******************************************************************************* - ** Set allowBearerLinks. - *******************************************************************************/ - public void setAllowBearerLinks(boolean allowBearerLinks) - { - this.allowBearerLinks = allowBearerLinks; - } - - - - /******************************************************************************* - ** With allowBearerLinks. - *******************************************************************************/ - public QDeliveryDestinationMetaData withAllowBearerLinks(boolean allowBearerLinks) - { - this.allowBearerLinks = allowBearerLinks; - return (this); - } - - - - /******************************************************************************* - ** Get aboveMaxClassification. - *******************************************************************************/ - public AboveMaxClassification getAboveMaxClassification() - { - return (aboveMaxClassification); - } - - - - /******************************************************************************* - ** Set aboveMaxClassification. - *******************************************************************************/ - public void setAboveMaxClassification(AboveMaxClassification aboveMaxClassification) - { - this.aboveMaxClassification = aboveMaxClassification; - } - - - - /******************************************************************************* - ** With aboveMaxClassification. - *******************************************************************************/ - public QDeliveryDestinationMetaData withAboveMaxClassification(AboveMaxClassification aboveMaxClassification) - { - this.aboveMaxClassification = aboveMaxClassification; - return (this); - } -} diff --git a/src/main/java/com/kingsrook/qbits/slack/phi/SlackPayloadGuard.java b/src/main/java/com/kingsrook/qbits/slack/phi/SlackPayloadGuard.java index 11b720e..96d530c 100644 --- a/src/main/java/com/kingsrook/qbits/slack/phi/SlackPayloadGuard.java +++ b/src/main/java/com/kingsrook/qbits/slack/phi/SlackPayloadGuard.java @@ -27,15 +27,16 @@ import java.util.Map; import java.util.regex.Pattern; import com.kingsrook.qbits.slack.support.SlackRuntime; import com.kingsrook.qbits.slack.template.VariableSchema; -import com.kingsrook.qqq.backend.core.actions.ai.AiDataClassifications; import com.kingsrook.qqq.backend.core.actions.ai.DataClassification; +import com.kingsrook.qqq.backend.core.actions.permissions.fieldaccess.ClassifiedValueTokens; +import com.kingsrook.qqq.backend.core.actions.permissions.fieldaccess.ClassifiedValues; import com.kingsrook.qqq.backend.core.context.QContext; /******************************************************************************* - ** Refuse restricted content before persistence. Uses RC.6 field classifications. - ** STAND-IN(core=QRun-IO/qqq#1142, removeBy=4.1.0-RC.8): ClassifiedValues and - ** ClassifiedValueTokens are absent in RC.6. Restricted tokens use changed:v1, - ** a deliberately non-reversible marker, never a hash of a low-entropy value. + ** Refuse restricted content before persistence. Field classification comes + ** from core ClassifiedValues (which honors a host's audit-value protector); + ** tokenized restricted values become core ClassifiedValueTokens.changed(), a + ** deliberately non-reversible marker, never a hash of a low-entropy value. *******************************************************************************/ public final class SlackPayloadGuard { @@ -88,7 +89,10 @@ public final class SlackPayloadGuard { throw (new IllegalArgumentException("Unknown classified field")); } - classification = DataClassification.max(classification, AiDataClassifications.ofField(table, field)); + if(ClassifiedValues.isClassified(table, field)) + { + return (true); + } } return (classification != null && classification.isAtLeast(DataClassification.PII)); } @@ -123,7 +127,7 @@ public final class SlackPayloadGuard { throw (new RestrictedVariableException(declaration.name())); } - safe.put(entry.getKey(), "changed:v1"); + safe.put(entry.getKey(), ClassifiedValueTokens.changed()); } else { @@ -145,7 +149,7 @@ public final class SlackPayloadGuard { throw (new RestrictedVariableException(declaration.name())); } - safe.put(declaration.name(), "changed:v1"); + safe.put(declaration.name(), ClassifiedValueTokens.changed()); } } return (safe); @@ -168,7 +172,7 @@ public final class SlackPayloadGuard { throw (new IllegalArgumentException("Restricted template variable")); } - return ("changed:v1"); + return (ClassifiedValueTokens.changed()); } return (sanitize(classified.value(), tokenize)); } diff --git a/src/test/java/com/kingsrook/qbits/slack/SlackDeliveryDestinationTest.java b/src/test/java/com/kingsrook/qbits/slack/SlackDeliveryDestinationTest.java index 75df0fc..75f1cba 100644 --- a/src/test/java/com/kingsrook/qbits/slack/SlackDeliveryDestinationTest.java +++ b/src/test/java/com/kingsrook/qbits/slack/SlackDeliveryDestinationTest.java @@ -25,16 +25,18 @@ import java.util.Map; import com.kingsrook.qbits.slack.api.SlackApi; import com.kingsrook.qbits.slack.delivery.SlackDeliveryDestination; import com.kingsrook.qbits.slack.delivery.SlackDispatcher; -import com.kingsrook.qbits.slack.delivery.standin.DeliveryAttachment; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) -import com.kingsrook.qbits.slack.delivery.standin.DeliveryDestinationContext; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) -import com.kingsrook.qbits.slack.delivery.standin.DeliveryRequest; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) -import com.kingsrook.qbits.slack.delivery.standin.DeliveryResult; // STAND-IN(core=QRun-IO/qqq#1205, removeBy=4.1.0-RC.8) import com.kingsrook.qbits.slack.metadata.SlackTables; import com.kingsrook.qbits.slack.security.SlackSecurity; import com.kingsrook.qbits.slack.support.SlackStore; import com.kingsrook.qbits.slack.template.SlackTemplates; import com.kingsrook.qqq.backend.core.actions.ai.DataClassification; import com.kingsrook.qqq.backend.core.actions.tables.StorageAction; +import com.kingsrook.qqq.backend.core.delivery.DeliveryAttachment; +import com.kingsrook.qqq.backend.core.delivery.DeliveryDestinationContext; +import com.kingsrook.qqq.backend.core.delivery.DeliveryDestinations; +import com.kingsrook.qqq.backend.core.delivery.DeliveryDestinationsMetaData; +import com.kingsrook.qqq.backend.core.delivery.DeliveryRequest; +import com.kingsrook.qqq.backend.core.delivery.DeliveryResult; import com.kingsrook.qqq.backend.core.model.actions.tables.storage.StorageInput; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; @@ -126,6 +128,25 @@ class SlackDeliveryDestinationTest assertThat(context.getDestination().getMaxAttachmentBytes()).isEqualTo(env.config().getMaxAttachmentBytes()); } + /******************************************************************************* + ** The producer registers Slack once in the core delivery registry, and core + ** loads this implementation from it. + *******************************************************************************/ + @Test + void registeredWithCoreDelivery() throws Exception + { + var registry = DeliveryDestinationsMetaData.of(env.instance()); + assertThat(registry).isNotNull(); + SlackDeliveryDestination.register(env.instance(), env.config()); + var registered = registry.getDestinations().stream().filter(d -> SlackDeliveryDestination.NAME.equals(d.getName())).toList(); + assertThat(registered).singleElement().satisfies(d -> + { + assertThat(d.getPermissionName()).isEqualTo("slack.send"); + assertThat(d.getMaxAttachmentBytes()).isEqualTo(env.config().getMaxAttachmentBytes()); + }); + assertThat(DeliveryDestinations.implementation(registered.getFirst())).isInstanceOf(SlackDeliveryDestination.class); + } + /******************************************************************************* ** DM and channel use the delivery template and preserve the idempotency key. *******************************************************************************/ -- 2.54.0