diff --git a/.circleci/config.yml b/.circleci/config.yml
index 1d96c5a0..39d8dc38 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -153,7 +153,7 @@ workflows:
context: [ qqq-maven-registry-credentials, build-qqq-sample-app ]
filters:
branches:
- ignore: /dev/
+ ignore: /(dev|integration.*)/
tags:
ignore: /(version|snapshot)-.*/
@@ -163,7 +163,7 @@ workflows:
context: [ qqq-maven-registry-credentials, build-qqq-sample-app ]
filters:
branches:
- only: /dev/
+ only: /(dev|integration.*)/
tags:
only: /(version|snapshot)-.*/
- publish_asciidoc:
diff --git a/qqq-backend-core/pom.xml b/qqq-backend-core/pom.xml
index 38bad7f5..26a4848f 100644
--- a/qqq-backend-core/pom.xml
+++ b/qqq-backend-core/pom.xml
@@ -102,6 +102,16 @@
fastexcel
0.12.15
+
+ org.apache.poi
+ poi
+ 5.2.5
+
+
+ org.apache.poi
+ poi-ooxml
+ 5.2.5
+
com.auth0
auth0
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/AsyncJobManager.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/AsyncJobManager.java
index 6cc317d5..875097fa 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/AsyncJobManager.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/AsyncJobManager.java
@@ -169,17 +169,24 @@ public class AsyncJobManager
LOG.debug("Completed job " + uuidAndTypeStateKey.getUuid());
return (result);
}
- catch(Exception e)
+ catch(Throwable t)
{
asyncJobStatus.setState(AsyncJobState.ERROR);
- asyncJobStatus.setCaughtException(e);
+ if(t instanceof Exception e)
+ {
+ asyncJobStatus.setCaughtException(e);
+ }
+ else
+ {
+ asyncJobStatus.setCaughtException(new QException("Caught throwable", t));
+ }
getStateProvider().put(uuidAndTypeStateKey, asyncJobStatus);
//////////////////////////////////////////////////////
// if user facing, just log an info, warn otherwise //
//////////////////////////////////////////////////////
- LOG.log((e instanceof QUserFacingException) ? Level.INFO : Level.WARN, "Job ended with an exception", e, logPair("jobId", uuidAndTypeStateKey.getUuid()));
- throw (new CompletionException(e));
+ LOG.log((t instanceof QUserFacingException) ? Level.INFO : Level.WARN, "Job ended with an exception", t, logPair("jobId", uuidAndTypeStateKey.getUuid()));
+ throw (new CompletionException(t));
}
finally
{
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/AsyncJobStatus.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/AsyncJobStatus.java
index 3ec54516..c5cf95f3 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/AsyncJobStatus.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/AsyncJobStatus.java
@@ -31,6 +31,7 @@ import java.io.Serializable;
*******************************************************************************/
public class AsyncJobStatus implements Serializable
{
+ private String jobName;
private AsyncJobState state;
private String message;
private Integer current;
@@ -187,4 +188,36 @@ public class AsyncJobStatus implements Serializable
{
this.cancelRequested = cancelRequested;
}
+
+
+
+ /*******************************************************************************
+ ** Getter for jobName
+ *******************************************************************************/
+ public String getJobName()
+ {
+ return (this.jobName);
+ }
+
+
+
+ /*******************************************************************************
+ ** Setter for jobName
+ *******************************************************************************/
+ public void setJobName(String jobName)
+ {
+ this.jobName = jobName;
+ }
+
+
+
+ /*******************************************************************************
+ ** Fluent setter for jobName
+ *******************************************************************************/
+ public AsyncJobStatus withJobName(String jobName)
+ {
+ this.jobName = jobName;
+ return (this);
+ }
+
}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/NonPersistedAsyncJobCallback.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/NonPersistedAsyncJobCallback.java
new file mode 100644
index 00000000..2373dc79
--- /dev/null
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/async/NonPersistedAsyncJobCallback.java
@@ -0,0 +1,62 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2024. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * 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
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.backend.core.actions.async;
+
+
+import java.util.UUID;
+
+
+/*******************************************************************************
+ ** subclass designed to be used when we want there to be an instance (so code
+ ** doesn't have to all be null-tolerant), but there's no one who will ever be
+ ** reading the status data, so we don't need to store the object in a
+ ** state provider.
+ *******************************************************************************/
+public class NonPersistedAsyncJobCallback extends AsyncJobCallback
+{
+ private final AsyncJobStatus asyncJobStatus;
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ public NonPersistedAsyncJobCallback(UUID jobUUID, AsyncJobStatus asyncJobStatus)
+ {
+ super(jobUUID, asyncJobStatus);
+ this.asyncJobStatus = asyncJobStatus;
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Override
+ protected void storeUpdatedStatus()
+ {
+ ///////////////////////////////////////////////////////////////////////////////////////
+ // noop - cf. base class, which writes to persistence here (our point is, we do not) //
+ ///////////////////////////////////////////////////////////////////////////////////////
+ }
+
+}
diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/audits/DMLAuditAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/audits/DMLAuditAction.java
index 3ab02ce4..044a24b7 100644
--- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/audits/DMLAuditAction.java
+++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/audits/DMLAuditAction.java
@@ -303,7 +303,7 @@ public class DMLAuditAction extends AbstractQActionFunction>> pvsData = new ArrayList<>();
- List pvsLabels = new ArrayList<>();
- List pvsNames = new ArrayList<>();
+ List>> dataList = new ArrayList<>();
+ List labelList = new ArrayList<>();
+ List nameList = new ArrayList<>();
List missingRequiredSelections = new ArrayList<>();
for(WidgetDropdownData dropdownData : CollectionUtils.nonNullList(metaData.getDropdowns()))
{
- String possibleValueSourceName = dropdownData.getPossibleValueSourceName();
- QPossibleValueSource possibleValueSource = input.getInstance().getPossibleValueSource(possibleValueSourceName);
-
- /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // this looks complicated, but is just look for a label in the dropdown data and if found use it, //
- // otherwise look for label in PVS and if found use that, otherwise just use the PVS name //
- /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- String pvsLabel = dropdownData.getLabel() != null ? dropdownData.getLabel() : (possibleValueSource.getLabel() != null ? possibleValueSource.getLabel() : possibleValueSourceName);
- pvsLabels.add(pvsLabel);
- pvsNames.add(possibleValueSourceName);
-
- SearchPossibleValueSourceInput pvsInput = new SearchPossibleValueSourceInput();
- pvsInput.setPossibleValueSourceName(possibleValueSourceName);
-
- if(dropdownData.getForeignKeyFieldName() != null)
+ if(WidgetDropdownType.DATE_PICKER.equals(dropdownData.getType()))
{
- ////////////////////////////////////////
- // look for an id in the query params //
- ////////////////////////////////////////
- Integer id = null;
- if(input.getQueryParams() != null && input.getQueryParams().containsKey("id") && StringUtils.hasContent(input.getQueryParams().get("id")))
+ String name = dropdownData.getName();
+ nameList.add(name);
+ labelList.add(dropdownData.getLabel());
+ dataList.add(new ArrayList<>());
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // sure that something has been selected, and if not, display a message that a selection needs made //
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ if(dropdownData.getIsRequired())
{
- id = Integer.parseInt(input.getQueryParams().get("id"));
- }
- if(id != null)
- {
- pvsInput.setDefaultQueryFilter(new QQueryFilter().withCriteria(
- new QFilterCriteria(
- dropdownData.getForeignKeyFieldName(),
- QCriteriaOperator.EQUALS,
- id)));
+ if(!input.getQueryParams().containsKey(name) || !StringUtils.hasContent(input.getQueryParams().get(name)))
+ {
+ missingRequiredSelections.add(dropdownData.getLabel());
+ }
}
}
-
- SearchPossibleValueSourceOutput output = new SearchPossibleValueSourceAction().execute(pvsInput);
-
- List