diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 00000000..0ef02745
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,77 @@
+version: 2.1
+
+executors:
+ java17:
+ docker:
+ - image: 'cimg/openjdk:17.0'
+ resource_class: small
+
+orbs:
+ slack: circleci/slack@4.10.1
+
+commands:
+ run_maven:
+ parameters:
+ maven_subcommand:
+ default: test
+ type: string
+ steps:
+ - checkout
+ - restore_cache:
+ keys:
+ - v1-dependencies-{{ checksum "pom.xml" }}
+ - run:
+ name: Run Maven
+ command: |
+ mvn -s .circleci/mvn-settings.xml << parameters.maven_subcommand >>
+ - run:
+ name: Save test results
+ command: |
+ mkdir -p ~/test-results/junit/
+ find . -type f -regex ".*/target/surefire-reports/.*xml" -exec cp {} ~/test-results/junit/ \;
+ when: always
+ - store_test_results:
+ path: ~/test-results
+ - save_cache:
+ paths:
+ - ~/.m2
+ key: v1-dependencies-{{ checksum "pom.xml" }}
+
+jobs:
+ mvn_test:
+ executor: java17
+ steps:
+ - run_maven:
+ maven_subcommand: test
+ - slack/notify:
+ event: fail
+
+ mvn_deploy:
+ executor: java17
+ steps:
+ - run_maven:
+ maven_subcommand: deploy
+ - slack/notify:
+ event: always
+
+workflows:
+ test_only:
+ jobs:
+ - mvn_test:
+ context: [ qqq-maven-registry-credentials, kingsrook-slack ]
+ filters:
+ branches:
+ ignore: /dev/
+ tags:
+ ignore: /(version|snapshot)-.*/
+
+ deploy:
+ jobs:
+ - mvn_deploy:
+ context: [ qqq-maven-registry-credentials, kingsrook-slack ]
+ filters:
+ branches:
+ only: /dev/
+ tags:
+ only: /(version|snapshot)-.*/
+
diff --git a/.circleci/mvn-settings.xml b/.circleci/mvn-settings.xml
new file mode 100644
index 00000000..b2a345f0
--- /dev/null
+++ b/.circleci/mvn-settings.xml
@@ -0,0 +1,9 @@
+
+
+
+ github-qqq-maven-registry
+ ${env.QQQ_MAVEN_REGISTRY_USERNAME}
+ ${env.QQQ_MAVEN_REGISTRY_PASSWORD}
+
+
+
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
deleted file mode 100644
index 8c794698..00000000
--- a/.github/workflows/maven.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
-# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
-
-name: Java CI with Maven
-
-on:
- push:
- branches: [ main ]
- pull_request:
- branches: [ main ]
- workflow_dispatch:
- branches: [ main ]
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v2
- - name: Set up JDK 17
- uses: actions/setup-java@v2
- with:
- java-version: '17'
- distribution: 'adopt'
- cache: maven
- - name: maven-settings-xml-action
- uses: whelk-io/maven-settings-xml-action@v20
- with:
- repositories: '[{ "id": "github-qqq-maven-registry", "url": "https://maven.pkg.github.com/Kingsrook/qqq-maven-registry", "snapshots": { "enabled": "true" }}]'
- servers: '[{ "id": "github-qqq-maven-registry", "username": "${{ secrets.QQQ_MAVEN_REGISTRY_USERNAME }}", "password": "${{ secrets.QQQ_MAVEN_REGISTRY_PASSWORD }}" }]'
- - name: Build with Maven
- run: mvn -B package --file pom.xml
- - name: Publish to GitHub Packages Apache Maven
- run: mvn deploy
- env:
- GITHUB_TOKEN: ${{ github.token }}
diff --git a/README.md b/README.md
index f64a1d0c..36d0831b 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,7 @@ This is a backend-module for the qqq framework - specifically, one that works wi
QQQ - Low-code Application Framework for Engineers. \
Copyright (C) 2022. Kingsrook, LLC \
651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States \
-contact@kingsrook.com
-https://github.com/Kingsrook/intellij-commentator-plugin
+contact@kingsrook.com | https://github.com/Kingsrook/
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
diff --git a/checkstyle.xml b/checkstyle.xml
index dbaa3479..76f872ed 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -46,6 +46,7 @@
-->
+
@@ -171,7 +172,7 @@
-
+
-
+
4.0.0
com.kingsrook.qqq
qqq-backend-module-rdbms
- 0.0-SNAPSHOT
+ 0.0.0
+
+
+ scm:git:git@github.com:Kingsrook/qqq-backend-module-rdbms.git
+ scm:git:git@github.com:Kingsrook/qqq-backend-module-rdbms.git
+ HEAD
+
@@ -47,7 +51,7 @@
com.kingsrook.qqq
qqq-backend-core
- 0.0-SNAPSHOT
+ 0.0.0
@@ -59,7 +63,7 @@
com.h2database
h2
- 1.4.197
+ 2.1.210
test
@@ -72,12 +76,12 @@
org.apache.logging.log4j
log4j-api
- 2.15.0
+ 2.17.1
org.apache.logging.log4j
log4j-core
- 2.15.0
+ 2.17.1
org.junit.jupiter
@@ -85,6 +89,12 @@
5.8.1
test
+
+ org.assertj
+ assertj-core
+ 3.23.1
+ test
+
@@ -138,12 +148,29 @@
+
+ com.amashchenko.maven.plugin
+ gitflow-maven-plugin
+ 1.18.0
+
+
+ main
+ dev
+ version-
+
+ true
+ install
+ true
+ 1
+
+
+
- github
+ github-qqq-maven-registry
GitHub QQQ Maven Registry
https://maven.pkg.github.com/Kingsrook/qqq-maven-registry
diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/RDBMSBackendModule.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/RDBMSBackendModule.java
index 9146e1e4..752b53c9 100644
--- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/RDBMSBackendModule.java
+++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/RDBMSBackendModule.java
@@ -22,6 +22,8 @@
package com.kingsrook.qqq.backend.module.rdbms;
+import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
+import com.kingsrook.qqq.backend.core.model.metadata.QTableBackendDetails;
import com.kingsrook.qqq.backend.core.modules.interfaces.DeleteInterface;
import com.kingsrook.qqq.backend.core.modules.interfaces.InsertInterface;
import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface;
@@ -31,21 +33,54 @@ import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSDeleteAction;
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSInsertAction;
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSQueryAction;
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSUpdateAction;
+import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
+import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
/*******************************************************************************
- ** RDBMSBackendModule
- *
+ ** QQQ Backend module for working with Relational Databases (RDBMS's).
*******************************************************************************/
public class RDBMSBackendModule implements QBackendModuleInterface
{
+ /*******************************************************************************
+ ** Method where a backend module must be able to provide its type (name).
+ *******************************************************************************/
+ public String getBackendType()
+ {
+ return ("rdbms");
+ }
+
+
+
+ /*******************************************************************************
+ ** Method to identify the class used for backend meta data for this module.
+ *******************************************************************************/
+ @Override
+ public Class extends QBackendMetaData> getBackendMetaDataClass()
+ {
+ return (RDBMSBackendMetaData.class);
+ }
+
+
+
+ /*******************************************************************************
+ ** Method to identify the class used for table-backend details for this module.
+ *******************************************************************************/
+ @Override
+ public Class extends QTableBackendDetails> getTableBackendDetailsClass()
+ {
+ return (RDBMSTableBackendDetails.class);
+ }
+
+
+
/*******************************************************************************
**
*******************************************************************************/
@Override
public QueryInterface getQueryInterface()
{
- return new RDBMSQueryAction();
+ return (new RDBMSQueryAction());
}
@@ -60,6 +95,7 @@ public class RDBMSBackendModule implements QBackendModuleInterface
}
+
/*******************************************************************************
**
*******************************************************************************/
@@ -79,4 +115,5 @@ public class RDBMSBackendModule implements QBackendModuleInterface
{
return (new RDBMSDeleteAction());
}
+
}
diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java
index 94abade1..0f1c7110 100644
--- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java
+++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java
@@ -22,17 +22,49 @@
package com.kingsrook.qqq.backend.module.rdbms.actions;
+import java.io.Serializable;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.time.OffsetDateTime;
+import com.kingsrook.qqq.backend.core.model.actions.AbstractQTableRequest;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
+import com.kingsrook.qqq.backend.core.model.metadata.QFieldType;
+import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
+import com.kingsrook.qqq.backend.core.utils.StringUtils;
+import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
+import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
+import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
/*******************************************************************************
- **
+ ** Base class for all core actions in the RDBMS module.
*******************************************************************************/
public abstract class AbstractRDBMSAction
{
/*******************************************************************************
+ ** Get the table name to use in the RDBMS from a QTableMetaData.
**
+ ** That is, table.backendDetails.tableName if set -- else, table.name
+ *******************************************************************************/
+ protected String getTableName(QTableMetaData table)
+ {
+ if(table.getBackendDetails() instanceof RDBMSTableBackendDetails details)
+ {
+ if(StringUtils.hasContent(details.getTableName()))
+ {
+ return (details.getTableName());
+ }
+ }
+ return (table.getName());
+ }
+
+
+
+ /*******************************************************************************
+ ** Get the column name to use for a field in the RDBMS, from the fieldMetaData.
+ **
+ ** That is, field.backendName if set -- else, field.name
*******************************************************************************/
protected String getColumnName(QFieldMetaData field)
{
@@ -43,4 +75,42 @@ public abstract class AbstractRDBMSAction
return (field.getName());
}
+
+
+ /*******************************************************************************
+ ** Get a database connection, per the backend in the request.
+ *******************************************************************************/
+ protected Connection getConnection(AbstractQTableRequest qTableRequest) throws SQLException
+ {
+ ConnectionManager connectionManager = new ConnectionManager();
+ return connectionManager.getConnection((RDBMSBackendMetaData) qTableRequest.getBackend());
+ }
+
+
+
+ /*******************************************************************************
+ ** Handle obvious problems with values - like empty string for integer should be null.
+ **
+ *******************************************************************************/
+ protected Serializable scrubValue(QFieldMetaData field, Serializable value)
+ {
+ if("".equals(value))
+ {
+ QFieldType type = field.getType();
+ if(type.equals(QFieldType.INTEGER) || type.equals(QFieldType.DECIMAL) || type.equals(QFieldType.DATE) || type.equals(QFieldType.DATE_TIME))
+ {
+ value = null;
+ }
+ }
+
+ //////////////////////////////////////////////////////
+ // todo - let this come from something in the field //
+ //////////////////////////////////////////////////////
+ if(value == null && (field.getName().equals("createDate") || field.getName().equals("modifyDate")))
+ {
+ value = OffsetDateTime.now();
+ }
+
+ return (value);
+ }
}
diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java
index 5c4254ec..3495a621 100644
--- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java
+++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java
@@ -31,11 +31,8 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteRequest;
import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteResult;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
-import com.kingsrook.qqq.backend.core.model.data.QRecordWithStatus;
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
import com.kingsrook.qqq.backend.core.modules.interfaces.DeleteInterface;
-import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendMetaData;
-import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
@@ -55,7 +52,7 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
DeleteResult rs = new DeleteResult();
QTableMetaData table = deleteRequest.getTable();
- String tableName = table.getName();
+ String tableName = getTableName(table);
String primaryKeyName = getColumnName(table.getField(table.getPrimaryKeyField()));
String sql = "DELETE FROM "
+ tableName
@@ -68,18 +65,16 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
// todo sql customization - can edit sql and/or param list
- ConnectionManager connectionManager = new ConnectionManager();
- Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(deleteRequest.getBackend()));
-
+ Connection connection = getConnection(deleteRequest);
QueryManager.executeUpdateForRowCount(connection, sql, params);
- List recordsWithStatus = new ArrayList<>();
- rs.setRecords(recordsWithStatus);
+ List outputRecords = new ArrayList<>();
+ rs.setRecords(outputRecords);
for(Serializable primaryKey : deleteRequest.getPrimaryKeys())
{
QRecord qRecord = new QRecord().withTableName(deleteRequest.getTableName()).withValue("id", primaryKey);
// todo uh, identify any errors?
- QRecordWithStatus recordWithStatus = new QRecordWithStatus(qRecord);
- recordsWithStatus.add(recordWithStatus);
+ QRecord outputRecord = new QRecord(qRecord);
+ outputRecords.add(outputRecord);
}
return rs;
diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java
index 73173920..c55fff9f 100644
--- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java
+++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java
@@ -22,6 +22,7 @@
package com.kingsrook.qqq.backend.module.rdbms.actions;
+import java.io.Serializable;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
@@ -30,12 +31,10 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.insert.InsertRequest;
import com.kingsrook.qqq.backend.core.model.actions.insert.InsertResult;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
-import com.kingsrook.qqq.backend.core.model.data.QRecordWithStatus;
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
import com.kingsrook.qqq.backend.core.modules.interfaces.InsertInterface;
-import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendMetaData;
-import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
+import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
@@ -50,9 +49,14 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
*******************************************************************************/
public InsertResult execute(InsertRequest insertRequest) throws QException
{
+ if(CollectionUtils.nullSafeIsEmpty(insertRequest.getRecords()))
+ {
+ throw (new QException("Request to insert 0 records."));
+ }
+
try
{
- InsertResult rs = new InsertResult();
+ InsertResult rs = new InsertResult();
QTableMetaData table = insertRequest.getTable();
List insertableFields = table.getFields().values().stream()
@@ -66,9 +70,9 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
.map(x -> "?")
.collect(Collectors.joining(", "));
- String tableName = table.getName();
- StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES");
- List