Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
502d183fc1 | ||
|
|
72adc8220b | ||
|
|
68f6a6309b | ||
|
|
3cf53b5f1a | ||
|
|
a73162babb | ||
|
|
4e9c047fdb | ||
|
|
6ae6f3dfdd | ||
|
|
2f87fbb31a | ||
|
|
819b3da7d7 | ||
|
|
b9eaa6cf48 | ||
|
|
e6a2ce7f58 | ||
|
|
3ae9356462 |
No files matched your search
@@ -1 +0,0 @@
|
||||
Central Java verification, tests and secret scan are supplied by the organization-scoped workflow.
|
||||
+73
-10
@@ -1,14 +1,77 @@
|
||||
version: 2.1
|
||||
setup: true
|
||||
|
||||
orbs:
|
||||
qqq-orb: kingsrook/qqq-orb@0.8
|
||||
qqq-orb: kingsrook/qqq-orb@0.6.8
|
||||
|
||||
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:
|
||||
setup:
|
||||
|
||||
test_only:
|
||||
jobs:
|
||||
- qqq-orb/generate_pipeline:
|
||||
orb_version: '0.8'
|
||||
filters:
|
||||
branches:
|
||||
ignore: automation/qbit-rc-pending
|
||||
tags:
|
||||
only: /^v\d+\.\d+\.\d+$/
|
||||
- qqq-orb/mvn_test_only:
|
||||
pre-steps:
|
||||
- configure-package-install
|
||||
context: [ qqq-maven-registry-credentials ]
|
||||
filters:
|
||||
branches:
|
||||
ignore: /(develop|release/.*|hotfix/.*|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.*/ ]
|
||||
|
||||
publish_hotfix_release:
|
||||
jobs:
|
||||
- qqq-orb/mvn_publish:
|
||||
pre-steps:
|
||||
- configure-package-install
|
||||
context: [ qqq-maven-registry-credentials ]
|
||||
branch_type: hotfix
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- /hotfix\/.*/
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(wc:*)",
|
||||
"Bash(mvn clean compile:*)",
|
||||
"Bash(mvn compile:*)",
|
||||
"Bash(find:*)",
|
||||
"Bash(mvn test:*)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(git push:*)",
|
||||
"Bash(mvn clean test:*)",
|
||||
"Bash(gh pr create:*)",
|
||||
"Bash(gh pr merge:*)",
|
||||
"Bash(git checkout:*)",
|
||||
"Bash(git pull:*)",
|
||||
"Bash(git merge:*)",
|
||||
"mcp__circleci-mcp-server__get_latest_pipeline_status",
|
||||
"mcp__circleci-mcp-server__get_build_failure_logs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
name: build
|
||||
on:
|
||||
push:
|
||||
jobs:
|
||||
maven:
|
||||
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@v1
|
||||
secrets: inherit
|
||||
@@ -1,23 +0,0 @@
|
||||
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
|
||||
+5
-1
@@ -1,6 +1,5 @@
|
||||
# Maven
|
||||
target/
|
||||
.flattened-pom.xml
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
@@ -22,3 +21,8 @@ Thumbs.db
|
||||
|
||||
# Generated
|
||||
generated/
|
||||
|
||||
.claude/
|
||||
|
||||
# flatten-maven-plugin output
|
||||
.flattened-pom.xml
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
profile: qbit
|
||||
orb_version: '0.8'
|
||||
default_branch: main
|
||||
java_version: '21'
|
||||
browsers: true
|
||||
api_checks: false
|
||||
docs_only: false
|
||||
release_on_main: false
|
||||
release_tag_policy: stable
|
||||
test_branch_policy: non-release
|
||||
publish:
|
||||
snapshot: true
|
||||
release_candidate: true
|
||||
release: true
|
||||
asciidoc: false
|
||||
release_completion: false
|
||||
+4
-33
@@ -7,39 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.2.0] - 2026-11-06
|
||||
|
||||
Published as `com.kingsrook.qbits:qbit-geo-data:0.2.0` on the QRun-IO Gitea Maven
|
||||
registry. Requires Java 21 and QQQ 4.1 (built against `qqq-bom-pom` 4.1.0-RC.21).
|
||||
This is the first release that works on QQQ 4.1; 0.1.0 was built against QQQ 0.35.0.
|
||||
|
||||
### Changed
|
||||
- Build against the published QQQ 4.1 core (`qqq-bom-pom` 4.1.0-RC.21) instead of 0.35.0.
|
||||
- `GeoDataQBitProducer.VERSION` is `0.2.0`.
|
||||
- Import the project's `junit-bom` 5.10.0 ahead of the QQQ BOM so every JUnit artifact
|
||||
converges on 5.10.0 and Surefire discovers the tests.
|
||||
- CI runs on Gitea through the shared `KofTwentyTwo/ci-workflows` Maven workflow
|
||||
(`main` verifies; a `vX.Y.Z` tag publishes to the Gitea registry).
|
||||
|
||||
### Fixed
|
||||
- `Country`, `StateProvince` and `City` ask for their tables (`produceTableMetaData`)
|
||||
and expose the setters `QRecordEntity` needs to discover fields, so the producer
|
||||
creates `<prefix>_country`, `<prefix>_stateProvince` and `<prefix>_city` on core 4.1
|
||||
instead of zero tables. The producer tolerates a table without fields.
|
||||
- `GeoDataSyncStep` resolves the bundled `countryAlpha2` / `stateCode` source codes to
|
||||
the persisted `countryId` / `stateProvinceId` before matching, so states and cities
|
||||
are persisted (0.1.0 reported success after writing 250 countries and zero states or
|
||||
cities because those source columns do not exist in the schema).
|
||||
- Cities match on `stateProvinceId`, `name`, `latitude` and `longitude` at the schema's
|
||||
seven-decimal precision; city names are not unique within a subdivision.
|
||||
- Numeric comparisons use `BigDecimal` value equality, so a second run reports zero
|
||||
inserts, updates or deactivations.
|
||||
- An inactive existing row whose source entry returns is reactivated.
|
||||
- Insert and update results are checked; a row rejected by the backend fails the sync
|
||||
with a `QException` instead of reporting success.
|
||||
|
||||
## [0.1.0] - 2025-12-28
|
||||
|
||||
### Added
|
||||
- Multi-module structure (qbit-geo-data-core + tools)
|
||||
- Country, StateProvince, City entities with sync process
|
||||
@@ -52,6 +19,10 @@ This is the first release that works on QQQ 4.1; 0.1.0 was built against QQQ 0.3
|
||||
|
||||
### Changed
|
||||
- Upgraded to QQQ 0.35.0 with Java 21 support
|
||||
- Re-pinned to qbit-build-parent 2.0.0 (QQQ 4.0.0); opt-in `qqq-snapshot` Maven profile builds against QQQ 4.1.0-SNAPSHOT
|
||||
|
||||
### Fixed
|
||||
- Published module poms name their parent by its real version instead of `${revision}` (flatten-maven-plugin)
|
||||
|
||||
## [0.1.0-alpha] - 2024-12-28
|
||||
|
||||
|
||||
@@ -7,33 +7,16 @@ Geographic reference data QBit providing countries, states/provinces, and cities
|
||||
|
||||
## Quick Start
|
||||
|
||||
The current release is **0.2.0**. It requires Java 21 and QQQ 4.1 (built against
|
||||
`qqq-bom-pom` 4.1.0-RC.21). See [CHANGELOG.md](CHANGELOG.md).
|
||||
|
||||
Add the dependency to your `pom.xml`:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qbits</groupId>
|
||||
<artifactId>qbit-geo-data</artifactId>
|
||||
<version>0.2.0</version>
|
||||
<artifactId>qbit-geo-data-core</artifactId>
|
||||
<version>${qbit-geo-data.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
Releases are published to the QRun-IO Gitea Maven registry, which requires
|
||||
authentication for reads as well as writes. Add the repository and a matching
|
||||
`<server>` entry (with your own Gitea username and token) in your private Maven
|
||||
`settings.xml`:
|
||||
|
||||
```xml
|
||||
<repository>
|
||||
<id>qrun-io</id>
|
||||
<url>https://git.kof22.com/api/packages/QRun-IO/maven</url>
|
||||
<releases><enabled>true</enabled></releases>
|
||||
<snapshots><enabled>false</enabled></snapshots>
|
||||
</repository>
|
||||
```
|
||||
|
||||
## What You Get
|
||||
|
||||
This QBit provides three entities with pre-populated reference data:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.kingsrook</groupId>
|
||||
<artifactId>qbit-build-parent</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<version>2.0.0</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.kingsrook.qbits</groupId>
|
||||
@@ -50,8 +50,7 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<revision>0.2.0</revision>
|
||||
<qqq.version>4.1.0-RC.21</qqq.version>
|
||||
<revision>0.2.0-SNAPSHOT</revision>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@@ -59,10 +58,11 @@
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- One JUnit line. The QQQ 4.1 BOM inherits junit-jupiter-engine/params at 6.0.2 from qqq-parent-project, while this pom's JUnit BOM
|
||||
manages junit-jupiter, the API and the platform at 5.10.0; the mixed engine fails discovery ("TestEngine with ID 'junit-jupiter'
|
||||
failed to discover tests") and no test runs. Import this pom's own JUnit version first (first import wins) so every JUnit
|
||||
artifact converges on 5.10.0. -->
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20231013</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
@@ -70,19 +70,6 @@
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<!-- Published QQQ 4.1 core (minimum QQQ 4.1), imported ahead of the parent's BOM. -->
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-bom-pom</artifactId>
|
||||
<version>${qqq.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20231013</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
@@ -92,9 +79,22 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<plugins>
|
||||
<!-- Resolve the CI-friendly ${revision} in the installed and deployed POMs so -->
|
||||
<!-- consumers of the published artifacts can resolve the parent chain. -->
|
||||
<!-- Replace ${revision} with its value in the poms that get installed and published. -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>flatten-maven-plugin</artifactId>
|
||||
@@ -112,7 +112,7 @@
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>flatten-clean</id>
|
||||
<id>flatten.clean</id>
|
||||
<phase>clean</phase>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
@@ -121,19 +121,40 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<!-- Opt-in check against the next qqq line: mvn -B verify -Pqqq-snapshot -->
|
||||
<!-- (override the version with -Dqqq.snapshot.version=...). Default builds -->
|
||||
<!-- take the qqq version only from qbit-build-parent. -->
|
||||
<profile>
|
||||
<id>qqq-snapshot</id>
|
||||
<properties>
|
||||
<qqq.snapshot.version>4.1.0-SNAPSHOT</qqq.snapshot.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-bom-pom</artifactId>
|
||||
<version>${qqq.snapshot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype-snapshots</id>
|
||||
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Java 17+
|
||||
- Java 21+
|
||||
- Maven 3.8+
|
||||
- QQQ 0.23.0+
|
||||
- QQQ 4.0.0+
|
||||
|
||||
## Creating Your Data QBit
|
||||
|
||||
|
||||
+1
-5
@@ -28,7 +28,7 @@ public class GeoDataQBitProducer implements QBitProducer
|
||||
{
|
||||
public static final String GROUP_ID = "com.kingsrook.qbits";
|
||||
public static final String ARTIFACT_ID = "qbit-geo-data";
|
||||
public static final String VERSION = "0.2.0";
|
||||
public static final String VERSION = "0.1.0";
|
||||
|
||||
private GeoDataQBitConfig config;
|
||||
|
||||
@@ -126,10 +126,6 @@ public class GeoDataQBitProducer implements QBitProducer
|
||||
*******************************************************************************/
|
||||
private void updateForeignKeyReferences(QTableMetaData table)
|
||||
{
|
||||
if(table.getFields() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
table.getFields().values().forEach(field ->
|
||||
{
|
||||
if(field.getPossibleValueSourceName() != null)
|
||||
|
||||
@@ -14,7 +14,7 @@ import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.producers.annotations.QMetaDataProducingEntity;
|
||||
|
||||
|
||||
@QMetaDataProducingEntity(produceTableMetaData = true, producePossibleValueSource = true)
|
||||
@QMetaDataProducingEntity(producePossibleValueSource = true)
|
||||
public class City extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "city";
|
||||
@@ -204,103 +204,4 @@ public class City extends QRecordEntity
|
||||
{
|
||||
return modifyDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for stateProvinceId (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setStateProvinceId(Integer stateProvinceId)
|
||||
{
|
||||
this.stateProvinceId = stateProvinceId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for asciiName (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setAsciiName(String asciiName)
|
||||
{
|
||||
this.asciiName = asciiName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for population (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setPopulation(Integer population)
|
||||
{
|
||||
this.population = population;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for latitude (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setLatitude(BigDecimal latitude)
|
||||
{
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for longitude (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setLongitude(BigDecimal longitude)
|
||||
{
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timezone (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setTimezone(String timezone)
|
||||
{
|
||||
this.timezone = timezone;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for isActive (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setIsActive(Boolean isActive)
|
||||
{
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.producers.annotations.QMetaDataProducingEntity;
|
||||
|
||||
|
||||
@QMetaDataProducingEntity(produceTableMetaData = true, producePossibleValueSource = true)
|
||||
@QMetaDataProducingEntity(producePossibleValueSource = true)
|
||||
public class Country extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "country";
|
||||
@@ -171,85 +171,4 @@ public class Country extends QRecordEntity
|
||||
{
|
||||
return modifyDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for alpha2Code (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setAlpha2Code(String alpha2Code)
|
||||
{
|
||||
this.alpha2Code = alpha2Code;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for alpha3Code (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setAlpha3Code(String alpha3Code)
|
||||
{
|
||||
this.alpha3Code = alpha3Code;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for numericCode (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setNumericCode(Integer numericCode)
|
||||
{
|
||||
this.numericCode = numericCode;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for officialName (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setOfficialName(String officialName)
|
||||
{
|
||||
this.officialName = officialName;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for isActive (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setIsActive(Boolean isActive)
|
||||
{
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
}
|
||||
+1
-73
@@ -13,7 +13,7 @@ import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.producers.annotations.QMetaDataProducingEntity;
|
||||
|
||||
|
||||
@QMetaDataProducingEntity(produceTableMetaData = true, producePossibleValueSource = true)
|
||||
@QMetaDataProducingEntity(producePossibleValueSource = true)
|
||||
public class StateProvince extends QRecordEntity
|
||||
{
|
||||
public static final String TABLE_NAME = "stateProvince";
|
||||
@@ -155,76 +155,4 @@ public class StateProvince extends QRecordEntity
|
||||
{
|
||||
return modifyDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for id (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setId(Integer id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for countryId (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setCountryId(Integer countryId)
|
||||
{
|
||||
this.countryId = countryId;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for code (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setCode(String code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for name (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for subdivisionType (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setSubdivisionType(String subdivisionType)
|
||||
{
|
||||
this.subdivisionType = subdivisionType;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for isActive (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setIsActive(Boolean isActive)
|
||||
{
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for createDate (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setCreateDate(Instant createDate)
|
||||
{
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for modifyDate (QRecordEntity discovers a field only from a getter and setter pair).
|
||||
*******************************************************************************/
|
||||
public void setModifyDate(Instant modifyDate)
|
||||
{
|
||||
this.modifyDate = modifyDate;
|
||||
}
|
||||
}
|
||||
+13
-60
@@ -4,15 +4,13 @@
|
||||
** Syncs all three entity types (Country, StateProvince, City) from bundled
|
||||
** JSON files into database tables. Performs natural key upsert:
|
||||
** - Country: by alpha2Code
|
||||
** - StateProvince: by countryId + code (countryAlpha2 resolved to the persisted country)
|
||||
** - City: by stateProvinceId + name + latitude + longitude (codes resolved to the persisted state)
|
||||
** - StateProvince: by countryAlpha2 + code
|
||||
** - City: by countryAlpha2 + stateCode + name
|
||||
*******************************************************************************/
|
||||
package com.kingsrook.qbits.geodata.sync;
|
||||
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -64,39 +62,9 @@ public class GeoDataSyncStep implements BackendStep
|
||||
// Sync in order: countries first (no dependencies), then states, then //
|
||||
// cities (which may reference states) //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
int countriesInserted = syncTable(prefix + "_country", loadJsonData("/data/countries.json"), List.of("alpha2Code"));
|
||||
Map<String, QRecord> countries = queryExisting(prefix + "_country", List.of("alpha2Code"));
|
||||
List<QRecord> states = loadJsonData("/data/states.json");
|
||||
for(QRecord state : states)
|
||||
{
|
||||
QRecord country = countries.get(state.getValueString("countryAlpha2"));
|
||||
if(country == null)
|
||||
{
|
||||
throw new QException("Geo Data state references an unavailable country");
|
||||
}
|
||||
state.setValue("countryId", country.getValue("id"));
|
||||
state.getValues().remove("countryAlpha2");
|
||||
}
|
||||
int statesInserted = syncTable(prefix + "_stateProvince", states, List.of("countryId", "code"));
|
||||
Map<String, QRecord> persistedStates = queryExisting(prefix + "_stateProvince", List.of("countryId", "code"));
|
||||
List<QRecord> cities = loadJsonData("/data/cities.json");
|
||||
for(QRecord city : cities)
|
||||
{
|
||||
QRecord country = countries.get(city.getValueString("countryAlpha2"));
|
||||
QRecord state = country == null ? null : persistedStates.get(country.getValue("id") + "|" + city.getValueString("stateCode"));
|
||||
if(state == null)
|
||||
{
|
||||
throw new QException("Geo Data city references an unavailable state");
|
||||
}
|
||||
city.setValue("stateProvinceId", state.getValue("id"));
|
||||
for(String coordinate : List.of("latitude", "longitude"))
|
||||
{
|
||||
city.setValue(coordinate, new BigDecimal(city.getValue(coordinate).toString()).setScale(7, RoundingMode.HALF_UP));
|
||||
}
|
||||
city.getValues().remove("countryAlpha2");
|
||||
city.getValues().remove("stateCode");
|
||||
}
|
||||
int citiesInserted = syncTable(prefix + "_city", cities, List.of("stateProvinceId", "name", "latitude", "longitude"));
|
||||
int countriesInserted = syncTable(prefix + "_country", "/data/countries.json", List.of("alpha2Code"));
|
||||
int statesInserted = syncTable(prefix + "_stateProvince", "/data/states.json", List.of("countryAlpha2", "code"));
|
||||
int citiesInserted = syncTable(prefix + "_city", "/data/cities.json", List.of("countryAlpha2", "stateCode", "name"));
|
||||
|
||||
LOG.info("Geo data sync complete",
|
||||
logPair("prefix", prefix),
|
||||
@@ -115,15 +83,17 @@ public class GeoDataSyncStep implements BackendStep
|
||||
** Sync a single table from JSON resource.
|
||||
** Returns count of records processed.
|
||||
*******************************************************************************/
|
||||
private int syncTable(String tableName, List<QRecord> sourceRecords, List<String> naturalKeyFields) throws QException
|
||||
private int syncTable(String tableName, String resourcePath, List<String> naturalKeyFields) throws QException
|
||||
{
|
||||
LOG.info("Syncing table",
|
||||
logPair("table", tableName),
|
||||
logPair("resource", resourcePath),
|
||||
logPair("naturalKey", naturalKeyFields));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// 1. Load source data from classpath JSON //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
List<QRecord> sourceRecords = loadJsonData(resourcePath);
|
||||
LOG.info("Loaded source data", logPair("count", sourceRecords.size()));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
@@ -149,7 +119,7 @@ public class GeoDataSyncStep implements BackendStep
|
||||
source.setValue("isActive", true);
|
||||
toInsert.add(source);
|
||||
}
|
||||
else if(hasChanges(source, existing) || !Boolean.TRUE.equals(existing.getValueBoolean("isActive")))
|
||||
else if(hasChanges(source, existing))
|
||||
{
|
||||
source.setValue("id", existing.getValue("id"));
|
||||
source.setValue("isActive", true);
|
||||
@@ -212,7 +182,7 @@ public class GeoDataSyncStep implements BackendStep
|
||||
key.append("|");
|
||||
}
|
||||
Object value = record.getValue(field);
|
||||
key.append(value instanceof Number ? new BigDecimal(value.toString()).stripTrailingZeros().toPlainString() : value != null ? value.toString() : "");
|
||||
key.append(value != null ? value.toString() : "");
|
||||
}
|
||||
return key.toString();
|
||||
}
|
||||
@@ -295,14 +265,7 @@ public class GeoDataSyncStep implements BackendStep
|
||||
}
|
||||
Object sourceValue = source.getValue(fieldName);
|
||||
Object existingValue = existing.getValue(fieldName);
|
||||
if(sourceValue instanceof Number && existingValue instanceof Number)
|
||||
{
|
||||
if(new BigDecimal(sourceValue.toString()).compareTo(new BigDecimal(existingValue.toString())) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if(!Objects.equals(sourceValue, existingValue))
|
||||
if(!Objects.equals(sourceValue, existingValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -320,7 +283,7 @@ public class GeoDataSyncStep implements BackendStep
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(tableName);
|
||||
insertInput.setRecords(records);
|
||||
requireSuccess(tableName, records.size(), new InsertAction().execute(insertInput).getRecords());
|
||||
new InsertAction().execute(insertInput);
|
||||
}
|
||||
|
||||
|
||||
@@ -333,16 +296,6 @@ public class GeoDataSyncStep implements BackendStep
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(tableName);
|
||||
updateInput.setRecords(records);
|
||||
requireSuccess(tableName, records.size(), new UpdateAction().execute(updateInput).getRecords());
|
||||
}
|
||||
/*******************************************************************************
|
||||
** Do not report a successful sync when a native action rejected rows.
|
||||
*******************************************************************************/
|
||||
private void requireSuccess(String tableName, int expected, List<QRecord> records) throws QException
|
||||
{
|
||||
if(records == null || records.size() != expected || records.stream().anyMatch(record -> !record.getErrors().isEmpty()))
|
||||
{
|
||||
throw new QException("Geo Data persistence failed for table " + tableName);
|
||||
}
|
||||
new UpdateAction().execute(updateInput);
|
||||
}
|
||||
}
|
||||
+1
-33
@@ -78,38 +78,6 @@ class GeoDataQBitProducerTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** The enabled entities produce their tables, on the configured backend, with the
|
||||
** prefix; the possible value sources point at tables that exist. (On core 4.1 an
|
||||
** entity annotation only produces a table when it asks for it.)
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testProduce_createsTablesAndPossibleValueSourcesPointAtThem() throws QException
|
||||
{
|
||||
QInstance qInstance = new QInstance();
|
||||
qInstance.addBackend(new QBackendMetaData().withName("rdbms"));
|
||||
|
||||
new GeoDataQBitProducer()
|
||||
.withConfig(new GeoDataQBitConfig().withBackendName("rdbms").withTableNamePrefix("geo")
|
||||
.withEnableCountries(true).withEnableStateProvinces(true).withEnableCities(true))
|
||||
.produce(qInstance, "geo");
|
||||
|
||||
assertThat(qInstance.getTables().keySet()).contains("geo_country", "geo_stateProvince", "geo_city");
|
||||
assertThat(qInstance.getTable("geo_city").getBackendName()).isEqualTo("rdbms");
|
||||
assertThat(qInstance.getTable("geo_country").getPrimaryKeyField()).isEqualTo("id");
|
||||
assertThat(qInstance.getTable("geo_country").getFields().keySet()).contains("alpha2Code", "name", "isActive");
|
||||
assertThat(qInstance.getTable("geo_city").getFields().keySet()).contains("name");
|
||||
for(var pvs : qInstance.getPossibleValueSources().values())
|
||||
{
|
||||
if(pvs.getTableName() != null)
|
||||
{
|
||||
assertThat(qInstance.getTable(pvs.getTableName())).as(pvs.getName()).isNotNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test that producer stores config in QBit metadata.
|
||||
*******************************************************************************/
|
||||
@@ -165,6 +133,6 @@ class GeoDataQBitProducerTest
|
||||
{
|
||||
assertThat(GeoDataQBitProducer.GROUP_ID).isEqualTo("com.kingsrook.qbits");
|
||||
assertThat(GeoDataQBitProducer.ARTIFACT_ID).isEqualTo("qbit-geo-data");
|
||||
assertThat(GeoDataQBitProducer.VERSION).isEqualTo("0.2.0");
|
||||
assertThat(GeoDataQBitProducer.VERSION).isEqualTo("0.1.0");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user