Compare commits

...
Author SHA1 Message Date
CircleCI ec03b810be Bump version to 0.1.0-RC.13 [skip ci] 2026-10-15 12:41:30 +00:00
github-actions[bot] eddbe20fd7 Merge develop into release/0.1 for the next 0.1.0 RC 2026-10-15 12:38:37 +00:00
james.maes 4a7dd2bb71 Merge pull request #42 from QRun-IO/fix/alerts-system-session-instance
fix(alerts): asSystem builds its system session with the runtime instance
2026-10-15 07:38:26 -05:00
james.maesandClaude Opus 5.5 b638741c61 fix(alerts): asSystem builds its system session with the runtime instance
The alerts-sink drain thread (and schedulers) run engine work through
AlertsRuntime.asSystem with no QContext, so new QSystemUserSession()
read a null instance and warned "QInstance was not supplied" on every
call. AlertsSecurity.systemSession(QInstance) takes the instance; the
no-arg form delegates with the context instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:25:44 -05:00
james.maes 7f79f86222 Merge pull request #41 from QRun-IO/fix/producer-version-from-build
fix: qbit metadata version comes from the build
2026-10-15 01:42:57 -05:00
james.maesandClaude Opus 5.5 69b94a13f5 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-alerts.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 <noreply@anthropic.com>
2026-10-15 01:23:05 -05:00
james.maes 884eab9432 Merge pull request #40 from QRun-IO/fix/help-bundle-version-test
test(help): bundle version test compares with the artifact version
2026-10-14 23:18:42 -05:00
CircleCI 17017cc693 Bump version to 0.1.0-RC.12 [skip ci] 2026-10-15 02:14:59 +00:00
james.maesandClaude Opus 5.5 1bbdff2fd9 Merge develop into release/0.1 for 0.1.0-RC.12 (#38 help bundle)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:10:04 -05:00
CircleCI 1c5aa5d33b Bump version to 0.1.0-RC.11 [skip ci] 2026-10-14 23:58:00 +00:00
james.maesandClaude Opus 5.5 1a911284ef Merge develop into release/0.1 for 0.1.0-RC.11 (#37 email REQUESTED recipients immediately)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:54:26 -05:00
CircleCI a4116b3c75 Bump version to 0.1.0-RC.10 [skip ci] 2026-10-14 23:08:43 +00:00
james.maesandClaude Opus 5.5 6c3fb450eb Merge develop into release/0.1 for 0.1.0-RC.10 (#33 duplicate notifications, #32, #35 indexes and FKs)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:03:54 -05:00
CircleCI 7f51d8ee93 Bump version to 0.1.0-RC.9 [skip ci] 2026-10-14 22:38:07 +00:00
james.maesandClaude Opus 5.5 cc297f649a Merge develop into release/0.1 for 0.1.0-RC.9 (#36 seeds, Setup card, coverage wiring)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:33:42 -05:00
CircleCI 289acee3cc Bump version to 0.1.0-RC.8 [skip ci] 2026-10-14 21:49:55 +00:00
james.maesandClaude Opus 5.5 8ca9fff6a1 Merge develop into release/0.1 for 0.1.0-RC.8 (#18 Notifications preferences section, QQQ RC.7)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 16:45:03 -05:00
CircleCI c67e1a343d Bump version to 0.1.0-RC.7 [skip ci] 2026-10-14 21:24:49 +00:00
james.maesandClaude Opus 5.5 4fe1db3462 Merge develop into release/0.1 for 0.1.0-RC.7 (#34 child lists JSON serialization fix)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 16:20:57 -05:00
CircleCI 27d471a8e1 Bump version to 0.1.0-RC.6 [skip ci] 2026-10-14 20:24:26 +00:00
james.maesandClaude Opus 5.5 ee54fc4ba5 Merge develop into release/0.1 for 0.1.0-RC.6 (per-user Slack delivery)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 15:21:06 -05:00
CircleCI bba86368d9 Bump version to 0.1.0-RC.5 [skip ci] 2026-10-14 16:22:13 +00:00
james.maesandClaude Opus 5.5 fb88168107 Merge develop into release/0.1 for 0.1.0-RC.5 (#23 #26 #28 #29 #30)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 11:17:45 -05:00
CircleCI 6d4b80793b Bump version to 0.1.0-RC.4 [skip ci] 2026-10-14 12:51:58 +00:00
james.maesandClaude Opus 5.5 da5aba1f39 Merge develop into release/0.1 for 0.1.0-RC.4 (#19 #21 #24)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:48:10 -05:00
CircleCI dbbcb97baf Bump version to 0.1.0-RC.3 [skip ci] 2026-10-14 00:51:41 +00:00
james.maesandClaude Opus 5.5 a84a10a4cd Merge develop into release/0.1 for 0.1.0-RC.3 (core QAlerts sink and WebhookSigner, #17)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:49:04 -05:00
CircleCI dae98dae82 Bump version to 0.1.0-RC.2 [skip ci] 2026-10-13 16:33:36 +00:00
james.maesandClaude Opus 5.5 aede554b25 Merge develop into release/0.1 for 0.1.0-RC.2
User notifications: spec (#8), tables, admin, audiences and bell API
(#9), job-failure notices (#10), security/share/release notices, email
digest and link hardening (#11).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:31:35 -05:00
CircleCI 567b4833fc Bump version to 0.1.0-RC.1 [skip ci] 2026-10-12 22:57:11 +00:00
9 changed files with 110 additions and 5 deletions

No files matched your search

+1
View File
@@ -5,6 +5,7 @@ All notable changes to this project are documented here. The format follows Keep
## [0.1.0] - Unreleased
### Fixed
- Read the QBit metadata version from the Maven-filtered artifact version so release builds report their release version.
- Record-view child lists (e.g. a notification's Recipients) failed with "Error in JSON Serialization": the child table's meta-data includes the qbit config, and `AlertsQBitConfig.getRuntime()` points at a runtime that points back at the config, so Jackson recursed to its 1000-level nesting limit. `getRuntime()` and `getAudienceResolver()` are now `@JsonIgnore`, as in the other qbits' configs. A regression test renders and serializes every child-list widget.
### Added
+3 -1
View File
@@ -18,7 +18,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<properties>
<revision>0.1.0-SNAPSHOT</revision>
<revision>0.1.0-RC.13</revision>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<jacoco.version>0.8.15</jacoco.version>
@@ -160,6 +160,7 @@
<filtering>true</filtering>
<includes>
<include>help/*.yaml</include>
<include>qbit-alerts.properties</include>
</includes>
</resource>
<resource>
@@ -167,6 +168,7 @@
<filtering>false</filtering>
<excludes>
<exclude>help/*.yaml</exclude>
<exclude>qbit-alerts.properties</exclude>
</excludes>
</resource>
</resources>
@@ -17,8 +17,11 @@
package com.kingsrook.qbits.alerts;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import com.kingsrook.qbits.alerts.delivery.DeliveryWorker;
import com.kingsrook.qbits.alerts.engine.AlertsProcessListener;
import com.kingsrook.qbits.alerts.engine.AlertsSinkAdapter;
@@ -66,7 +69,7 @@ public class AlertsQBitProducer implements QBitMetaDataProducer<AlertsQBitConfig
public static final String GROUP_ID = "com.kingsrook.qbits";
public static final String ARTIFACT_ID = "qbit-alerts";
public static final String VERSION = "0.1.0";
public static final String VERSION = loadVersion();
private static final String JAVALIN_CLASS = "com.kingsrook.qqq.middleware.javalin.QJavalinMetaData";
@@ -75,6 +78,34 @@ public class AlertsQBitProducer implements QBitMetaDataProducer<AlertsQBitConfig
/*******************************************************************************
** Read the artifact version from the Maven-filtered resource.
*******************************************************************************/
private static String loadVersion()
{
try(InputStream inputStream = AlertsQBitProducer.class.getClassLoader().getResourceAsStream("qbit-alerts.properties"))
{
if(inputStream == null)
{
throw (new IllegalStateException("Missing qbit-alerts.properties; build with Maven to generate 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 invalid version in qbit-alerts.properties; build with Maven resource filtering enabled"));
}
return (version);
}
catch(IOException e)
{
throw (new IllegalStateException("Could not read artifact version from qbit-alerts.properties", e));
}
}
/*******************************************************************************
** Fluent setter for the config.
*******************************************************************************/
@@ -144,7 +144,7 @@ public class AlertsRuntime
public <R> R asSystem(SystemWork<R> work) throws QException
{
Object[] result = new Object[1];
QContext.withTemporaryContext(new CapturedContext(qInstance, AlertsSecurity.systemSession()), () ->
QContext.withTemporaryContext(new CapturedContext(qInstance, AlertsSecurity.systemSession(qInstance)), () ->
{
result[0] = work.run();
});
@@ -278,7 +278,19 @@ public final class AlertsSecurity
*******************************************************************************/
public static QSystemUserSession systemSession()
{
QSystemUserSession session = new QSystemUserSession();
return (systemSession(QContext.getQInstance()));
}
/*******************************************************************************
** A system-user session for this instance, holding the alerts all-access
** keys. Background threads (the alerts sink, schedulers) have no QContext
** yet, so they must pass the instance in.
*******************************************************************************/
public static QSystemUserSession systemSession(QInstance qInstance)
{
QSystemUserSession session = new QSystemUserSession(qInstance);
session.withSecurityKeyValue(KEY_USER_ALL_ACCESS, true);
session.withSecurityKeyValue(KEY_PERMISSION_ALL, true);
session.withSecurityKeyValue(KEY_READABLE_TABLE_ALL, true);
@@ -0,0 +1 @@
version=${project.version}
@@ -64,6 +64,8 @@ class AlertsQBitProducerTest extends BaseAlertsTest
void testProduce()
{
assertThat(qInstance.getQBits()).hasSize(1);
assertThat(System.getProperty("qbit.artifactVersion")).isNotBlank().isEqualTo(AlertsQBitProducer.VERSION);
assertThat(qInstance.getQBits().values().iterator().next().getVersion()).isEqualTo(System.getProperty("qbit.artifactVersion"));
assertThat(qInstance.getQBits().values().iterator().next().getArtifactId()).isEqualTo(AlertsQBitProducer.ARTIFACT_ID);
assertThat(AlertsRuntime.find(qInstance)).isSameAs(runtime);
assertThat(AlertsRuntime.find(new QInstance())).isNull();
@@ -0,0 +1,56 @@
/*
* Copyright 2026 Kingsrook, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.alerts;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.logging.QCollectingLogger;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/*******************************************************************************
** Background threads (the alerts sink, schedulers) run engine work through
** asSystem with no QContext of their own; the system session must be built
** from the runtime instance, not from the (empty) context.
*******************************************************************************/
class AlertsSystemSessionTest extends BaseAlertsTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void asSystemWithoutContextUsesTheRuntimeInstance() throws Exception
{
QContext.clear();
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(QSystemUserSession.class);
try
{
Boolean systemUser = runtime.asSystem(() -> QContext.getQInstance() != null && AlertsSecurity.isSystemUser());
assertThat(systemUser).isTrue();
assertThat(collectingLogger.getCollectedMessages())
.noneMatch(m -> m.getMessage().contains("QInstance was not supplied"));
}
finally
{
QLogger.deactivateCollectingLoggerForClass(QSystemUserSession.class);
}
}
}
@@ -158,7 +158,7 @@ class AlertsTablesGuardrailTest extends BaseAlertsTest
assertThat(HelpBundles.of(qInstance).getBundles()).singleElement().satisfies(bundle ->
{
assertThat(bundle.getId()).isEqualTo("qbit-alerts");
assertThat(bundle.getVersion()).as("the help bundle carries the artifact version").isEqualTo(System.getProperty("qbit.artifactVersion"));
assertThat(bundle.getVersion()).as("the help bundle carries the artifact and producer version").isEqualTo(System.getProperty("qbit.artifactVersion")).isEqualTo(AlertsQBitProducer.VERSION);
});
HelpBundles.syncOnStartup(qInstance);
}