feat(release): migrate extension template to QQQ 4.0 #4

Merged
KofTwentyTwo merged 4 commits from feature/qqq-4-polish into main 2026-09-24 18:32:16 +00:00
6 changed files with 46 additions and 92 deletions

No files matched your search

+13 -1
View File
@@ -3,7 +3,19 @@ version: 2.1
orbs:
qqq-orb: kingsrook/qqq-orb@0.5.0
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
workflows:
build:
jobs:
- qqq-orb/mvn_test
- qqq-orb/mvn_test_only:
pre-steps:
- configure-package-install
+4
View File
@@ -2,6 +2,10 @@
GitHub template repository for scaffolding QQQ "Extension QBits" (infrastructure-level plugins: table customizers, auth, audit, action wrappers). Not a shipping library — consumers click "Use this template" and rename the `com.kingsrook.qbits.example` package.
## QQQ 4.0 baseline — 2026-09-24
The historical review below predates the 4.0 migration. This template targets Java 21 and the public QQQ 4.0.0 release from Maven Central. The corrected APIs, license references and actual template commands are in README.md and docs/00-getting-started.md. Preserve the documented multi-instance and scaffold limitations.
## Knowledge base
Reviewed dossiers for this repo and the wider QQQ platform live in the second-brain vault:
+4 -10
View File
@@ -13,11 +13,7 @@ Extension QBits extend or enhance how QQQ operates at the infrastructure/framewo
## Quick Start
1. Click "Use this template" to create your QBit
2. Run `scripts/customize_template.py` to rename the example
3. Implement your extension logic
4. Add tests
5. Publish to Maven repository
Requires **Java 21**, **Maven 3.8+**, and QQQ **4.0.0**. Create your repository, customize its Maven coordinates and Java package/classes, implement the table customizer, then run `mvn clean verify`. See [Getting Started](docs/00-getting-started.md).
## Structure
@@ -25,10 +21,8 @@ Extension QBits extend or enhance how QQQ operates at the infrastructure/framewo
src/main/java/com/kingsrook/qbits/example/
├── ExampleExtensionQBitConfig.java # Configuration
├── ExampleExtensionQBitProducer.java # Entry point
├── customizers/
│ └── ExampleTableCustomizer.java # Table customization
└── actions/
└── ExampleActionCustomizer.java # Action handling
└── customizers/
└── ExampleTableCustomizer.java # Table customization
```
## Key Characteristics
@@ -45,4 +39,4 @@ src/main/java/com/kingsrook/qbits/example/
## License
AGPL-3.0 - See [LICENSE](LICENSE)
See [LICENSE](LICENSE) and [NOTICE](NOTICE).
+17 -31
View File
@@ -1,42 +1,28 @@
# Getting Started
## Prerequisites
Targets Java 21, Maven 3.8 or later, and QQQ 4.0.0. Maven resolves the framework dependencies from Maven Central.
- Java 17+
- Maven 3.8+
- QQQ 0.23.0+
1. Choose **Use this template** on GitHub, then clone the repository you created.
2. In `pom.xml`, set your own `groupId`, `artifactId`, name, description, and version.
3. Use your IDE's package refactoring to rename `com.kingsrook.qbits.example` throughout `src/`. Rename the `Example*` classes and update their imports and references with the IDE's rename refactoring.
4. Update the QBit producer's `GROUP_ID`, `ARTIFACT_ID`, and `VERSION` constants to match your project. Give the metadata names their own stable names before combining the QBit with other examples.
5. Build the generated project:
## Creating Your Extension QBit
1. Click "Use this template" on GitHub
2. Clone your new repository
3. Run the customization script:
```bash
python scripts/customize_template.py
```
4. Enter your QBit name when prompted
## Project Structure
```
src/main/java/com/kingsrook/qbits/yourextension/
├── YourExtensionQBitConfig.java
├── YourExtensionQBitProducer.java
├── customizers/
└── actions/
```bash
mvn clean verify
```
## Using Your QBit
The repository provides example Java sources; customization uses normal package/class refactoring. Keep example coordinates unpublished. After customizing, add tests for your QBit's behavior before setting up its publishing workflow.
## Register with a host application
The following uses the original example names; substitute the names chosen above:
```java
new YourExtensionQBitProducer()
.withConfig(new YourExtensionQBitConfig()
new ExampleExtensionQBitProducer()
.withConfig(new ExampleExtensionQBitConfig()
.withTargetTableName("order"))
.produce(qInstance, "myExtension");
.produce(qInstance, "my-extension");
```
## Next Steps
- Read [Extension Patterns](01-extension-patterns.md)
- Implement your customization logic
- Add tests
Register the target table before producing the extension, then implement its customizer. See [Extension Patterns](01-extension-patterns.md).
+7 -49
View File
@@ -1,58 +1,16 @@
# Extension Patterns
## Table Customizers
Add behavior to existing tables:
[ExampleExtensionQBitProducer](../src/main/java/com/kingsrook/qbits/example/ExampleExtensionQBitProducer.java) registers the QBit identity and calls `applyTableCustomizations` for the configured target. That method is the extension point: look up the table, validate that it exists, and call your customized [ExampleTableCustomizer](../src/main/java/com/kingsrook/qbits/example/customizers/ExampleTableCustomizer.java).
```java
public class AuditTableCustomizer
QTableMetaData table = qInstance.getTable(tableName);
if(table == null)
{
public void customize(QTableMetaData table)
{
table.withField(new QFieldMetaData("createdAt", QFieldType.DATE_TIME));
table.withField(new QFieldMetaData("modifiedAt", QFieldType.DATE_TIME));
table.withCustomizer(
TableCustomizers.PRE_INSERT_RECORD,
new QCodeReference(AuditPreInsert.class));
}
throw new QException("Target table not found: " + tableName);
}
new ExampleTableCustomizer().customize(table);
```
## Action Customizers
When implementing this method, add `throws QException` to its declaration. Run the config's `validate(QInstance, List<String>)` before registering metadata, and report validation errors rather than silently ignoring a missing target. The uncustomized template contains placeholders for this behavior.
Wrap actions with additional behavior:
```java
public class LoggingActionWrapper implements PreInsertRecordInterface
{
@Override
public void preInsertRecord(InsertInput input, QRecord record) throws QException
{
LOG.info("Inserting record", logPair("table", input.getTableName()));
}
}
```
## Config Validation
Validate that required components exist:
```java
@Override
public void validate(QInstance qInstance, List<String> errors)
{
if(qInstance.getTable(targetTableName) == null)
{
errors.add("Target table required: " + targetTableName);
}
}
```
## When to Use Extension QBits
- Adding audit logging to tables
- Implementing authentication/authorization
- Adding encryption to fields
- Custom validation rules
- Performance monitoring
For record hooks, use QQQ's existing table customizer interfaces or `AbstractPreInsertCustomizer`. Its `apply(List<QRecord>)` method returns the records for the next stage; register the customizer through the table's `TableCustomizers.PRE_INSERT_RECORD` role. Check the actual QQQ interface before adding another hook. Add tests proving that the intended table changes and invalid configurations are handled.
+1 -1
View File
@@ -17,7 +17,7 @@
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<qqq.version>0.35.0</qqq.version>
<qqq.version>4.0.0</qqq.version>
</properties>
<dependencies>