mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Merged feature/qol-improvements-20240801 into dev
This commit is contained in:
@ -37,7 +37,7 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
|||||||
**
|
**
|
||||||
** Note: One would imagine that this class shouldn't ever implement Serializable...
|
** Note: One would imagine that this class shouldn't ever implement Serializable...
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QBackendTransaction
|
public class QBackendTransaction implements AutoCloseable
|
||||||
{
|
{
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
@ -227,6 +227,11 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void performValidations(InsertInput insertInput, boolean isPreview) throws QException
|
public void performValidations(InsertInput insertInput, boolean isPreview) throws QException
|
||||||
{
|
{
|
||||||
|
if(CollectionUtils.nullSafeIsEmpty(insertInput.getRecords()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QTableMetaData table = insertInput.getTable();
|
QTableMetaData table = insertInput.getTable();
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////
|
||||||
|
@ -68,7 +68,7 @@ public class QValueFormatter
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static String formatValue(QFieldMetaData field, Serializable value)
|
public static String formatValue(QFieldMetaData field, Serializable value)
|
||||||
{
|
{
|
||||||
return (formatValue(field.getDisplayFormat(), field.getName(), value));
|
return (formatValue(field.getDisplayFormat(), field.getType(), field.getName(), value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ public class QValueFormatter
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static String formatValue(String displayFormat, Serializable value)
|
public static String formatValue(String displayFormat, Serializable value)
|
||||||
{
|
{
|
||||||
return (formatValue(displayFormat, "", value));
|
return (formatValue(displayFormat, null, "", value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ public class QValueFormatter
|
|||||||
** For a display format string, an optional fieldName (only used for logging),
|
** For a display format string, an optional fieldName (only used for logging),
|
||||||
** and a value, apply the format.
|
** and a value, apply the format.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static String formatValue(String displayFormat, String fieldName, Serializable value)
|
private static String formatValue(String displayFormat, QFieldType fieldType, String fieldName, Serializable value)
|
||||||
{
|
{
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// null values get null results //
|
// null values get null results //
|
||||||
@ -107,6 +107,11 @@ public class QValueFormatter
|
|||||||
return formatBoolean(b);
|
return formatBoolean(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(QFieldType.BOOLEAN.equals(fieldType))
|
||||||
|
{
|
||||||
|
return formatBoolean(ValueUtils.getValueAsBoolean(value));
|
||||||
|
}
|
||||||
|
|
||||||
if(value instanceof LocalTime lt)
|
if(value instanceof LocalTime lt)
|
||||||
{
|
{
|
||||||
return formatLocalTime(lt);
|
return formatLocalTime(lt);
|
||||||
@ -404,6 +409,7 @@ public class QValueFormatter
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** For a single record, set its display values - where caller (meant to stay private)
|
** For a single record, set its display values - where caller (meant to stay private)
|
||||||
** can specify if they've already done fieldBehaviors (to avoid re-doing).
|
** can specify if they've already done fieldBehaviors (to avoid re-doing).
|
||||||
|
@ -22,8 +22,8 @@
|
|||||||
package com.kingsrook.qqq.backend.core.exceptions;
|
package com.kingsrook.qqq.backend.core.exceptions;
|
||||||
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -55,12 +55,11 @@ public class QInstanceValidationException extends QException
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QInstanceValidationException(List<String> reasons)
|
public QInstanceValidationException(List<String> reasons)
|
||||||
{
|
{
|
||||||
super(
|
super((CollectionUtils.nullSafeHasContents(reasons))
|
||||||
(reasons != null && reasons.size() > 0)
|
? "Instance validation failed for the following reasons:\n - " + StringUtils.join("\n - ", reasons) + "\n(" + reasons.size() + " Total reason" + StringUtils.plural(reasons) + ")"
|
||||||
? "Instance validation failed for the following reasons:\n - " + StringUtils.join("\n - ", reasons)
|
: "Validation failed, but no reasons were provided");
|
||||||
: "Validation failed, but no reasons were provided");
|
|
||||||
|
|
||||||
if(reasons != null && reasons.size() > 0)
|
if(CollectionUtils.nullSafeHasContents(reasons))
|
||||||
{
|
{
|
||||||
this.reasons = reasons;
|
this.reasons = reasons;
|
||||||
}
|
}
|
||||||
@ -68,25 +67,6 @@ public class QInstanceValidationException extends QException
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Constructor of an array/varargs of reasons. They feed into the core exception message.
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
public QInstanceValidationException(String... reasons)
|
|
||||||
{
|
|
||||||
super(
|
|
||||||
(reasons != null && reasons.length > 0)
|
|
||||||
? "Instance validation failed for the following reasons: " + StringUtils.joinWithCommasAndAnd(Arrays.stream(reasons).toList())
|
|
||||||
: "Validation failed, but no reasons were provided");
|
|
||||||
|
|
||||||
if(reasons != null && reasons.length > 0)
|
|
||||||
{
|
|
||||||
this.reasons = Arrays.stream(reasons).toList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Constructor of message & cause - does not populate reasons!
|
** Constructor of message & cause - does not populate reasons!
|
||||||
**
|
**
|
||||||
|
@ -59,6 +59,30 @@ public class AggregateInput extends AbstractTableActionInput
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public AggregateInput(String tableName)
|
||||||
|
{
|
||||||
|
this();
|
||||||
|
setTableName(tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public AggregateInput withTableName(String tableName)
|
||||||
|
{
|
||||||
|
super.withTableName(tableName);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for filter
|
** Getter for filter
|
||||||
**
|
**
|
||||||
|
@ -29,6 +29,7 @@ import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
@ -44,6 +45,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.savedviews.SavedView;
|
import com.kingsrook.qqq.backend.core.model.savedviews.SavedView;
|
||||||
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -78,10 +80,10 @@ public class QuerySavedViewProcess implements BackendStep
|
|||||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
{
|
{
|
||||||
ActionHelper.validateSession(runBackendStepInput);
|
ActionHelper.validateSession(runBackendStepInput);
|
||||||
|
Integer savedViewId = runBackendStepInput.getValueInteger("id");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Integer savedViewId = runBackendStepInput.getValueInteger("id");
|
|
||||||
if(savedViewId != null)
|
if(savedViewId != null)
|
||||||
{
|
{
|
||||||
GetInput input = new GetInput();
|
GetInput input = new GetInput();
|
||||||
@ -89,6 +91,11 @@ public class QuerySavedViewProcess implements BackendStep
|
|||||||
input.setPrimaryKey(savedViewId);
|
input.setPrimaryKey(savedViewId);
|
||||||
|
|
||||||
GetOutput output = new GetAction().execute(input);
|
GetOutput output = new GetAction().execute(input);
|
||||||
|
if(output.getRecord() == null)
|
||||||
|
{
|
||||||
|
throw (new QNotFoundException("The requested view was not found."));
|
||||||
|
}
|
||||||
|
|
||||||
runBackendStepOutput.addRecord(output.getRecord());
|
runBackendStepOutput.addRecord(output.getRecord());
|
||||||
runBackendStepOutput.addValue("savedView", output.getRecord());
|
runBackendStepOutput.addValue("savedView", output.getRecord());
|
||||||
runBackendStepOutput.addValue("savedViewList", (Serializable) List.of(output.getRecord()));
|
runBackendStepOutput.addValue("savedViewList", (Serializable) List.of(output.getRecord()));
|
||||||
@ -108,6 +115,11 @@ public class QuerySavedViewProcess implements BackendStep
|
|||||||
runBackendStepOutput.addValue("savedViewList", (Serializable) output.getRecords());
|
runBackendStepOutput.addValue("savedViewList", (Serializable) output.getRecords());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch(QNotFoundException qnfe)
|
||||||
|
{
|
||||||
|
LOG.info("View not found", logPair("savedViewId", savedViewId));
|
||||||
|
throw (qnfe);
|
||||||
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
LOG.warn("Error querying for saved views", e);
|
LOG.warn("Error querying for saved views", e);
|
||||||
|
@ -433,6 +433,8 @@ public class ProcessLockUtils
|
|||||||
{
|
{
|
||||||
throw (new QException("Error deleting processLock record: " + deleteOutput.getRecordsWithErrors().get(0).getErrorsAsString()));
|
throw (new QException("Error deleting processLock record: " + deleteOutput.getRecordsWithErrors().get(0).getErrorsAsString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG.info("Released process lock", logPair("id", processLock.getId()), logPair("key", processLock.getKey()), logPair("typeId", processLock.getProcessLockTypeId()), logPair("details", processLock.getDetails()));
|
||||||
}
|
}
|
||||||
catch(QException e)
|
catch(QException e)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.collections.MutableMap;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Hash that provides "counting" capability -- keys map to Integers that
|
||||||
|
** are automatically/easily summed to
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class CountingHash<K extends Serializable> extends AbstractMap<K, Integer> implements Serializable
|
||||||
|
{
|
||||||
|
private Map<K, Integer> map = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Default constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public CountingHash()
|
||||||
|
{
|
||||||
|
this.map = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor where you can supply a source map (e.g., if you want a specific
|
||||||
|
** Map type (like LinkedHashMap), or with pre-values.
|
||||||
|
**
|
||||||
|
** Note - the input map will be wrapped in a MutableMap - so - it'll be mutable.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public CountingHash(Map<K, Integer> sourceMap)
|
||||||
|
{
|
||||||
|
this.map = new MutableMap<>(sourceMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Increment the value for the specified key by 1.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer add(K key)
|
||||||
|
{
|
||||||
|
Integer value = getOrCreateListForKey(key);
|
||||||
|
Integer sum = value + 1;
|
||||||
|
map.put(key, sum);
|
||||||
|
return (sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Increment the value for the specified key by the supplied addend
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer add(K key, Integer addend)
|
||||||
|
{
|
||||||
|
Integer value = getOrCreateListForKey(key);
|
||||||
|
Integer sum = value + addend;
|
||||||
|
map.put(key, sum);
|
||||||
|
return (sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private Integer getOrCreateListForKey(K key)
|
||||||
|
{
|
||||||
|
Integer value;
|
||||||
|
|
||||||
|
if(!this.map.containsKey(key))
|
||||||
|
{
|
||||||
|
this.map.put(key, 0);
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = this.map.get(key);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
public Set<Entry<K, Integer>> entrySet()
|
||||||
|
{
|
||||||
|
return this.map.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -89,6 +89,10 @@ class QValueFormatterTest extends BaseTest
|
|||||||
assertNull(QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), null));
|
assertNull(QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), null));
|
||||||
assertEquals("Yes", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), true));
|
assertEquals("Yes", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), true));
|
||||||
assertEquals("No", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), false));
|
assertEquals("No", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), false));
|
||||||
|
assertEquals("Yes", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), "true"));
|
||||||
|
assertEquals("No", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), "false"));
|
||||||
|
assertEquals("true", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.STRING), "true"));
|
||||||
|
assertEquals("false", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.STRING), "false"));
|
||||||
|
|
||||||
assertNull(QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.TIME), null));
|
assertNull(QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.TIME), null));
|
||||||
assertEquals("5:00:00 AM", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.TIME), LocalTime.of(5, 0)));
|
assertEquals("5:00:00 AM", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.TIME), LocalTime.of(5, 0)));
|
||||||
|
@ -83,7 +83,7 @@ class SavedViewProcessTests extends BaseTest
|
|||||||
runProcessInput.addValue("tableName", tableName);
|
runProcessInput.addValue("tableName", tableName);
|
||||||
runProcessInput.addValue("viewJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
runProcessInput.addValue("viewJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
||||||
assertEquals(1, savedViewList.size());
|
assertEquals(1, savedViewList.size());
|
||||||
savedViewId = savedViewList.get(0).getValueInteger("id");
|
savedViewId = savedViewList.get(0).getValueInteger("id");
|
||||||
assertNotNull(savedViewId);
|
assertNotNull(savedViewId);
|
||||||
@ -104,7 +104,7 @@ class SavedViewProcessTests extends BaseTest
|
|||||||
runProcessInput.setProcessName(QuerySavedViewProcess.getProcessMetaData().getName());
|
runProcessInput.setProcessName(QuerySavedViewProcess.getProcessMetaData().getName());
|
||||||
runProcessInput.addValue("tableName", tableName);
|
runProcessInput.addValue("tableName", tableName);
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
||||||
assertEquals(1, savedViewList.size());
|
assertEquals(1, savedViewList.size());
|
||||||
assertEquals(1, savedViewList.get(0).getValueInteger("id"));
|
assertEquals(1, savedViewList.get(0).getValueInteger("id"));
|
||||||
assertEquals("My View", savedViewList.get(0).getValueString("label"));
|
assertEquals("My View", savedViewList.get(0).getValueString("label"));
|
||||||
@ -121,7 +121,7 @@ class SavedViewProcessTests extends BaseTest
|
|||||||
runProcessInput.addValue("tableName", tableName);
|
runProcessInput.addValue("tableName", tableName);
|
||||||
runProcessInput.addValue("viewJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
runProcessInput.addValue("viewJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
||||||
assertEquals(1, savedViewList.size());
|
assertEquals(1, savedViewList.size());
|
||||||
assertEquals(1, savedViewList.get(0).getValueInteger("id"));
|
assertEquals(1, savedViewList.get(0).getValueInteger("id"));
|
||||||
assertEquals("My Updated View", savedViewList.get(0).getValueString("label"));
|
assertEquals("My Updated View", savedViewList.get(0).getValueString("label"));
|
||||||
@ -184,7 +184,64 @@ class SavedViewProcessTests extends BaseTest
|
|||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
assertEquals(0, ((List<?>) runProcessOutput.getValues().get("savedViewList")).size());
|
assertEquals(0, ((List<?>) runProcessOutput.getValues().get("savedViewList")).size());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testNotFoundThrowsProperly() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
new SavedViewsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
String tableName = TestUtils.TABLE_NAME_PERSON_MEMORY;
|
||||||
|
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
// get one by id when it doesn't exist - should throw //
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(QuerySavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("tableName", tableName);
|
||||||
|
runProcessInput.addValue("id", -1);
|
||||||
|
assertThatThrownBy(() -> new RunProcessAction().execute(runProcessInput))
|
||||||
|
.hasMessageContaining("view was not found")
|
||||||
|
.isInstanceOf(QUserFacingException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer savedViewId;
|
||||||
|
{
|
||||||
|
//////////////////////
|
||||||
|
// store a new view //
|
||||||
|
//////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(StoreSavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("label", "My View");
|
||||||
|
runProcessInput.addValue("tableName", tableName);
|
||||||
|
runProcessInput.addValue("viewJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
||||||
|
assertEquals(1, savedViewList.size());
|
||||||
|
savedViewId = savedViewList.get(0).getValueInteger("id");
|
||||||
|
assertNotNull(savedViewId);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
////////////////////////////////////////
|
||||||
|
// get now with valid id, should work //
|
||||||
|
////////////////////////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(QuerySavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("tableName", tableName);
|
||||||
|
runProcessInput.addValue("id", savedViewId);
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
||||||
|
assertEquals(1, savedViewList.size());
|
||||||
|
assertEquals(1, savedViewList.get(0).getValueInteger("id"));
|
||||||
|
assertEquals("My View", savedViewList.get(0).getValueString("label"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for CountingHash
|
||||||
|
*******************************************************************************/
|
||||||
|
class CountingHashTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test()
|
||||||
|
{
|
||||||
|
CountingHash<String> countingHash = new CountingHash<>();
|
||||||
|
|
||||||
|
assertNull(countingHash.get("a"));
|
||||||
|
|
||||||
|
countingHash.add("a");
|
||||||
|
assertEquals(1, countingHash.get("a"));
|
||||||
|
|
||||||
|
countingHash.add("a");
|
||||||
|
assertEquals(2, countingHash.get("a"));
|
||||||
|
|
||||||
|
countingHash.add("a", 2);
|
||||||
|
assertEquals(4, countingHash.get("a"));
|
||||||
|
|
||||||
|
countingHash.add("b", 5);
|
||||||
|
assertEquals(5, countingHash.get("b"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testAlwaysMutable()
|
||||||
|
{
|
||||||
|
CountingHash<String> alwaysMutable = new CountingHash<>(Map.of("A", 5));
|
||||||
|
alwaysMutable.add("A");
|
||||||
|
alwaysMutable.add("B");
|
||||||
|
assertEquals(6, alwaysMutable.get("A"));
|
||||||
|
assertEquals(1, alwaysMutable.get("B"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -737,20 +737,7 @@ public class BaseAPIActionUtil
|
|||||||
case API_KEY_HEADER -> request.setHeader("API-Key", backendMetaData.getApiKey());
|
case API_KEY_HEADER -> request.setHeader("API-Key", backendMetaData.getApiKey());
|
||||||
case API_TOKEN -> request.setHeader("Authorization", "Token " + backendMetaData.getApiKey());
|
case API_TOKEN -> request.setHeader("Authorization", "Token " + backendMetaData.getApiKey());
|
||||||
case OAUTH2 -> request.setHeader("Authorization", "Bearer " + getOAuth2Token());
|
case OAUTH2 -> request.setHeader("Authorization", "Bearer " + getOAuth2Token());
|
||||||
case API_KEY_QUERY_PARAM ->
|
case API_KEY_QUERY_PARAM -> addApiKeyQueryParamToRequest(request);
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
String uri = request.getURI().toString();
|
|
||||||
uri += (uri.contains("?") ? "&" : "?");
|
|
||||||
uri += backendMetaData.getApiKeyQueryParamName() + "=" + backendMetaData.getApiKey();
|
|
||||||
request.setURI(new URI(uri));
|
|
||||||
}
|
|
||||||
catch(URISyntaxException e)
|
|
||||||
{
|
|
||||||
throw (new QException("Error setting authorization query parameter", e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case CUSTOM ->
|
case CUSTOM ->
|
||||||
{
|
{
|
||||||
handleCustomAuthorization(request);
|
handleCustomAuthorization(request);
|
||||||
@ -761,6 +748,35 @@ public class BaseAPIActionUtil
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
protected void addApiKeyQueryParamToRequest(HttpRequestBase request) throws QException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String uri = request.getURI().toString();
|
||||||
|
String pair = backendMetaData.getApiKeyQueryParamName() + "=" + backendMetaData.getApiKey();
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// avoid re-adding the name=value pair if it's already there (e.g., for a retry) //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(!uri.contains(pair))
|
||||||
|
{
|
||||||
|
uri += (uri.contains("?") ? "&" : "?");
|
||||||
|
uri += pair;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.setURI(new URI(uri));
|
||||||
|
}
|
||||||
|
catch(URISyntaxException e)
|
||||||
|
{
|
||||||
|
throw (new QException("Error setting authorization query parameter", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -1210,39 +1226,11 @@ public class BaseAPIActionUtil
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String requestBody = null;
|
OutboundAPILog outboundAPILog = generateOutboundApiLogRecord(request, response);
|
||||||
if(request instanceof HttpEntityEnclosingRequest entityRequest)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
requestBody = StringUtils.join("\n", IOUtils.readLines(entityRequest.getEntity().getContent(), StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
// leave it null...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
// mask api keys in query strings //
|
|
||||||
////////////////////////////////////
|
|
||||||
String url = request.getURI().toString();
|
|
||||||
if(backendMetaData.getAuthorizationType().equals(AuthorizationType.API_KEY_QUERY_PARAM))
|
|
||||||
{
|
|
||||||
url = url.replaceFirst(backendMetaData.getApiKey(), "******");
|
|
||||||
}
|
|
||||||
|
|
||||||
InsertInput insertInput = new InsertInput();
|
InsertInput insertInput = new InsertInput();
|
||||||
insertInput.setTableName(table.getName());
|
insertInput.setTableName(table.getName());
|
||||||
insertInput.setRecords(List.of(new OutboundAPILog()
|
insertInput.setRecords(List.of(outboundAPILog.toQRecord()));
|
||||||
.withMethod(request.getMethod())
|
|
||||||
.withUrl(url)
|
|
||||||
.withTimestamp(Instant.now())
|
|
||||||
.withRequestBody(requestBody)
|
|
||||||
.withStatusCode(response.getStatusCode())
|
|
||||||
.withResponseBody(response.getContent())
|
|
||||||
.toQRecord()
|
|
||||||
));
|
|
||||||
new InsertAction().executeAsync(insertInput);
|
new InsertAction().executeAsync(insertInput);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
@ -1253,6 +1241,44 @@ public class BaseAPIActionUtil
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
**
|
||||||
|
***************************************************************************/
|
||||||
|
public OutboundAPILog generateOutboundApiLogRecord(HttpRequestBase request, QHttpResponse response)
|
||||||
|
{
|
||||||
|
String requestBody = null;
|
||||||
|
if(request instanceof HttpEntityEnclosingRequest entityRequest)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
requestBody = StringUtils.join("\n", IOUtils.readLines(entityRequest.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
// leave it null...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
// mask api keys in query strings //
|
||||||
|
////////////////////////////////////
|
||||||
|
String url = request.getURI().toString();
|
||||||
|
if(backendMetaData.getAuthorizationType().equals(AuthorizationType.API_KEY_QUERY_PARAM))
|
||||||
|
{
|
||||||
|
url = url.replaceAll(backendMetaData.getApiKey(), "******");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OutboundAPILog()
|
||||||
|
.withMethod(request.getMethod())
|
||||||
|
.withUrl(url)
|
||||||
|
.withTimestamp(Instant.now())
|
||||||
|
.withRequestBody(requestBody)
|
||||||
|
.withStatusCode(response.getStatusCode())
|
||||||
|
.withResponseBody(response.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -61,6 +61,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeConsumer;
|
||||||
import com.kingsrook.qqq.backend.module.api.BaseTest;
|
import com.kingsrook.qqq.backend.module.api.BaseTest;
|
||||||
import com.kingsrook.qqq.backend.module.api.TestUtils;
|
import com.kingsrook.qqq.backend.module.api.TestUtils;
|
||||||
import com.kingsrook.qqq.backend.module.api.exceptions.RateLimitException;
|
import com.kingsrook.qqq.backend.module.api.exceptions.RateLimitException;
|
||||||
@ -74,6 +75,7 @@ import org.apache.http.Header;
|
|||||||
import org.apache.http.HttpEntity;
|
import org.apache.http.HttpEntity;
|
||||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
@ -868,6 +870,72 @@ class BaseAPIActionUtilTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testAuthorizationApiKeyQueryParam() throws QException
|
||||||
|
{
|
||||||
|
APIBackendMetaData backend = (APIBackendMetaData) QContext.getQInstance().getBackend(TestUtils.MOCK_BACKEND_NAME);
|
||||||
|
backend.setAuthorizationType(AuthorizationType.API_KEY_QUERY_PARAM);
|
||||||
|
backend.setApiKeyQueryParamName("apikey");
|
||||||
|
backend.setApiKey("9876-WXYZ");
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// this will make it not use the mock makeRequest method, //
|
||||||
|
// but instead the mock executeHttpRequest, so we can test code from the base makeRequest //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
mockApiUtilsHelper.setUseMock(false);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// we'll want to assert that the URL has the api query string - and just //
|
||||||
|
// one copy of it (as we once had a bug where it got duplicated upon retry) //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
UnsafeConsumer<HttpRequestBase, Exception> asserter = request -> assertThat(request.getURI().toString())
|
||||||
|
.contains("?apikey=9876-WXYZ")
|
||||||
|
.doesNotContain("?apikey=9876-WXYZ&apikey=9876-WXYZ");
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
// queue up a 429, so we'll try-again //
|
||||||
|
////////////////////////////////////////
|
||||||
|
mockApiUtilsHelper.setMockRequestAsserter(asserter);
|
||||||
|
mockApiUtilsHelper.enqueueMockResponse(new QHttpResponse().withStatusCode(429).withContent(""));
|
||||||
|
|
||||||
|
//////////////////////
|
||||||
|
// queue a response //
|
||||||
|
//////////////////////
|
||||||
|
mockApiUtilsHelper.setMockRequestAsserter(asserter);
|
||||||
|
mockApiUtilsHelper.enqueueMockResponse("""
|
||||||
|
{"id": 3, "name": "Bart"},
|
||||||
|
""");
|
||||||
|
|
||||||
|
GetOutput getOutput = runSimpleGetAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testGenerateOutboundApiLogRecord() throws QException
|
||||||
|
{
|
||||||
|
APIBackendMetaData backend = (APIBackendMetaData) QContext.getQInstance().getBackend(TestUtils.MOCK_BACKEND_NAME);
|
||||||
|
backend.setAuthorizationType(AuthorizationType.API_KEY_QUERY_PARAM);
|
||||||
|
backend.setApiKeyQueryParamName("apikey");
|
||||||
|
backend.setApiKey("9876-WXYZ");
|
||||||
|
|
||||||
|
MockApiActionUtils mockApiActionUtils = new MockApiActionUtils();
|
||||||
|
mockApiActionUtils.setBackendMetaData(backend);
|
||||||
|
OutboundAPILog outboundAPILog = mockApiActionUtils.generateOutboundApiLogRecord(new HttpGet("...?apikey=9876-WXYZ"), new QHttpResponse());
|
||||||
|
|
||||||
|
assertThat(outboundAPILog.getUrl())
|
||||||
|
.doesNotContain("9876-WXYZ")
|
||||||
|
.contains("?apikey=*****");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -661,7 +661,7 @@ public abstract class AbstractRDBMSAction
|
|||||||
}
|
}
|
||||||
else if(!expectedNoOfParams.equals(values.size()))
|
else if(!expectedNoOfParams.equals(values.size()))
|
||||||
{
|
{
|
||||||
throw new IllegalArgumentException("Incorrect number of values given for criteria [" + field.getName() + "]");
|
throw new IllegalArgumentException("Incorrect number of values given for criteria [" + field.getName() + "] (expected " + expectedNoOfParams + ", received " + values.size() + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -117,6 +118,14 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
|
|||||||
actionTimeoutHelper = new ActionTimeoutHelper(aggregateInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
|
actionTimeoutHelper = new ActionTimeoutHelper(aggregateInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
|
||||||
actionTimeoutHelper.start();
|
actionTimeoutHelper.start();
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// to avoid counting time spent acquiring a connection, re-set the queryStat startTimestamp here //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(queryStat != null)
|
||||||
|
{
|
||||||
|
queryStat.setStartTimestamp(Instant.now());
|
||||||
|
}
|
||||||
|
|
||||||
QueryManager.executeStatement(statement, sql, ((ResultSet resultSet) ->
|
QueryManager.executeStatement(statement, sql, ((ResultSet resultSet) ->
|
||||||
{
|
{
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -97,6 +98,14 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
|
|||||||
actionTimeoutHelper = new ActionTimeoutHelper(countInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
|
actionTimeoutHelper = new ActionTimeoutHelper(countInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
|
||||||
actionTimeoutHelper.start();
|
actionTimeoutHelper.start();
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// to avoid counting time spent acquiring a connection, re-set the queryStat startTimestamp here //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(queryStat != null)
|
||||||
|
{
|
||||||
|
queryStat.setStartTimestamp(Instant.now());
|
||||||
|
}
|
||||||
|
|
||||||
QueryManager.executeStatement(statement, sql, ((ResultSet resultSet) ->
|
QueryManager.executeStatement(statement, sql, ((ResultSet resultSet) ->
|
||||||
{
|
{
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -28,6 +28,7 @@ import java.sql.PreparedStatement;
|
|||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.ResultSetMetaData;
|
import java.sql.ResultSetMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -166,6 +167,14 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
|||||||
actionTimeoutHelper = new ActionTimeoutHelper(queryInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
|
actionTimeoutHelper = new ActionTimeoutHelper(queryInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
|
||||||
actionTimeoutHelper.start();
|
actionTimeoutHelper.start();
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// to avoid counting time spent acquiring a connection, re-set the queryStat startTimestamp here //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(queryStat != null)
|
||||||
|
{
|
||||||
|
queryStat.setStartTimestamp(Instant.now());
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////
|
//////////////////////////////////////////////
|
||||||
// execute the query - iterate over results //
|
// execute the query - iterate over results //
|
||||||
//////////////////////////////////////////////
|
//////////////////////////////////////////////
|
||||||
|
Reference in New Issue
Block a user