From ae111b4aa1dbde556d2de10f8fb53b37450ac89b Mon Sep 17 00:00:00 2001 From: James Maes Date: Thu, 10 Sep 2026 07:09:38 -0500 Subject: [PATCH] fix(template): complete QQQ 4.0 data migration --- .circleci/config.yml | 2 +- README.md | 38 +++++-------- docs/00-getting-started.md | 53 +++++++------------ pom.xml | 2 +- ...xampleDataSyncProcessMetaDataProducer.java | 13 +++-- .../example/sync/ExampleDataSyncStep.java | 14 ++++- 6 files changed, 51 insertions(+), 71 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8828026..f770741 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,4 +6,4 @@ orbs: workflows: build: jobs: - - qqq-orb/mvn_test + - qqq-orb/mvn_test_only diff --git a/README.md b/README.md index f46dde7..ee95bda 100644 --- a/README.md +++ b/README.md @@ -12,24 +12,20 @@ Data QBits provide reusable reference data sets that applications consume. They ## Quick Start -1. Click "Use this template" to create your QBit -2. Run `scripts/customize_template.py` to rename the example -3. Define your entity classes -4. Add reference data JSON files -5. Implement sync process -6. Generate Liquibase changelog +Requires **Java 21**, **Maven 3.8+**, and QQQ **4.0.0**. Create your repository, rename its Maven coordinates and Java package/classes, then run `mvn clean verify`. See [Getting Started](docs/00-getting-started.md). ## Structure ``` src/main/java/com/kingsrook/qbits/example/ -├── ExampleDataQBitConfig.java # Extends AbstractDataQBitConfig -├── ExampleDataQBitProducer.java # Extends AbstractDataQBitProducer +├── ExampleDataQBitConfig.java # Configuration +├── ExampleDataQBitProducer.java # QBit registration ├── model/ │ ├── ExampleEntity.java │ └── ExampleChildEntity.java ├── sync/ -│ └── ExampleDataSyncProcess.java +│ ├── ExampleDataSyncStep.java +│ └── ExampleDataSyncProcessMetaDataProducer.java └── liquibase/ └── ExampleLiquibaseGenerator.java ``` @@ -37,28 +33,20 @@ src/main/java/com/kingsrook/qbits/example/ ## Key Characteristics - **Table prefixing** - `shipping_country`, `billing_country` -- **Multi-instance** - Same QBit deployed multiple times +- **Host configuration** - Backend and optional table prefix - **Sync process** - Upsert by natural key - **Liquibase generator** - Template-based, prefix-aware -## Multi-Instance Usage +## Host integration ```java -// Shipping addresses -new GeoDataQBitProducer() - .withConfig(new GeoDataQBitConfig() - .withBackendName("rdbms") - .withTableNamePrefix("shipping")) - .produce(qInstance, "shipping-geo"); - -// Billing addresses -new GeoDataQBitProducer() - .withConfig(new GeoDataQBitConfig() - .withBackendName("rdbms") - .withTableNamePrefix("billing")) - .produce(qInstance, "billing-geo"); +new ExampleDataQBitProducer() + .withConfig(new ExampleDataQBitConfig().withBackendName("rdbms")) + .produce(qInstance, "example-data"); ``` +The host must already define the backend. The example prefixes table names; adapting it for multiple instances also requires unique process and possible-value-source names and consistent references. See [Getting Started](docs/00-getting-started.md). + ## Documentation - [Getting Started](docs/00-getting-started.md) @@ -68,4 +56,4 @@ new GeoDataQBitProducer() ## License -AGPL-3.0 - See [LICENSE](LICENSE) +See [LICENSE](LICENSE) and [NOTICE](NOTICE). diff --git a/docs/00-getting-started.md b/docs/00-getting-started.md index 047d995..ca2217d 100644 --- a/docs/00-getting-started.md +++ b/docs/00-getting-started.md @@ -1,47 +1,30 @@ # Getting Started -## Prerequisites +Requires Java 21, Maven 3.8 or later, and a host application using QQQ 4.0.0. Dependencies resolve 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 Data 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/yourdata/ -├── YourDataQBitConfig.java -├── YourDataQBitProducer.java -├── model/ -│ └── YourEntity.java -├── sync/ -│ └── YourDataSyncProcess.java -└── liquibase/ - └── YourLiquibaseGenerator.java +```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 YourDataQBitProducer() - .withConfig(new YourDataQBitConfig() - .withBackendName("rdbms") - .withTableNamePrefix("myprefix")) +new ExampleDataQBitProducer() + .withConfig(new ExampleDataQBitConfig() + .withBackendName("rdbms")) .produce(qInstance, "my-data"); ``` -## Next Steps +Register the configured backend in the host first. If you add table prefixes or multiple QBit instances, update related table references, possible-value-source names, and process/widget names consistently. A table prefix alone does not namespace all metadata. -- Read [Data QBit Architecture](01-data-qbit-architecture.md) -- Implement your sync process -- Generate Liquibase changelog +[Data QBit Architecture](01-data-qbit-architecture.md) explains the example data model. diff --git a/pom.xml b/pom.xml index 41d62c3..22ada99 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ 21 21 UTF-8 - 0.35.0 + 4.0.0 diff --git a/src/main/java/com/kingsrook/qbits/example/sync/ExampleDataSyncProcessMetaDataProducer.java b/src/main/java/com/kingsrook/qbits/example/sync/ExampleDataSyncProcessMetaDataProducer.java index 1a3184b..0d7be72 100644 --- a/src/main/java/com/kingsrook/qbits/example/sync/ExampleDataSyncProcessMetaDataProducer.java +++ b/src/main/java/com/kingsrook/qbits/example/sync/ExampleDataSyncProcessMetaDataProducer.java @@ -6,14 +6,14 @@ package com.kingsrook.qbits.example.sync; import java.util.List; import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface; 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.metadata.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; -import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface; -import com.kingsrook.qbits.example.model.ExampleEntity; public class ExampleDataSyncProcessMetaDataProducer implements MetaDataProducerInterface @@ -31,15 +31,14 @@ public class ExampleDataSyncProcessMetaDataProducer implements MetaDataProducerI return new QProcessMetaData() .withName(NAME) .withLabel("Example Data Sync") - .withInputFields(List.of( - new QFieldMetaData("tableName", QFieldType.STRING), - new QFieldMetaData("naturalKeyField", QFieldType.STRING), - new QFieldMetaData("dataResourcePath", QFieldType.STRING) - )) .withStepList(List.of( new QBackendStepMetaData() .withName("sync") .withCode(new QCodeReference(ExampleDataSyncStep.class)) + .withInputData(new QFunctionInputMetaData() + .withField(new QFieldMetaData("tableName", QFieldType.STRING)) + .withField(new QFieldMetaData("naturalKeyField", QFieldType.STRING)) + .withField(new QFieldMetaData("dataResourcePath", QFieldType.STRING))) )); } } diff --git a/src/main/java/com/kingsrook/qbits/example/sync/ExampleDataSyncStep.java b/src/main/java/com/kingsrook/qbits/example/sync/ExampleDataSyncStep.java index e3a8217..477b91e 100644 --- a/src/main/java/com/kingsrook/qbits/example/sync/ExampleDataSyncStep.java +++ b/src/main/java/com/kingsrook/qbits/example/sync/ExampleDataSyncStep.java @@ -17,8 +17,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import com.kingsrook.qqq.backend.core.actions.processes.RunBackendStepInput; -import com.kingsrook.qqq.backend.core.actions.processes.RunBackendStepOutput; import com.kingsrook.qqq.backend.core.actions.tables.InsertAction; import com.kingsrook.qqq.backend.core.actions.tables.QueryAction; import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction; @@ -31,6 +29,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; +import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractTransformStep; import org.json.JSONArray; @@ -236,4 +235,15 @@ public class ExampleDataSyncStep extends AbstractTransformStep updateInput.setRecords(records); new UpdateAction().execute(updateInput); } + + + + /******************************************************************************* + ** Return the process summary. + *******************************************************************************/ + @Override + public ArrayList getProcessSummary(RunBackendStepOutput runBackendStepOutput, boolean isForResultScreen) + { + return (new ArrayList<>()); + } }