mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 21:20:45 +00:00
Merge branch 'feature/query-stats' into feature/CTLE-507-custom-ct-live-packing-slips
This commit is contained in:
@ -10,7 +10,7 @@ The bundle contains all of the sub-jars. It is named:
|
|||||||
|
|
||||||
```qqq-${version}.jar```
|
```qqq-${version}.jar```
|
||||||
|
|
||||||
You can also use fine grained jars:
|
You can also use fine-grained jars:
|
||||||
- `qqq-backend-core`: The core module. Useful if you're developing other modules.
|
- `qqq-backend-core`: The core module. Useful if you're developing other modules.
|
||||||
- `qqq-backend-module-rdbms`: Backend module for working with Relational Databases.
|
- `qqq-backend-module-rdbms`: Backend module for working with Relational Databases.
|
||||||
- `qqq-backend-module-filesystem`: Backend module for working with Filesystems (including AWS S3).
|
- `qqq-backend-module-filesystem`: Backend module for working with Filesystems (including AWS S3).
|
||||||
@ -35,4 +35,3 @@ GNU Affero General Public License for more details.
|
|||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
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/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOu
|
|||||||
** Interface for the Aggregate action.
|
** Interface for the Aggregate action.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public interface AggregateInterface
|
public interface AggregateInterface extends BaseQueryInterface
|
||||||
{
|
{
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. 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.actions.interfaces;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Base class for "query" (e.g., read-operations) action interfaces (query, count, aggregate).
|
||||||
|
** Initially just here for the QueryStat methods - if we expand those to apply
|
||||||
|
** to insert/update/delete, well, then rename this maybe to BaseActionInterface?
|
||||||
|
*******************************************************************************/
|
||||||
|
public interface BaseQueryInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
default void setQueryStat(QueryStat queryStat)
|
||||||
|
{
|
||||||
|
//////////
|
||||||
|
// noop //
|
||||||
|
//////////
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
default QueryStat getQueryStat()
|
||||||
|
{
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
default void setQueryStatFirstResultTime()
|
||||||
|
{
|
||||||
|
QueryStat queryStat = getQueryStat();
|
||||||
|
if(queryStat != null)
|
||||||
|
{
|
||||||
|
if(queryStat.getFirstResultTimestamp() == null)
|
||||||
|
{
|
||||||
|
queryStat.setFirstResultTimestamp(Instant.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -31,7 +31,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
|||||||
** Interface for the Count action.
|
** Interface for the Count action.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public interface CountInterface
|
public interface CountInterface extends BaseQueryInterface
|
||||||
{
|
{
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
|
@ -31,10 +31,11 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
|||||||
** Interface for the Query action.
|
** Interface for the Query action.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public interface QueryInterface
|
public interface QueryInterface extends BaseQueryInterface
|
||||||
{
|
{
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
QueryOutput execute(QueryInput queryInput) throws QException;
|
QueryOutput execute(QueryInput queryInput) throws QException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,14 @@ package com.kingsrook.qqq.backend.core.actions.tables;
|
|||||||
|
|
||||||
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.interfaces.AggregateInterface;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatManager;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||||
|
|
||||||
@ -43,11 +48,20 @@ public class AggregateAction
|
|||||||
{
|
{
|
||||||
ActionHelper.validateSession(aggregateInput);
|
ActionHelper.validateSession(aggregateInput);
|
||||||
|
|
||||||
|
QTableMetaData table = aggregateInput.getTable();
|
||||||
|
QBackendMetaData backend = aggregateInput.getBackend();
|
||||||
|
|
||||||
|
QueryStat queryStat = QueryStatManager.newQueryStat(backend, table, aggregateInput.getFilter());
|
||||||
|
|
||||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(aggregateInput.getBackend());
|
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(aggregateInput.getBackend());
|
||||||
// todo pre-customization - just get to modify the request?
|
|
||||||
AggregateOutput aggregateOutput = qModule.getAggregateInterface().execute(aggregateInput);
|
AggregateInterface aggregateInterface = qModule.getAggregateInterface();
|
||||||
// todo post-customization - can do whatever w/ the result if you want
|
aggregateInterface.setQueryStat(queryStat);
|
||||||
|
AggregateOutput aggregateOutput = aggregateInterface.execute(aggregateInput);
|
||||||
|
|
||||||
|
QueryStatManager.getInstance().add(queryStat);
|
||||||
|
|
||||||
return aggregateOutput;
|
return aggregateOutput;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,14 @@ package com.kingsrook.qqq.backend.core.actions.tables;
|
|||||||
|
|
||||||
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatManager;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||||
|
|
||||||
@ -43,11 +48,20 @@ public class CountAction
|
|||||||
{
|
{
|
||||||
ActionHelper.validateSession(countInput);
|
ActionHelper.validateSession(countInput);
|
||||||
|
|
||||||
|
QTableMetaData table = countInput.getTable();
|
||||||
|
QBackendMetaData backend = countInput.getBackend();
|
||||||
|
|
||||||
|
QueryStat queryStat = QueryStatManager.newQueryStat(backend, table, countInput.getFilter());
|
||||||
|
|
||||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(countInput.getBackend());
|
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(countInput.getBackend());
|
||||||
// todo pre-customization - just get to modify the request?
|
|
||||||
CountOutput countOutput = qModule.getCountInterface().execute(countInput);
|
CountInterface countInterface = qModule.getCountInterface();
|
||||||
// todo post-customization - can do whatever w/ the result if you want
|
countInterface.setQueryStat(queryStat);
|
||||||
|
CountOutput countOutput = countInterface.execute(countInput);
|
||||||
|
|
||||||
|
QueryStatManager.getInstance().add(queryStat);
|
||||||
|
|
||||||
return countOutput;
|
return countOutput;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,10 @@ import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
|||||||
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostQueryCustomizer;
|
import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostQueryCustomizer;
|
||||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||||
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
|
import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
|
||||||
import com.kingsrook.qqq.backend.core.actions.reporting.BufferedRecordPipe;
|
import com.kingsrook.qqq.backend.core.actions.reporting.BufferedRecordPipe;
|
||||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipeBufferedWrapper;
|
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipeBufferedWrapper;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatManager;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QPossibleValueTranslator;
|
import com.kingsrook.qqq.backend.core.actions.values.QPossibleValueTranslator;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
@ -47,12 +49,14 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleDispatcher;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
@ -87,12 +91,14 @@ public class QueryAction
|
|||||||
throw (new QException("Table name was not specified in query input"));
|
throw (new QException("Table name was not specified in query input"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(queryInput.getTable() == null)
|
QTableMetaData table = queryInput.getTable();
|
||||||
|
if(table == null)
|
||||||
{
|
{
|
||||||
throw (new QException("A table named [" + queryInput.getTableName() + "] was not found in the active QInstance"));
|
throw (new QException("A table named [" + queryInput.getTableName() + "] was not found in the active QInstance"));
|
||||||
}
|
}
|
||||||
|
|
||||||
postQueryRecordCustomizer = QCodeLoader.getTableCustomizer(AbstractPostQueryCustomizer.class, queryInput.getTable(), TableCustomizers.POST_QUERY_RECORD.getRole());
|
QBackendMetaData backend = queryInput.getBackend();
|
||||||
|
postQueryRecordCustomizer = QCodeLoader.getTableCustomizer(AbstractPostQueryCustomizer.class, table, TableCustomizers.POST_QUERY_RECORD.getRole());
|
||||||
this.queryInput = queryInput;
|
this.queryInput = queryInput;
|
||||||
|
|
||||||
if(queryInput.getRecordPipe() != null)
|
if(queryInput.getRecordPipe() != null)
|
||||||
@ -109,11 +115,16 @@ public class QueryAction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryStat queryStat = QueryStatManager.newQueryStat(backend, table, queryInput.getFilter());
|
||||||
|
|
||||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(queryInput.getBackend());
|
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(backend);
|
||||||
// todo pre-customization - just get to modify the request?
|
|
||||||
QueryOutput queryOutput = qModule.getQueryInterface().execute(queryInput);
|
QueryInterface queryInterface = qModule.getQueryInterface();
|
||||||
// todo post-customization - can do whatever w/ the result if you want
|
queryInterface.setQueryStat(queryStat);
|
||||||
|
QueryOutput queryOutput = queryInterface.execute(queryInput);
|
||||||
|
|
||||||
|
QueryStatManager.getInstance().add(queryStat);
|
||||||
|
|
||||||
if(queryInput.getRecordPipe() instanceof BufferedRecordPipe bufferedRecordPipe)
|
if(queryInput.getRecordPipe() instanceof BufferedRecordPipe bufferedRecordPipe)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,640 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. 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.actions.tables.helpers;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
||||||
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStatCriteriaField;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStatJoinTable;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStatOrderByField;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||||
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Singleton, which starts a thread, to store query stats into a table.
|
||||||
|
**
|
||||||
|
** Supports these systemProperties or ENV_VARS:
|
||||||
|
** qqq.queryStatManager.enabled / QQQ_QUERY_STAT_MANAGER_ENABLED
|
||||||
|
** qqq.queryStatManager.minMillisToStore / QQQ_QUERY_STAT_MANAGER_MIN_MILLIS_TO_STORE
|
||||||
|
** qqq.queryStatManager.jobPeriodSeconds / QQQ_QUERY_STAT_MANAGER_JOB_PERIOD_SECONDS
|
||||||
|
** qqq.queryStatManager.jobInitialDelay / QQQ_QUERY_STAT_MANAGER_JOB_INITIAL_DELAY
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QueryStatManager
|
||||||
|
{
|
||||||
|
private static final QLogger LOG = QLogger.getLogger(QueryStatManager.class);
|
||||||
|
|
||||||
|
private static QueryStatManager queryStatManager = null;
|
||||||
|
|
||||||
|
// todo - support multiple qInstances?
|
||||||
|
private QInstance qInstance;
|
||||||
|
private Supplier<QSession> sessionSupplier;
|
||||||
|
|
||||||
|
private boolean active = false;
|
||||||
|
private List<QueryStat> queryStats = new ArrayList<>();
|
||||||
|
|
||||||
|
private ScheduledExecutorService executorService;
|
||||||
|
|
||||||
|
private int jobPeriodSeconds = 60;
|
||||||
|
private int jobInitialDelay = 60;
|
||||||
|
private int minMillisToStore = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Singleton constructor
|
||||||
|
*******************************************************************************/
|
||||||
|
private QueryStatManager()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Singleton accessor
|
||||||
|
*******************************************************************************/
|
||||||
|
public static QueryStatManager getInstance()
|
||||||
|
{
|
||||||
|
if(queryStatManager == null)
|
||||||
|
{
|
||||||
|
queryStatManager = new QueryStatManager();
|
||||||
|
|
||||||
|
QMetaDataVariableInterpreter interpreter = new QMetaDataVariableInterpreter();
|
||||||
|
|
||||||
|
Integer propertyMinMillisToStore = interpreter.getIntegerFromPropertyOrEnvironment("qqq.queryStatManager.minMillisToStore", "QQQ_QUERY_STAT_MANAGER_MIN_MILLIS_TO_STORE", null);
|
||||||
|
if(propertyMinMillisToStore != null)
|
||||||
|
{
|
||||||
|
queryStatManager.setMinMillisToStore(propertyMinMillisToStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer propertyJobPeriodSeconds = interpreter.getIntegerFromPropertyOrEnvironment("qqq.queryStatManager.jobPeriodSeconds", "QQQ_QUERY_STAT_MANAGER_JOB_PERIOD_SECONDS", null);
|
||||||
|
if(propertyJobPeriodSeconds != null)
|
||||||
|
{
|
||||||
|
queryStatManager.setJobPeriodSeconds(propertyJobPeriodSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer propertyJobInitialDelay = interpreter.getIntegerFromPropertyOrEnvironment("qqq.queryStatManager.jobInitialDelay", "QQQ_QUERY_STAT_MANAGER_JOB_INITIAL_DELAY", null);
|
||||||
|
if(propertyJobInitialDelay != null)
|
||||||
|
{
|
||||||
|
queryStatManager.setJobInitialDelay(propertyJobInitialDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return (queryStatManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static QueryStat newQueryStat(QBackendMetaData backend, QTableMetaData table, QQueryFilter filter)
|
||||||
|
{
|
||||||
|
QueryStat queryStat = null;
|
||||||
|
|
||||||
|
if(table.isCapabilityEnabled(backend, Capability.QUERY_STATS))
|
||||||
|
{
|
||||||
|
queryStat = new QueryStat();
|
||||||
|
queryStat.setTableName(table.getName());
|
||||||
|
queryStat.setQueryFilter(Objects.requireNonNullElse(filter, new QQueryFilter()));
|
||||||
|
queryStat.setStartTimestamp(Instant.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (queryStat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void start(QInstance qInstance, Supplier<QSession> sessionSupplier)
|
||||||
|
{
|
||||||
|
if(!isEnabled())
|
||||||
|
{
|
||||||
|
LOG.info("Not starting QueryStatManager per settings.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("Starting QueryStatManager");
|
||||||
|
|
||||||
|
this.qInstance = qInstance;
|
||||||
|
this.sessionSupplier = sessionSupplier;
|
||||||
|
|
||||||
|
active = true;
|
||||||
|
queryStats = new ArrayList<>();
|
||||||
|
|
||||||
|
executorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
executorService.scheduleAtFixedRate(new QueryStatManagerInsertJob(), jobInitialDelay, jobPeriodSeconds, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static boolean isEnabled()
|
||||||
|
{
|
||||||
|
return new QMetaDataVariableInterpreter().getBooleanFromPropertyOrEnvironment("qqq.queryStatManager.enabled", "QQQ_QUERY_STAT_MANAGER_ENABLED", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void stop()
|
||||||
|
{
|
||||||
|
active = false;
|
||||||
|
queryStats.clear();
|
||||||
|
|
||||||
|
if(executorService != null)
|
||||||
|
{
|
||||||
|
executorService.shutdown();
|
||||||
|
executorService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void add(QueryStat queryStat)
|
||||||
|
{
|
||||||
|
if(queryStat == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(active)
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// set fields that we need to capture now (rather than when the thread to store runs) //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(queryStat.getFirstResultTimestamp() == null)
|
||||||
|
{
|
||||||
|
queryStat.setFirstResultTimestamp(Instant.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(queryStat.getStartTimestamp() != null && queryStat.getFirstResultTimestamp() != null && queryStat.getFirstResultMillis() == null)
|
||||||
|
{
|
||||||
|
long millis = queryStat.getFirstResultTimestamp().toEpochMilli() - queryStat.getStartTimestamp().toEpochMilli();
|
||||||
|
queryStat.setFirstResultMillis((int) millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(queryStat.getFirstResultMillis() != null && queryStat.getFirstResultMillis() < minMillisToStore)
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
// discard this record if it's under the min millis setting //
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(queryStat.getSessionId() == null && QContext.getQSession() != null)
|
||||||
|
{
|
||||||
|
queryStat.setSessionId(QContext.getQSession().getUuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(queryStat.getAction() == null)
|
||||||
|
{
|
||||||
|
if(!QContext.getActionStack().isEmpty())
|
||||||
|
{
|
||||||
|
queryStat.setAction(QContext.getActionStack().peek().getActionIdentity());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boolean expected = false;
|
||||||
|
Exception e = new Exception("Unexpected empty action stack");
|
||||||
|
for(StackTraceElement stackTraceElement : e.getStackTrace())
|
||||||
|
{
|
||||||
|
String className = stackTraceElement.getClassName();
|
||||||
|
if(className.contains(QueryStatManagerInsertJob.class.getName()))
|
||||||
|
{
|
||||||
|
expected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!expected)
|
||||||
|
{
|
||||||
|
LOG.debug(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(this)
|
||||||
|
{
|
||||||
|
queryStats.add(queryStat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private List<QueryStat> getListAndReset()
|
||||||
|
{
|
||||||
|
if(queryStats.isEmpty())
|
||||||
|
{
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(this)
|
||||||
|
{
|
||||||
|
List<QueryStat> returnList = queryStats;
|
||||||
|
queryStats = new ArrayList<>();
|
||||||
|
return (returnList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** force stats to be stored right now (rather than letting the scheduled job do it)
|
||||||
|
*******************************************************************************/
|
||||||
|
public void storeStatsNow()
|
||||||
|
{
|
||||||
|
new QueryStatManagerInsertJob().run();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Runnable that gets scheduled to periodically reset and store the list of collected stats
|
||||||
|
*******************************************************************************/
|
||||||
|
private static class QueryStatManagerInsertJob implements Runnable
|
||||||
|
{
|
||||||
|
private static final QLogger LOG = QLogger.getLogger(QueryStatManagerInsertJob.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QContext.init(getInstance().qInstance, getInstance().sessionSupplier.get());
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// every time we re-run, check if we've been turned off - if so, stop the service. //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(!isEnabled())
|
||||||
|
{
|
||||||
|
LOG.info("Stopping QueryStatManager.");
|
||||||
|
getInstance().stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<QueryStat> list = getInstance().getListAndReset();
|
||||||
|
|
||||||
|
LOG.info(logPair("queryStatListSize", list.size()));
|
||||||
|
|
||||||
|
if(list.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
// prime the entities for storing //
|
||||||
|
////////////////////////////////////
|
||||||
|
List<QRecord> queryStatQRecordsToInsert = new ArrayList<>();
|
||||||
|
for(QueryStat queryStat : list)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//////////////////////
|
||||||
|
// set the table id //
|
||||||
|
//////////////////////
|
||||||
|
Integer qqqTableId = getQQQTableId(queryStat.getTableName());
|
||||||
|
queryStat.setQqqTableId(qqqTableId);
|
||||||
|
|
||||||
|
//////////////////////////////
|
||||||
|
// build join-table records //
|
||||||
|
//////////////////////////////
|
||||||
|
if(CollectionUtils.nullSafeHasContents(queryStat.getJoinTableNames()))
|
||||||
|
{
|
||||||
|
List<QueryStatJoinTable> queryStatJoinTableList = new ArrayList<>();
|
||||||
|
for(String joinTableName : queryStat.getJoinTableNames())
|
||||||
|
{
|
||||||
|
queryStatJoinTableList.add(new QueryStatJoinTable().withQqqTableId(getQQQTableId(joinTableName)));
|
||||||
|
}
|
||||||
|
queryStat.setQueryStatJoinTableList(queryStatJoinTableList);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// build criteria records //
|
||||||
|
////////////////////////////
|
||||||
|
if(queryStat.getQueryFilter() != null && queryStat.getQueryFilter().hasAnyCriteria())
|
||||||
|
{
|
||||||
|
List<QueryStatCriteriaField> queryStatCriteriaFieldList = new ArrayList<>();
|
||||||
|
processCriteriaFromFilter(qqqTableId, queryStatCriteriaFieldList, queryStat.getQueryFilter());
|
||||||
|
queryStat.setQueryStatCriteriaFieldList(queryStatCriteriaFieldList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CollectionUtils.nullSafeHasContents(queryStat.getQueryFilter().getOrderBys()))
|
||||||
|
{
|
||||||
|
List<QueryStatOrderByField> queryStatOrderByFieldList = new ArrayList<>();
|
||||||
|
processOrderByFromFilter(qqqTableId, queryStatOrderByFieldList, queryStat.getQueryFilter());
|
||||||
|
queryStat.setQueryStatOrderByFieldList(queryStatOrderByFieldList);
|
||||||
|
}
|
||||||
|
|
||||||
|
queryStatQRecordsToInsert.add(queryStat.toQRecord());
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
//////////////////////
|
||||||
|
// skip this record //
|
||||||
|
//////////////////////
|
||||||
|
LOG.warn("Error priming a query stat for storing", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InsertInput insertInput = new InsertInput();
|
||||||
|
insertInput.setTableName(QueryStat.TABLE_NAME);
|
||||||
|
insertInput.setRecords(queryStatQRecordsToInsert);
|
||||||
|
new InsertAction().execute(insertInput);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.error("Error inserting query stats", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn("Error storing query stats", e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
QContext.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static void processCriteriaFromFilter(Integer qqqTableId, List<QueryStatCriteriaField> queryStatCriteriaFieldList, QQueryFilter queryFilter) throws QException
|
||||||
|
{
|
||||||
|
for(QFilterCriteria criteria : CollectionUtils.nonNullList(queryFilter.getCriteria()))
|
||||||
|
{
|
||||||
|
String fieldName = criteria.getFieldName();
|
||||||
|
QueryStatCriteriaField queryStatCriteriaField = new QueryStatCriteriaField();
|
||||||
|
queryStatCriteriaField.setOperator(String.valueOf(criteria.getOperator()));
|
||||||
|
|
||||||
|
if(criteria.getValues() != null)
|
||||||
|
{
|
||||||
|
queryStatCriteriaField.setValues(StringUtils.join(",", criteria.getValues()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fieldName.contains("."))
|
||||||
|
{
|
||||||
|
String[] parts = fieldName.split("\\.");
|
||||||
|
if(parts.length > 1)
|
||||||
|
{
|
||||||
|
queryStatCriteriaField.setQqqTableId(getQQQTableId(parts[0]));
|
||||||
|
queryStatCriteriaField.setName(parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
queryStatCriteriaField.setQqqTableId(qqqTableId);
|
||||||
|
queryStatCriteriaField.setName(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
queryStatCriteriaFieldList.add(queryStatCriteriaField);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QQueryFilter subFilter : CollectionUtils.nonNullList(queryFilter.getSubFilters()))
|
||||||
|
{
|
||||||
|
processCriteriaFromFilter(qqqTableId, queryStatCriteriaFieldList, subFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static void processOrderByFromFilter(Integer qqqTableId, List<QueryStatOrderByField> queryStatOrderByFieldList, QQueryFilter queryFilter) throws QException
|
||||||
|
{
|
||||||
|
for(QFilterOrderBy orderBy : CollectionUtils.nonNullList(queryFilter.getOrderBys()))
|
||||||
|
{
|
||||||
|
String fieldName = orderBy.getFieldName();
|
||||||
|
QueryStatOrderByField queryStatOrderByField = new QueryStatOrderByField();
|
||||||
|
|
||||||
|
if(fieldName != null)
|
||||||
|
{
|
||||||
|
if(fieldName.contains("."))
|
||||||
|
{
|
||||||
|
String[] parts = fieldName.split("\\.");
|
||||||
|
if(parts.length > 1)
|
||||||
|
{
|
||||||
|
queryStatOrderByField.setQqqTableId(getQQQTableId(parts[0]));
|
||||||
|
queryStatOrderByField.setName(parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
queryStatOrderByField.setQqqTableId(qqqTableId);
|
||||||
|
queryStatOrderByField.setName(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
queryStatOrderByFieldList.add(queryStatOrderByField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static Integer getQQQTableId(String tableName) throws QException
|
||||||
|
{
|
||||||
|
/////////////////////////////
|
||||||
|
// look in the cache table //
|
||||||
|
/////////////////////////////
|
||||||
|
GetInput getInput = new GetInput();
|
||||||
|
getInput.setTableName(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME);
|
||||||
|
getInput.setUniqueKey(MapBuilder.of("name", tableName));
|
||||||
|
GetOutput getOutput = new GetAction().execute(getInput);
|
||||||
|
|
||||||
|
////////////////////////
|
||||||
|
// upon cache miss... //
|
||||||
|
////////////////////////
|
||||||
|
if(getOutput.getRecord() == null)
|
||||||
|
{
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
// insert the record (into the table, not the cache) //
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
QTableMetaData tableMetaData = getInstance().qInstance.getTable(tableName);
|
||||||
|
InsertInput insertInput = new InsertInput();
|
||||||
|
insertInput.setTableName(QQQTable.TABLE_NAME);
|
||||||
|
insertInput.setRecords(List.of(new QRecord().withValue("name", tableName).withValue("label", tableMetaData.getLabel())));
|
||||||
|
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||||
|
|
||||||
|
///////////////////////////////////
|
||||||
|
// repeat the get from the cache //
|
||||||
|
///////////////////////////////////
|
||||||
|
getOutput = new GetAction().execute(getInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getOutput.getRecord().getValueInteger("id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for jobPeriodSeconds
|
||||||
|
*******************************************************************************/
|
||||||
|
public int getJobPeriodSeconds()
|
||||||
|
{
|
||||||
|
return (this.jobPeriodSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for jobPeriodSeconds
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setJobPeriodSeconds(int jobPeriodSeconds)
|
||||||
|
{
|
||||||
|
this.jobPeriodSeconds = jobPeriodSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for jobPeriodSeconds
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatManager withJobPeriodSeconds(int jobPeriodSeconds)
|
||||||
|
{
|
||||||
|
this.jobPeriodSeconds = jobPeriodSeconds;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for jobInitialDelay
|
||||||
|
*******************************************************************************/
|
||||||
|
public int getJobInitialDelay()
|
||||||
|
{
|
||||||
|
return (this.jobInitialDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for jobInitialDelay
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setJobInitialDelay(int jobInitialDelay)
|
||||||
|
{
|
||||||
|
this.jobInitialDelay = jobInitialDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for jobInitialDelay
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatManager withJobInitialDelay(int jobInitialDelay)
|
||||||
|
{
|
||||||
|
this.jobInitialDelay = jobInitialDelay;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for minMillisToStore
|
||||||
|
*******************************************************************************/
|
||||||
|
public int getMinMillisToStore()
|
||||||
|
{
|
||||||
|
return (this.minMillisToStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for minMillisToStore
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setMinMillisToStore(int minMillisToStore)
|
||||||
|
{
|
||||||
|
this.minMillisToStore = minMillisToStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for minMillisToStore
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatManager withMinMillisToStore(int minMillisToStore)
|
||||||
|
{
|
||||||
|
this.minMillisToStore = minMillisToStore;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -30,6 +30,7 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
import io.github.cdimascio.dotenv.Dotenv;
|
import io.github.cdimascio.dotenv.Dotenv;
|
||||||
import io.github.cdimascio.dotenv.DotenvEntry;
|
import io.github.cdimascio.dotenv.DotenvEntry;
|
||||||
@ -266,4 +267,111 @@ public class QMetaDataVariableInterpreter
|
|||||||
|
|
||||||
valueMaps.put(name, values);
|
valueMaps.put(name, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** First look for a boolean ("true" or "false") in the specified system property -
|
||||||
|
** Next look for a boolean in the specified env var name -
|
||||||
|
** Finally return the default.
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getBooleanFromPropertyOrEnvironment(String systemPropertyName, String environmentVariableName, boolean defaultIfNotSet)
|
||||||
|
{
|
||||||
|
String propertyValue = System.getProperty(systemPropertyName);
|
||||||
|
if(StringUtils.hasContent(propertyValue))
|
||||||
|
{
|
||||||
|
if("false".equalsIgnoreCase(propertyValue))
|
||||||
|
{
|
||||||
|
LOG.info("Read system property [" + systemPropertyName + "] as boolean false.");
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
else if("true".equalsIgnoreCase(propertyValue))
|
||||||
|
{
|
||||||
|
LOG.info("Read system property [" + systemPropertyName + "] as boolean true.");
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.warn("Unrecognized boolean value [" + propertyValue + "] for system property [" + systemPropertyName + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String envValue = interpret("${env." + environmentVariableName + "}");
|
||||||
|
if(StringUtils.hasContent(envValue))
|
||||||
|
{
|
||||||
|
if("false".equalsIgnoreCase(envValue))
|
||||||
|
{
|
||||||
|
LOG.info("Read env var [" + environmentVariableName + "] as boolean false.");
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
else if("true".equalsIgnoreCase(envValue))
|
||||||
|
{
|
||||||
|
LOG.info("Read env var [" + environmentVariableName + "] as boolean true.");
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.warn("Unrecognized boolean value [" + envValue + "] for env var [" + environmentVariableName + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultIfNotSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** First look for an Integer in the specified system property -
|
||||||
|
** Next look for an Integer in the specified env var name -
|
||||||
|
** Finally return the default (null allowed as default!)
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getIntegerFromPropertyOrEnvironment(String systemPropertyName, String environmentVariableName, Integer defaultIfNotSet)
|
||||||
|
{
|
||||||
|
String propertyValue = System.getProperty(systemPropertyName);
|
||||||
|
if(StringUtils.hasContent(propertyValue))
|
||||||
|
{
|
||||||
|
if(canParseAsInteger(propertyValue))
|
||||||
|
{
|
||||||
|
LOG.info("Read system property [" + systemPropertyName + "] as integer " + propertyValue);
|
||||||
|
return (Integer.parseInt(propertyValue));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.warn("Unrecognized integer value [" + propertyValue + "] for system property [" + systemPropertyName + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String envValue = interpret("${env." + environmentVariableName + "}");
|
||||||
|
if(StringUtils.hasContent(envValue))
|
||||||
|
{
|
||||||
|
if(canParseAsInteger(envValue))
|
||||||
|
{
|
||||||
|
LOG.info("Read env var [" + environmentVariableName + "] as integer " + environmentVariableName);
|
||||||
|
return (Integer.parseInt(propertyValue));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.warn("Unrecognized integer value [" + envValue + "] for env var [" + environmentVariableName + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultIfNotSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** we'd use NumberUtils.isDigits, but that doesn't allow negatives, or
|
||||||
|
** numberUtils.isParseable, but that allows decimals, so...
|
||||||
|
*******************************************************************************/
|
||||||
|
private boolean canParseAsInteger(String value)
|
||||||
|
{
|
||||||
|
if(value == null)
|
||||||
|
{
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (value.matches("^-?[0-9]+$"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,16 @@ public class AbstractActionInput
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getActionIdentity()
|
||||||
|
{
|
||||||
|
return (getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** performance instance validation (if not previously done).
|
** performance instance validation (if not previously done).
|
||||||
* // todo - verify this is happening (e.g., when context is set i guess)
|
* // todo - verify this is happening (e.g., when context is set i guess)
|
||||||
|
@ -46,6 +46,17 @@ public class AbstractTableActionInput extends AbstractActionInput
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public String getActionIdentity()
|
||||||
|
{
|
||||||
|
return (getClass().getSimpleName() + ":" + getTableName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -76,6 +76,17 @@ public class RunProcessInput extends AbstractActionInput
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public String getActionIdentity()
|
||||||
|
{
|
||||||
|
return (getClass().getSimpleName() + ":" + getProcessName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** e.g., for steps after the first step in a process, seed the data in a run
|
** e.g., for steps after the first step in a process, seed the data in a run
|
||||||
** function request from a process state.
|
** function request from a process state.
|
||||||
|
@ -50,6 +50,17 @@ public class RenderWidgetInput extends AbstractActionInput
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public String getActionIdentity()
|
||||||
|
{
|
||||||
|
return (getClass().getSimpleName() + ":" + widgetMetaData.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for widgetMetaData
|
** Getter for widgetMetaData
|
||||||
**
|
**
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. 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.model.data;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Annotation to place onto fields in a QRecordEntity, to mark them as associated
|
||||||
|
** record lists
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface QAssociation
|
||||||
|
{
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
String name();
|
||||||
|
|
||||||
|
}
|
@ -23,8 +23,12 @@ package com.kingsrook.qqq.backend.core.model.data;
|
|||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedParameterizedType;
|
||||||
|
import java.lang.reflect.AnnotatedType;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
@ -40,7 +44,9 @@ import java.util.Optional;
|
|||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||||
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -50,7 +56,8 @@ public abstract class QRecordEntity
|
|||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(QRecordEntity.class);
|
private static final QLogger LOG = QLogger.getLogger(QRecordEntity.class);
|
||||||
|
|
||||||
private static final ListingHash<Class<? extends QRecordEntity>, QRecordEntityField> fieldMapping = new ListingHash<>();
|
private static final ListingHash<Class<? extends QRecordEntity>, QRecordEntityField> fieldMapping = new ListingHash<>();
|
||||||
|
private static final ListingHash<Class<? extends QRecordEntity>, QRecordEntityAssociation> associationMapping = new ListingHash<>();
|
||||||
|
|
||||||
private Map<String, Serializable> originalRecordValues;
|
private Map<String, Serializable> originalRecordValues;
|
||||||
|
|
||||||
@ -92,7 +99,7 @@ public abstract class QRecordEntity
|
|||||||
** Build an entity of this QRecord type from a QRecord
|
** Build an entity of this QRecord type from a QRecord
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
protected <T extends QRecordEntity> void populateFromQRecord(QRecord qRecord) throws QRuntimeException
|
protected void populateFromQRecord(QRecord qRecord) throws QRuntimeException
|
||||||
{
|
{
|
||||||
populateFromQRecord(qRecord, "");
|
populateFromQRecord(qRecord, "");
|
||||||
}
|
}
|
||||||
@ -123,6 +130,42 @@ public abstract class QRecordEntity
|
|||||||
qRecordEntityField.getSetter().invoke(this, typedValue);
|
qRecordEntityField.getSetter().invoke(this, typedValue);
|
||||||
originalRecordValues.put(qRecordEntityField.getFieldName(), value);
|
originalRecordValues.put(qRecordEntityField.getFieldName(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||||
|
{
|
||||||
|
List<QRecord> associatedRecords = qRecord.getAssociatedRecords().get(qRecordEntityAssociation.getAssociationAnnotation().name());
|
||||||
|
if(associatedRecords == null)
|
||||||
|
{
|
||||||
|
qRecordEntityAssociation.getSetter().invoke(this, (Object) null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<QRecordEntity> associatedEntityList = new ArrayList<>();
|
||||||
|
for(QRecord associatedRecord : CollectionUtils.nonNullList(associatedRecords))
|
||||||
|
{
|
||||||
|
associatedEntityList.add(QRecordEntity.fromQRecord(qRecordEntityAssociation.getAssociatedType(), associatedRecord));
|
||||||
|
}
|
||||||
|
qRecordEntityAssociation.getSetter().invoke(this, associatedEntityList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||||
|
{
|
||||||
|
List<QRecord> associatedRecords = qRecord.getAssociatedRecords().get(qRecordEntityAssociation.getAssociationAnnotation().name());
|
||||||
|
if(associatedRecords == null)
|
||||||
|
{
|
||||||
|
qRecordEntityAssociation.getSetter().invoke(this, (Object) null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<QRecordEntity> associatedEntityList = new ArrayList<>();
|
||||||
|
for(QRecord associatedRecord : CollectionUtils.nonNullList(associatedRecords))
|
||||||
|
{
|
||||||
|
associatedEntityList.add(QRecordEntity.fromQRecord(qRecordEntityAssociation.getAssociatedType(), associatedRecord));
|
||||||
|
}
|
||||||
|
qRecordEntityAssociation.getSetter().invoke(this, associatedEntityList);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
@ -142,12 +185,30 @@ public abstract class QRecordEntity
|
|||||||
{
|
{
|
||||||
QRecord qRecord = new QRecord();
|
QRecord qRecord = new QRecord();
|
||||||
|
|
||||||
List<QRecordEntityField> fieldList = getFieldList(this.getClass());
|
for(QRecordEntityField qRecordEntityField : getFieldList(this.getClass()))
|
||||||
for(QRecordEntityField qRecordEntityField : fieldList)
|
|
||||||
{
|
{
|
||||||
qRecord.setValue(qRecordEntityField.getFieldName(), (Serializable) qRecordEntityField.getGetter().invoke(this));
|
qRecord.setValue(qRecordEntityField.getFieldName(), (Serializable) qRecordEntityField.getGetter().invoke(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||||
|
{
|
||||||
|
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
||||||
|
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
||||||
|
|
||||||
|
if(associatedEntities != null)
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// do this so an empty list in the entity becomes an empty list in the QRecord //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
qRecord.withAssociatedRecords(associationName, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QRecordEntity associatedEntity : CollectionUtils.nonNullList(associatedEntities))
|
||||||
|
{
|
||||||
|
qRecord.withAssociatedRecord(associationName, associatedEntity.toQRecord());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (qRecord);
|
return (qRecord);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
@ -157,7 +218,6 @@ public abstract class QRecordEntity
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -167,8 +227,7 @@ public abstract class QRecordEntity
|
|||||||
{
|
{
|
||||||
QRecord qRecord = new QRecord();
|
QRecord qRecord = new QRecord();
|
||||||
|
|
||||||
List<QRecordEntityField> fieldList = getFieldList(this.getClass());
|
for(QRecordEntityField qRecordEntityField : getFieldList(this.getClass()))
|
||||||
for(QRecordEntityField qRecordEntityField : fieldList)
|
|
||||||
{
|
{
|
||||||
Serializable thisValue = (Serializable) qRecordEntityField.getGetter().invoke(this);
|
Serializable thisValue = (Serializable) qRecordEntityField.getGetter().invoke(this);
|
||||||
Serializable originalValue = null;
|
Serializable originalValue = null;
|
||||||
@ -183,6 +242,25 @@ public abstract class QRecordEntity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||||
|
{
|
||||||
|
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
||||||
|
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
||||||
|
|
||||||
|
if(associatedEntities != null)
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// do this so an empty list in the entity becomes an empty list in the QRecord //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
qRecord.withAssociatedRecords(associationName, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QRecordEntity associatedEntity : CollectionUtils.nonNullList(associatedEntities))
|
||||||
|
{
|
||||||
|
qRecord.withAssociatedRecord(associationName, associatedEntity.toQRecord());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (qRecord);
|
return (qRecord);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
@ -211,7 +289,15 @@ public abstract class QRecordEntity
|
|||||||
{
|
{
|
||||||
String fieldName = getFieldNameFromGetter(possibleGetter);
|
String fieldName = getFieldNameFromGetter(possibleGetter);
|
||||||
Optional<QField> fieldAnnotation = getQFieldAnnotation(c, fieldName);
|
Optional<QField> fieldAnnotation = getQFieldAnnotation(c, fieldName);
|
||||||
fieldList.add(new QRecordEntityField(fieldName, possibleGetter, setter.get(), possibleGetter.getReturnType(), fieldAnnotation.orElse(null)));
|
|
||||||
|
if(fieldAnnotation.isPresent())
|
||||||
|
{
|
||||||
|
fieldList.add(new QRecordEntityField(fieldName, possibleGetter, setter.get(), possibleGetter.getReturnType(), fieldAnnotation.orElse(null)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.debug("Skipping field without @QField annotation", logPair("class", c.getSimpleName()), logPair("fieldName", fieldName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -226,15 +312,73 @@ public abstract class QRecordEntity
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static List<QRecordEntityAssociation> getAssociationList(Class<? extends QRecordEntity> c)
|
||||||
|
{
|
||||||
|
if(!associationMapping.containsKey(c))
|
||||||
|
{
|
||||||
|
List<QRecordEntityAssociation> associationList = new ArrayList<>();
|
||||||
|
for(Method possibleGetter : c.getMethods())
|
||||||
|
{
|
||||||
|
if(isGetter(possibleGetter))
|
||||||
|
{
|
||||||
|
Optional<Method> setter = getSetterForGetter(c, possibleGetter);
|
||||||
|
|
||||||
|
if(setter.isPresent())
|
||||||
|
{
|
||||||
|
String fieldName = getFieldNameFromGetter(possibleGetter);
|
||||||
|
Optional<QAssociation> associationAnnotation = getQAssociationAnnotation(c, fieldName);
|
||||||
|
|
||||||
|
if(associationAnnotation.isPresent())
|
||||||
|
{
|
||||||
|
Class<? extends QRecordEntity> listTypeParam = (Class<? extends QRecordEntity>) getListTypeParam(possibleGetter.getReturnType(), possibleGetter.getAnnotatedReturnType());
|
||||||
|
associationList.add(new QRecordEntityAssociation(fieldName, possibleGetter, setter.get(), listTypeParam, associationAnnotation.orElse(null)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.info("Getter method [" + possibleGetter.getName() + "] does not have a corresponding setter.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
associationMapping.put(c, associationList);
|
||||||
|
}
|
||||||
|
return (associationMapping.get(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static Optional<QField> getQFieldAnnotation(Class<? extends QRecordEntity> c, String fieldName)
|
public static Optional<QField> getQFieldAnnotation(Class<? extends QRecordEntity> c, String fieldName)
|
||||||
|
{
|
||||||
|
return (getAnnotationOnField(c, QField.class, fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static Optional<QAssociation> getQAssociationAnnotation(Class<? extends QRecordEntity> c, String fieldName)
|
||||||
|
{
|
||||||
|
return (getAnnotationOnField(c, QAssociation.class, fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static <A extends Annotation> Optional<A> getAnnotationOnField(Class<? extends QRecordEntity> c, Class<A> annotationClass, String fieldName)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Field field = c.getDeclaredField(fieldName);
|
Field field = c.getDeclaredField(fieldName);
|
||||||
return (Optional.ofNullable(field.getAnnotation(QField.class)));
|
return (Optional.ofNullable(field.getAnnotation(annotationClass)));
|
||||||
}
|
}
|
||||||
catch(NoSuchFieldException e)
|
catch(NoSuchFieldException e)
|
||||||
{
|
{
|
||||||
@ -269,7 +413,7 @@ public abstract class QRecordEntity
|
|||||||
{
|
{
|
||||||
if(method.getParameterTypes().length == 0 && method.getName().matches("^get[A-Z].*"))
|
if(method.getParameterTypes().length == 0 && method.getName().matches("^get[A-Z].*"))
|
||||||
{
|
{
|
||||||
if(isSupportedFieldType(method.getReturnType()))
|
if(isSupportedFieldType(method.getReturnType()) || isSupportedAssociation(method.getReturnType(), method.getAnnotatedReturnType()))
|
||||||
{
|
{
|
||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
@ -334,4 +478,41 @@ public abstract class QRecordEntity
|
|||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static boolean isSupportedAssociation(Class<?> returnType, AnnotatedType annotatedType)
|
||||||
|
{
|
||||||
|
Class<?> listTypeParam = getListTypeParam(returnType, annotatedType);
|
||||||
|
return (listTypeParam != null && QRecordEntity.class.isAssignableFrom(listTypeParam));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static Class<?> getListTypeParam(Class<?> listType, AnnotatedType annotatedType)
|
||||||
|
{
|
||||||
|
if(listType.equals(List.class))
|
||||||
|
{
|
||||||
|
if(annotatedType instanceof AnnotatedParameterizedType apt)
|
||||||
|
{
|
||||||
|
AnnotatedType[] annotatedActualTypeArguments = apt.getAnnotatedActualTypeArguments();
|
||||||
|
for(AnnotatedType annotatedActualTypeArgument : annotatedActualTypeArguments)
|
||||||
|
{
|
||||||
|
Type type = annotatedActualTypeArgument.getType();
|
||||||
|
if(type instanceof Class<?> c)
|
||||||
|
{
|
||||||
|
return (c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. 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.model.data;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Reflective information about an association in a QRecordEntity
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QRecordEntityAssociation
|
||||||
|
{
|
||||||
|
private final String fieldName;
|
||||||
|
private final Method getter;
|
||||||
|
private final Method setter;
|
||||||
|
|
||||||
|
private final Class<? extends QRecordEntity> associatedType;
|
||||||
|
|
||||||
|
private final QAssociation associationAnnotation;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor.
|
||||||
|
*******************************************************************************/
|
||||||
|
public QRecordEntityAssociation(String fieldName, Method getter, Method setter, Class<? extends QRecordEntity> associatedType, QAssociation associationAnnotation)
|
||||||
|
{
|
||||||
|
this.fieldName = fieldName;
|
||||||
|
this.getter = getter;
|
||||||
|
this.setter = setter;
|
||||||
|
this.associatedType = associatedType;
|
||||||
|
this.associationAnnotation = associationAnnotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for fieldName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getFieldName()
|
||||||
|
{
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for getter
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Method getGetter()
|
||||||
|
{
|
||||||
|
return getter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for setter
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Method getSetter()
|
||||||
|
{
|
||||||
|
return setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for associatedType
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Class<? extends QRecordEntity> getAssociatedType()
|
||||||
|
{
|
||||||
|
return associatedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for associationAnnotation
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QAssociation getAssociationAnnotation()
|
||||||
|
{
|
||||||
|
return associationAnnotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -22,7 +22,6 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.metadata;
|
package com.kingsrook.qqq.backend.core.model.metadata;
|
||||||
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
@ -59,6 +58,10 @@ public class QBackendMetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QBackendMetaData()
|
public QBackendMetaData()
|
||||||
{
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// by default, we will turn off the query stats capability on all backends //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
withoutCapability(Capability.QUERY_STATS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -199,6 +202,10 @@ public class QBackendMetaData
|
|||||||
public void setEnabledCapabilities(Set<Capability> enabledCapabilities)
|
public void setEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||||
{
|
{
|
||||||
this.enabledCapabilities = enabledCapabilities;
|
this.enabledCapabilities = enabledCapabilities;
|
||||||
|
if(this.disabledCapabilities != null)
|
||||||
|
{
|
||||||
|
this.disabledCapabilities.removeAll(enabledCapabilities);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -209,7 +216,7 @@ public class QBackendMetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QBackendMetaData withEnabledCapabilities(Set<Capability> enabledCapabilities)
|
public QBackendMetaData withEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||||
{
|
{
|
||||||
this.enabledCapabilities = enabledCapabilities;
|
setEnabledCapabilities(enabledCapabilities);
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +228,10 @@ public class QBackendMetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QBackendMetaData withCapabilities(Set<Capability> enabledCapabilities)
|
public QBackendMetaData withCapabilities(Set<Capability> enabledCapabilities)
|
||||||
{
|
{
|
||||||
this.enabledCapabilities = enabledCapabilities;
|
for(Capability enabledCapability : enabledCapabilities)
|
||||||
|
{
|
||||||
|
withCapability(enabledCapability);
|
||||||
|
}
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,6 +248,7 @@ public class QBackendMetaData
|
|||||||
this.enabledCapabilities = new HashSet<>();
|
this.enabledCapabilities = new HashSet<>();
|
||||||
}
|
}
|
||||||
this.enabledCapabilities.add(capability);
|
this.enabledCapabilities.add(capability);
|
||||||
|
this.disabledCapabilities.remove(capability);
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,11 +260,10 @@ public class QBackendMetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QBackendMetaData withCapabilities(Capability... enabledCapabilities)
|
public QBackendMetaData withCapabilities(Capability... enabledCapabilities)
|
||||||
{
|
{
|
||||||
if(this.enabledCapabilities == null)
|
for(Capability enabledCapability : enabledCapabilities)
|
||||||
{
|
{
|
||||||
this.enabledCapabilities = new HashSet<>();
|
withCapability(enabledCapability);
|
||||||
}
|
}
|
||||||
this.enabledCapabilities.addAll(Arrays.stream(enabledCapabilities).toList());
|
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,6 +287,10 @@ public class QBackendMetaData
|
|||||||
public void setDisabledCapabilities(Set<Capability> disabledCapabilities)
|
public void setDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||||
{
|
{
|
||||||
this.disabledCapabilities = disabledCapabilities;
|
this.disabledCapabilities = disabledCapabilities;
|
||||||
|
if(this.enabledCapabilities != null)
|
||||||
|
{
|
||||||
|
this.enabledCapabilities.removeAll(disabledCapabilities);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -287,7 +301,7 @@ public class QBackendMetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QBackendMetaData withDisabledCapabilities(Set<Capability> disabledCapabilities)
|
public QBackendMetaData withDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||||
{
|
{
|
||||||
this.disabledCapabilities = disabledCapabilities;
|
setDisabledCapabilities(disabledCapabilities);
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,11 +313,10 @@ public class QBackendMetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QBackendMetaData withoutCapabilities(Capability... disabledCapabilities)
|
public QBackendMetaData withoutCapabilities(Capability... disabledCapabilities)
|
||||||
{
|
{
|
||||||
if(this.disabledCapabilities == null)
|
for(Capability disabledCapability : disabledCapabilities)
|
||||||
{
|
{
|
||||||
this.disabledCapabilities = new HashSet<>();
|
withoutCapability(disabledCapability);
|
||||||
}
|
}
|
||||||
this.disabledCapabilities.addAll(Arrays.stream(disabledCapabilities).toList());
|
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +328,10 @@ public class QBackendMetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QBackendMetaData withoutCapabilities(Set<Capability> disabledCapabilities)
|
public QBackendMetaData withoutCapabilities(Set<Capability> disabledCapabilities)
|
||||||
{
|
{
|
||||||
this.disabledCapabilities = disabledCapabilities;
|
for(Capability disabledCapability : disabledCapabilities)
|
||||||
|
{
|
||||||
|
withCapability(disabledCapability);
|
||||||
|
}
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,6 +348,7 @@ public class QBackendMetaData
|
|||||||
this.disabledCapabilities = new HashSet<>();
|
this.disabledCapabilities = new HashSet<>();
|
||||||
}
|
}
|
||||||
this.disabledCapabilities.add(capability);
|
this.disabledCapabilities.add(capability);
|
||||||
|
this.enabledCapabilities.remove(capability);
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,9 +33,10 @@ public enum Capability
|
|||||||
TABLE_COUNT,
|
TABLE_COUNT,
|
||||||
TABLE_INSERT,
|
TABLE_INSERT,
|
||||||
TABLE_UPDATE,
|
TABLE_UPDATE,
|
||||||
TABLE_DELETE
|
TABLE_DELETE,
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
// keep these values in sync with Capability.ts in qqq-frontend-core //
|
// keep these values in sync with Capability.ts in qqq-frontend-core //
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
QUERY_STATS
|
||||||
}
|
}
|
||||||
|
@ -862,6 +862,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
public void setEnabledCapabilities(Set<Capability> enabledCapabilities)
|
public void setEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||||
{
|
{
|
||||||
this.enabledCapabilities = enabledCapabilities;
|
this.enabledCapabilities = enabledCapabilities;
|
||||||
|
if(this.disabledCapabilities != null)
|
||||||
|
{
|
||||||
|
this.disabledCapabilities.removeAll(enabledCapabilities);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -872,7 +876,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QTableMetaData withEnabledCapabilities(Set<Capability> enabledCapabilities)
|
public QTableMetaData withEnabledCapabilities(Set<Capability> enabledCapabilities)
|
||||||
{
|
{
|
||||||
this.enabledCapabilities = enabledCapabilities;
|
setEnabledCapabilities(enabledCapabilities);
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -884,7 +888,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QTableMetaData withCapabilities(Set<Capability> enabledCapabilities)
|
public QTableMetaData withCapabilities(Set<Capability> enabledCapabilities)
|
||||||
{
|
{
|
||||||
this.enabledCapabilities = enabledCapabilities;
|
for(Capability enabledCapability : enabledCapabilities)
|
||||||
|
{
|
||||||
|
withCapability(enabledCapability);
|
||||||
|
}
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,6 +908,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
this.enabledCapabilities = new HashSet<>();
|
this.enabledCapabilities = new HashSet<>();
|
||||||
}
|
}
|
||||||
this.enabledCapabilities.add(capability);
|
this.enabledCapabilities.add(capability);
|
||||||
|
this.disabledCapabilities.remove(capability);
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -912,11 +920,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QTableMetaData withCapabilities(Capability... enabledCapabilities)
|
public QTableMetaData withCapabilities(Capability... enabledCapabilities)
|
||||||
{
|
{
|
||||||
if(this.enabledCapabilities == null)
|
for(Capability enabledCapability : enabledCapabilities)
|
||||||
{
|
{
|
||||||
this.enabledCapabilities = new HashSet<>();
|
withCapability(enabledCapability);
|
||||||
}
|
}
|
||||||
this.enabledCapabilities.addAll(Arrays.stream(enabledCapabilities).toList());
|
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -940,6 +947,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
public void setDisabledCapabilities(Set<Capability> disabledCapabilities)
|
public void setDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||||
{
|
{
|
||||||
this.disabledCapabilities = disabledCapabilities;
|
this.disabledCapabilities = disabledCapabilities;
|
||||||
|
if(this.enabledCapabilities != null)
|
||||||
|
{
|
||||||
|
this.enabledCapabilities.removeAll(disabledCapabilities);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -950,7 +961,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QTableMetaData withDisabledCapabilities(Set<Capability> disabledCapabilities)
|
public QTableMetaData withDisabledCapabilities(Set<Capability> disabledCapabilities)
|
||||||
{
|
{
|
||||||
this.disabledCapabilities = disabledCapabilities;
|
setDisabledCapabilities(disabledCapabilities);
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -962,11 +973,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QTableMetaData withoutCapabilities(Capability... disabledCapabilities)
|
public QTableMetaData withoutCapabilities(Capability... disabledCapabilities)
|
||||||
{
|
{
|
||||||
if(this.disabledCapabilities == null)
|
for(Capability disabledCapability : disabledCapabilities)
|
||||||
{
|
{
|
||||||
this.disabledCapabilities = new HashSet<>();
|
withoutCapability(disabledCapability);
|
||||||
}
|
}
|
||||||
this.disabledCapabilities.addAll(Arrays.stream(disabledCapabilities).toList());
|
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,7 +988,10 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QTableMetaData withoutCapabilities(Set<Capability> disabledCapabilities)
|
public QTableMetaData withoutCapabilities(Set<Capability> disabledCapabilities)
|
||||||
{
|
{
|
||||||
this.disabledCapabilities = disabledCapabilities;
|
for(Capability disabledCapability : disabledCapabilities)
|
||||||
|
{
|
||||||
|
withCapability(disabledCapability);
|
||||||
|
}
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -995,6 +1008,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
this.disabledCapabilities = new HashSet<>();
|
this.disabledCapabilities = new HashSet<>();
|
||||||
}
|
}
|
||||||
this.disabledCapabilities.add(capability);
|
this.disabledCapabilities.add(capability);
|
||||||
|
this.enabledCapabilities.remove(capability);
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,537 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. 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.model.querystats;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QAssociation;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** QRecord Entity for QueryStat table
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QueryStat extends QRecordEntity
|
||||||
|
{
|
||||||
|
public static final String TABLE_NAME = "queryStat";
|
||||||
|
|
||||||
|
@QField(isEditable = false)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@QField()
|
||||||
|
private Instant startTimestamp;
|
||||||
|
|
||||||
|
@QField()
|
||||||
|
private Instant firstResultTimestamp;
|
||||||
|
|
||||||
|
@QField()
|
||||||
|
private Integer firstResultMillis;
|
||||||
|
|
||||||
|
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||||
|
private Integer qqqTableId;
|
||||||
|
|
||||||
|
@QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||||
|
private String action;
|
||||||
|
|
||||||
|
@QField(maxLength = 36, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||||
|
private String sessionId;
|
||||||
|
|
||||||
|
@QField(maxLength = 64 * 1024 - 1, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||||
|
private String queryText;
|
||||||
|
|
||||||
|
@QAssociation(name = "queryStatJoinTables")
|
||||||
|
private List<QueryStatJoinTable> queryStatJoinTableList;
|
||||||
|
|
||||||
|
@QAssociation(name = "queryStatCriteriaFields")
|
||||||
|
private List<QueryStatCriteriaField> queryStatCriteriaFieldList;
|
||||||
|
|
||||||
|
@QAssociation(name = "queryStatOrderByFields")
|
||||||
|
private List<QueryStatOrderByField> queryStatOrderByFieldList;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
// non-persistent fields - used to help build the record //
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
private String tableName;
|
||||||
|
private Set<String> joinTableNames;
|
||||||
|
private QQueryFilter queryFilter;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Default constructor
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor that takes a QRecord
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat(QRecord record)
|
||||||
|
{
|
||||||
|
populateFromQRecord(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getId()
|
||||||
|
{
|
||||||
|
return (this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for startTimestamp
|
||||||
|
*******************************************************************************/
|
||||||
|
public Instant getStartTimestamp()
|
||||||
|
{
|
||||||
|
return (this.startTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for startTimestamp
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setStartTimestamp(Instant startTimestamp)
|
||||||
|
{
|
||||||
|
this.startTimestamp = startTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for startTimestamp
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withStartTimestamp(Instant startTimestamp)
|
||||||
|
{
|
||||||
|
this.startTimestamp = startTimestamp;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for firstResultTimestamp
|
||||||
|
*******************************************************************************/
|
||||||
|
public Instant getFirstResultTimestamp()
|
||||||
|
{
|
||||||
|
return (this.firstResultTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for firstResultTimestamp
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFirstResultTimestamp(Instant firstResultTimestamp)
|
||||||
|
{
|
||||||
|
this.firstResultTimestamp = firstResultTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for firstResultTimestamp
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withFirstResultTimestamp(Instant firstResultTimestamp)
|
||||||
|
{
|
||||||
|
this.firstResultTimestamp = firstResultTimestamp;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for firstResultMillis
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getFirstResultMillis()
|
||||||
|
{
|
||||||
|
return (this.firstResultMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for firstResultMillis
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setFirstResultMillis(Integer firstResultMillis)
|
||||||
|
{
|
||||||
|
this.firstResultMillis = firstResultMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for firstResultMillis
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withFirstResultMillis(Integer firstResultMillis)
|
||||||
|
{
|
||||||
|
this.firstResultMillis = firstResultMillis;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryText
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getQueryText()
|
||||||
|
{
|
||||||
|
return (this.queryText);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryText
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryText(String queryText)
|
||||||
|
{
|
||||||
|
this.queryText = queryText;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for queryText
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withQueryText(String queryText)
|
||||||
|
{
|
||||||
|
this.queryText = queryText;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryStatJoinTableList
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<QueryStatJoinTable> getQueryStatJoinTableList()
|
||||||
|
{
|
||||||
|
return (this.queryStatJoinTableList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryStatJoinTableList
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryStatJoinTableList(List<QueryStatJoinTable> queryStatJoinTableList)
|
||||||
|
{
|
||||||
|
this.queryStatJoinTableList = queryStatJoinTableList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for queryStatJoinTableList
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withQueryStatJoinTableList(List<QueryStatJoinTable> queryStatJoinTableList)
|
||||||
|
{
|
||||||
|
this.queryStatJoinTableList = queryStatJoinTableList;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryStatCriteriaFieldList
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<QueryStatCriteriaField> getQueryStatCriteriaFieldList()
|
||||||
|
{
|
||||||
|
return (this.queryStatCriteriaFieldList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryStatCriteriaFieldList
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryStatCriteriaFieldList(List<QueryStatCriteriaField> queryStatCriteriaFieldList)
|
||||||
|
{
|
||||||
|
this.queryStatCriteriaFieldList = queryStatCriteriaFieldList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for queryStatCriteriaFieldList
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withQueryStatCriteriaFieldList(List<QueryStatCriteriaField> queryStatCriteriaFieldList)
|
||||||
|
{
|
||||||
|
this.queryStatCriteriaFieldList = queryStatCriteriaFieldList;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryStatOrderByFieldList
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<QueryStatOrderByField> getQueryStatOrderByFieldList()
|
||||||
|
{
|
||||||
|
return (this.queryStatOrderByFieldList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryStatOrderByFieldList
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryStatOrderByFieldList(List<QueryStatOrderByField> queryStatOrderByFieldList)
|
||||||
|
{
|
||||||
|
this.queryStatOrderByFieldList = queryStatOrderByFieldList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for queryStatOrderByFieldList
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withQueryStatOrderByFieldList(List<QueryStatOrderByField> queryStatOrderByFieldList)
|
||||||
|
{
|
||||||
|
this.queryStatOrderByFieldList = queryStatOrderByFieldList;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for tableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getTableName()
|
||||||
|
{
|
||||||
|
return (this.tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for tableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for tableName
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryFilter
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQueryFilter getQueryFilter()
|
||||||
|
{
|
||||||
|
return (this.queryFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryFilter
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryFilter(QQueryFilter queryFilter)
|
||||||
|
{
|
||||||
|
this.queryFilter = queryFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for queryFilter
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withQueryFilter(QQueryFilter queryFilter)
|
||||||
|
{
|
||||||
|
this.queryFilter = queryFilter;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getQqqTableId()
|
||||||
|
{
|
||||||
|
return (this.qqqTableId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQqqTableId(Integer qqqTableId)
|
||||||
|
{
|
||||||
|
this.qqqTableId = qqqTableId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withQqqTableId(Integer qqqTableId)
|
||||||
|
{
|
||||||
|
this.qqqTableId = qqqTableId;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for joinTableNames
|
||||||
|
*******************************************************************************/
|
||||||
|
public Set<String> getJoinTableNames()
|
||||||
|
{
|
||||||
|
return (this.joinTableNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for joinTableNames
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setJoinTableNames(Set<String> joinTableNames)
|
||||||
|
{
|
||||||
|
this.joinTableNames = joinTableNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for joinTableNames
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withJoinTableNames(Set<String> joinTableNames)
|
||||||
|
{
|
||||||
|
this.joinTableNames = joinTableNames;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for action
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getAction()
|
||||||
|
{
|
||||||
|
return (this.action);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for action
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setAction(String action)
|
||||||
|
{
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for action
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withAction(String action)
|
||||||
|
{
|
||||||
|
this.action = action;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for sessionId
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getSessionId()
|
||||||
|
{
|
||||||
|
return (this.sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for sessionId
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setSessionId(String sessionId)
|
||||||
|
{
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for sessionId
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat withSessionId(String sessionId)
|
||||||
|
{
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. 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.model.querystats;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** QRecord Entity for QueryStatCriteriaField table
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QueryStatCriteriaField extends QRecordEntity
|
||||||
|
{
|
||||||
|
public static final String TABLE_NAME = "queryStatCriteriaField";
|
||||||
|
|
||||||
|
@QField(isEditable = false)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@QField(possibleValueSourceName = QueryStat.TABLE_NAME)
|
||||||
|
private Integer queryStatId;
|
||||||
|
|
||||||
|
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||||
|
private Integer qqqTableId;
|
||||||
|
|
||||||
|
@QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@QField(maxLength = 30, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||||
|
private String operator;
|
||||||
|
|
||||||
|
@QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||||
|
private String values;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Default constructor
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatCriteriaField()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor that takes a QRecord
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatCriteriaField(QRecord record)
|
||||||
|
{
|
||||||
|
populateFromQRecord(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getId()
|
||||||
|
{
|
||||||
|
return (this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatCriteriaField withId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryStatId
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getQueryStatId()
|
||||||
|
{
|
||||||
|
return (this.queryStatId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryStatId
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryStatId(Integer queryStatId)
|
||||||
|
{
|
||||||
|
this.queryStatId = queryStatId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for queryStatId
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatCriteriaField withQueryStatId(Integer queryStatId)
|
||||||
|
{
|
||||||
|
this.queryStatId = queryStatId;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getQqqTableId()
|
||||||
|
{
|
||||||
|
return (this.qqqTableId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQqqTableId(Integer qqqTableId)
|
||||||
|
{
|
||||||
|
this.qqqTableId = qqqTableId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatCriteriaField withQqqTableId(Integer qqqTableId)
|
||||||
|
{
|
||||||
|
this.qqqTableId = qqqTableId;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for name
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return (this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for name
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for name
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatCriteriaField withName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for operator
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getOperator()
|
||||||
|
{
|
||||||
|
return (this.operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for operator
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setOperator(String operator)
|
||||||
|
{
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for operator
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatCriteriaField withOperator(String operator)
|
||||||
|
{
|
||||||
|
this.operator = operator;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for values
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getValues()
|
||||||
|
{
|
||||||
|
return (this.values);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for values
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setValues(String values)
|
||||||
|
{
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for values
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatCriteriaField withValues(String values)
|
||||||
|
{
|
||||||
|
this.values = values;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. 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.model.querystats;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** QRecord Entity for QueryStatJoinTable table
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QueryStatJoinTable extends QRecordEntity
|
||||||
|
{
|
||||||
|
public static final String TABLE_NAME = "queryStatJoinTable"; // todo - lowercase the first letter
|
||||||
|
|
||||||
|
@QField(isEditable = false)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@QField(possibleValueSourceName = QueryStat.TABLE_NAME)
|
||||||
|
private Integer queryStatId;
|
||||||
|
|
||||||
|
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||||
|
private Integer qqqTableId;
|
||||||
|
|
||||||
|
@QField(maxLength = 10, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Default constructor
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatJoinTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor that takes a QRecord
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatJoinTable(QRecord record)
|
||||||
|
{
|
||||||
|
populateFromQRecord(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getId()
|
||||||
|
{
|
||||||
|
return (this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatJoinTable withId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryStatId
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getQueryStatId()
|
||||||
|
{
|
||||||
|
return (this.queryStatId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryStatId
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryStatId(Integer queryStatId)
|
||||||
|
{
|
||||||
|
this.queryStatId = queryStatId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for queryStatId
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatJoinTable withQueryStatId(Integer queryStatId)
|
||||||
|
{
|
||||||
|
this.queryStatId = queryStatId;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getQqqTableId()
|
||||||
|
{
|
||||||
|
return (this.qqqTableId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQqqTableId(Integer qqqTableId)
|
||||||
|
{
|
||||||
|
this.qqqTableId = qqqTableId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatJoinTable withQqqTableId(Integer qqqTableId)
|
||||||
|
{
|
||||||
|
this.qqqTableId = qqqTableId;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for type
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getType()
|
||||||
|
{
|
||||||
|
return (this.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for type
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setType(String type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for type
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatJoinTable withType(String type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. 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.model.querystats;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.ChildRecordListRenderer;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.audits.AuditLevel;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QueryStatMetaDataProvider
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void defineAll(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||||
|
{
|
||||||
|
addJoins(instance);
|
||||||
|
|
||||||
|
defineQueryStatTable(instance, backendName, backendDetailEnricher);
|
||||||
|
|
||||||
|
instance.addTable(defineStandardTable(QueryStatJoinTable.TABLE_NAME, QueryStatJoinTable.class, backendName, backendDetailEnricher));
|
||||||
|
|
||||||
|
instance.addTable(defineStandardTable(QueryStatCriteriaField.TABLE_NAME, QueryStatCriteriaField.class, backendName, backendDetailEnricher)
|
||||||
|
.withExposedJoin(new ExposedJoin().withJoinTable(QueryStat.TABLE_NAME))
|
||||||
|
);
|
||||||
|
|
||||||
|
instance.addTable(defineStandardTable(QueryStatOrderByField.TABLE_NAME, QueryStatOrderByField.class, backendName, backendDetailEnricher));
|
||||||
|
|
||||||
|
instance.addPossibleValueSource(defineQueryStatPossibleValueSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void addJoins(QInstance instance)
|
||||||
|
{
|
||||||
|
instance.addJoin(new QJoinMetaData()
|
||||||
|
.withLeftTable(QueryStat.TABLE_NAME)
|
||||||
|
.withRightTable(QueryStatJoinTable.TABLE_NAME)
|
||||||
|
.withInferredName()
|
||||||
|
.withType(JoinType.ONE_TO_MANY)
|
||||||
|
.withJoinOn(new JoinOn("id", "queryStatId")));
|
||||||
|
|
||||||
|
instance.addJoin(new QJoinMetaData()
|
||||||
|
.withLeftTable(QueryStat.TABLE_NAME)
|
||||||
|
.withRightTable(QueryStatCriteriaField.TABLE_NAME)
|
||||||
|
.withInferredName()
|
||||||
|
.withType(JoinType.ONE_TO_MANY)
|
||||||
|
.withJoinOn(new JoinOn("id", "queryStatId")));
|
||||||
|
|
||||||
|
instance.addJoin(new QJoinMetaData()
|
||||||
|
.withLeftTable(QueryStat.TABLE_NAME)
|
||||||
|
.withRightTable(QueryStatOrderByField.TABLE_NAME)
|
||||||
|
.withInferredName()
|
||||||
|
.withType(JoinType.ONE_TO_MANY)
|
||||||
|
.withJoinOn(new JoinOn("id", "queryStatId")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private QTableMetaData defineQueryStatTable(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||||
|
{
|
||||||
|
String joinTablesJoinName = QJoinMetaData.makeInferredJoinName(QueryStat.TABLE_NAME, QueryStatJoinTable.TABLE_NAME);
|
||||||
|
String criteriaFieldsJoinName = QJoinMetaData.makeInferredJoinName(QueryStat.TABLE_NAME, QueryStatCriteriaField.TABLE_NAME);
|
||||||
|
String orderByFieldsJoinName = QJoinMetaData.makeInferredJoinName(QueryStat.TABLE_NAME, QueryStatOrderByField.TABLE_NAME);
|
||||||
|
|
||||||
|
QTableMetaData table = new QTableMetaData()
|
||||||
|
.withName(QueryStat.TABLE_NAME)
|
||||||
|
.withBackendName(backendName)
|
||||||
|
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||||
|
.withRecordLabelFormat("%s")
|
||||||
|
.withRecordLabelFields("id")
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withFieldsFromEntity(QueryStat.class)
|
||||||
|
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "action", "qqqTableId", "sessionId")))
|
||||||
|
.withSection(new QFieldSection("data", new QIcon().withName("dataset"), Tier.T2, List.of("queryText", "startTimestamp", "firstResultTimestamp", "firstResultMillis")))
|
||||||
|
.withSection(new QFieldSection("joins", new QIcon().withName("merge"), Tier.T2).withWidgetName(joinTablesJoinName + "Widget"))
|
||||||
|
.withSection(new QFieldSection("criteria", new QIcon().withName("filter_alt"), Tier.T2).withWidgetName(criteriaFieldsJoinName + "Widget"))
|
||||||
|
.withSection(new QFieldSection("orderBys", new QIcon().withName("sort_by_alpha"), Tier.T2).withWidgetName(orderByFieldsJoinName + "Widget"))
|
||||||
|
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||||
|
|
||||||
|
if(backendDetailEnricher != null)
|
||||||
|
{
|
||||||
|
backendDetailEnricher.accept(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(joinTablesJoinName)).withName(joinTablesJoinName + "Widget").withLabel("Join Tables").getWidgetMetaData());
|
||||||
|
instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(criteriaFieldsJoinName)).withName(criteriaFieldsJoinName + "Widget").withLabel("Criteria Fields").getWidgetMetaData());
|
||||||
|
instance.addWidget(ChildRecordListRenderer.widgetMetaDataBuilder(instance.getJoin(orderByFieldsJoinName)).withName(orderByFieldsJoinName + "Widget").withLabel("Order by Fields").getWidgetMetaData());
|
||||||
|
|
||||||
|
table.withAssociation(new Association().withName("queryStatJoinTables").withJoinName(joinTablesJoinName).withAssociatedTableName(QueryStatJoinTable.TABLE_NAME))
|
||||||
|
.withAssociation(new Association().withName("queryStatCriteriaFields").withJoinName(criteriaFieldsJoinName).withAssociatedTableName(QueryStatCriteriaField.TABLE_NAME))
|
||||||
|
.withAssociation(new Association().withName("queryStatOrderByFields").withJoinName(orderByFieldsJoinName).withAssociatedTableName(QueryStatOrderByField.TABLE_NAME));
|
||||||
|
|
||||||
|
table.getField("queryText").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("sql")));
|
||||||
|
table.getField("firstResultMillis").withDisplayFormat(DisplayFormat.COMMAS);
|
||||||
|
|
||||||
|
instance.addTable(table);
|
||||||
|
return (table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private QTableMetaData defineStandardTable(String tableName, Class<? extends QRecordEntity> entityClass, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||||
|
{
|
||||||
|
QTableMetaData table = new QTableMetaData()
|
||||||
|
.withName(tableName)
|
||||||
|
.withBackendName(backendName)
|
||||||
|
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||||
|
.withRecordLabelFormat("%d")
|
||||||
|
.withRecordLabelFields("id")
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withFieldsFromEntity(entityClass)
|
||||||
|
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||||
|
|
||||||
|
if(backendDetailEnricher != null)
|
||||||
|
{
|
||||||
|
backendDetailEnricher.accept(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource defineQueryStatPossibleValueSource()
|
||||||
|
{
|
||||||
|
return (new QPossibleValueSource()
|
||||||
|
.withType(QPossibleValueSourceType.TABLE)
|
||||||
|
.withName(QueryStat.TABLE_NAME)
|
||||||
|
.withTableName(QueryStat.TABLE_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. 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.model.querystats;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** QRecord Entity for QueryStatOrderByField table
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QueryStatOrderByField extends QRecordEntity
|
||||||
|
{
|
||||||
|
public static final String TABLE_NAME = "queryStatOrderByField";
|
||||||
|
|
||||||
|
@QField(isEditable = false)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@QField(possibleValueSourceName = QueryStat.TABLE_NAME)
|
||||||
|
private Integer queryStatId;
|
||||||
|
|
||||||
|
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||||
|
private Integer qqqTableId;
|
||||||
|
|
||||||
|
@QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Default constructor
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatOrderByField()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor that takes a QRecord
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatOrderByField(QRecord record)
|
||||||
|
{
|
||||||
|
populateFromQRecord(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getId()
|
||||||
|
{
|
||||||
|
return (this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatOrderByField withId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryStatId
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getQueryStatId()
|
||||||
|
{
|
||||||
|
return (this.queryStatId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryStatId
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryStatId(Integer queryStatId)
|
||||||
|
{
|
||||||
|
this.queryStatId = queryStatId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for queryStatId
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatOrderByField withQueryStatId(Integer queryStatId)
|
||||||
|
{
|
||||||
|
this.queryStatId = queryStatId;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getQqqTableId()
|
||||||
|
{
|
||||||
|
return (this.qqqTableId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQqqTableId(Integer qqqTableId)
|
||||||
|
{
|
||||||
|
this.qqqTableId = qqqTableId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for qqqTableId
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatOrderByField withQqqTableId(Integer qqqTableId)
|
||||||
|
{
|
||||||
|
this.qqqTableId = qqqTableId;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for name
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return (this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for name
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for name
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStatOrderByField withName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. 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.model.tables;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** QRecord Entity for QQQTable table
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QQQTable extends QRecordEntity
|
||||||
|
{
|
||||||
|
public static final String TABLE_NAME = "qqqTable";
|
||||||
|
|
||||||
|
@QField(isEditable = false)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@QField(isEditable = false)
|
||||||
|
private Instant createDate;
|
||||||
|
|
||||||
|
@QField(isEditable = false)
|
||||||
|
private Instant modifyDate;
|
||||||
|
|
||||||
|
@QField(maxLength = 250, valueTooLongBehavior = ValueTooLongBehavior.ERROR)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@QField(maxLength = 250, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Default constructor
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQQTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor that takes a QRecord
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQQTable(QRecord record)
|
||||||
|
{
|
||||||
|
populateFromQRecord(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getId()
|
||||||
|
{
|
||||||
|
return (this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQQTable withId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for createDate
|
||||||
|
*******************************************************************************/
|
||||||
|
public Instant getCreateDate()
|
||||||
|
{
|
||||||
|
return (this.createDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for createDate
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setCreateDate(Instant createDate)
|
||||||
|
{
|
||||||
|
this.createDate = createDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for createDate
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQQTable withCreateDate(Instant createDate)
|
||||||
|
{
|
||||||
|
this.createDate = createDate;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for modifyDate
|
||||||
|
*******************************************************************************/
|
||||||
|
public Instant getModifyDate()
|
||||||
|
{
|
||||||
|
return (this.modifyDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for modifyDate
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setModifyDate(Instant modifyDate)
|
||||||
|
{
|
||||||
|
this.modifyDate = modifyDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for modifyDate
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQQTable withModifyDate(Instant modifyDate)
|
||||||
|
{
|
||||||
|
this.modifyDate = modifyDate;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for name
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return (this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for name
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for name
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQQTable withName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for label
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getLabel()
|
||||||
|
{
|
||||||
|
return (this.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for label
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setLabel(String label)
|
||||||
|
{
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for label
|
||||||
|
*******************************************************************************/
|
||||||
|
public QQQTable withLabel(String label)
|
||||||
|
{
|
||||||
|
this.label = label;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2023. 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.model.tables;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.audits.AuditLevel;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheOf;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QQQTablesMetaDataProvider
|
||||||
|
{
|
||||||
|
public static final String QQQ_TABLE_CACHE_TABLE_NAME = QQQTable.TABLE_NAME + "Cache";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void defineAll(QInstance instance, String persistentBackendName, String cacheBackendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||||
|
{
|
||||||
|
instance.addTable(defineQQQTable(persistentBackendName, backendDetailEnricher));
|
||||||
|
instance.addTable(defineQQQTableCache(cacheBackendName, backendDetailEnricher));
|
||||||
|
instance.addPossibleValueSource(defineQQQTablePossibleValueSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QTableMetaData defineQQQTable(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||||
|
{
|
||||||
|
QTableMetaData table = new QTableMetaData()
|
||||||
|
.withName(QQQTable.TABLE_NAME)
|
||||||
|
.withLabel("Table")
|
||||||
|
.withBackendName(backendName)
|
||||||
|
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||||
|
.withRecordLabelFormat("%s")
|
||||||
|
.withRecordLabelFields("label")
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withUniqueKey(new UniqueKey("name"))
|
||||||
|
.withFieldsFromEntity(QQQTable.class)
|
||||||
|
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||||
|
|
||||||
|
if(backendDetailEnricher != null)
|
||||||
|
{
|
||||||
|
backendDetailEnricher.accept(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QTableMetaData defineQQQTableCache(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||||
|
{
|
||||||
|
QTableMetaData table = new QTableMetaData()
|
||||||
|
.withName(QQQ_TABLE_CACHE_TABLE_NAME)
|
||||||
|
.withBackendName(backendName)
|
||||||
|
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||||
|
.withRecordLabelFormat("%s")
|
||||||
|
.withRecordLabelFields("label")
|
||||||
|
.withPrimaryKeyField("id")
|
||||||
|
.withUniqueKey(new UniqueKey("name"))
|
||||||
|
.withFieldsFromEntity(QQQTable.class)
|
||||||
|
.withCacheOf(new CacheOf()
|
||||||
|
.withSourceTable(QQQTable.TABLE_NAME)
|
||||||
|
.withUseCase(new CacheUseCase()
|
||||||
|
.withType(CacheUseCase.Type.UNIQUE_KEY_TO_UNIQUE_KEY)
|
||||||
|
.withCacheSourceMisses(false)
|
||||||
|
.withCacheUniqueKey(new UniqueKey("name"))
|
||||||
|
.withSourceUniqueKey(new UniqueKey("name"))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if(backendDetailEnricher != null)
|
||||||
|
{
|
||||||
|
backendDetailEnricher.accept(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (table);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource defineQQQTablePossibleValueSource()
|
||||||
|
{
|
||||||
|
return (new QPossibleValueSource()
|
||||||
|
.withType(QPossibleValueSourceType.TABLE)
|
||||||
|
.withName(QQQTable.TABLE_NAME)
|
||||||
|
.withTableName(QQQTable.TABLE_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -110,20 +110,9 @@ public class ScheduleManager
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void start()
|
public void start()
|
||||||
{
|
{
|
||||||
String propertyName = "qqq.scheduleManager.enabled";
|
if(!new QMetaDataVariableInterpreter().getBooleanFromPropertyOrEnvironment("qqq.scheduleManager.enabled", "QQQ_SCHEDULE_MANAGER_ENABLED", true))
|
||||||
String propertyValue = System.getProperty(propertyName);
|
|
||||||
if("false".equals(propertyValue))
|
|
||||||
{
|
{
|
||||||
LOG.info("Not starting ScheduleManager (per system property] [" + propertyName + "=" + propertyValue + "]).");
|
LOG.info("Not starting ScheduleManager per settings.");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMetaDataVariableInterpreter qMetaDataVariableInterpreter = new QMetaDataVariableInterpreter();
|
|
||||||
String envName = "QQQ_SCHEDULE_MANAGER_ENABLED";
|
|
||||||
String envValue = qMetaDataVariableInterpreter.interpret("${env." + envName + "}");
|
|
||||||
if("false".equals(envValue))
|
|
||||||
{
|
|
||||||
LOG.info("Not starting ScheduleManager (per environment variable] [" + envName + "=" + envValue + "]).");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,13 +28,21 @@ import java.util.List;
|
|||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncRecordPipeLoop;
|
import com.kingsrook.qqq.backend.core.actions.async.AsyncRecordPipeLoop;
|
||||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatManager;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStatMetaDataProvider;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.mock.MockQueryAction;
|
import com.kingsrook.qqq.backend.core.modules.backend.implementations.mock.MockQueryAction;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
@ -390,6 +398,59 @@ class QueryActionTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testQueryManager() throws QException
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// add tables for QueryStats, and turn them on in the memory backend, then start the query-stat manager //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
qInstance.getBackend(TestUtils.MEMORY_BACKEND_NAME).withCapability(Capability.QUERY_STATS);
|
||||||
|
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
new QueryStatMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
QueryStatManager.getInstance().start(QContext.getQInstance(), QSession::new);
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// insert some order "trees", then query them, so some stats will get recorded //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
insert2OrdersWith3Lines3LineExtrinsicsAnd4OrderExtrinsicAssociations();
|
||||||
|
QueryInput queryInput = new QueryInput();
|
||||||
|
queryInput.setTableName(TestUtils.TABLE_NAME_ORDER);
|
||||||
|
queryInput.setIncludeAssociations(true);
|
||||||
|
queryInput.setFilter(new QQueryFilter().withOrderBy(new QFilterOrderBy("id")));
|
||||||
|
QContext.pushAction(queryInput);
|
||||||
|
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// run the stat manager (so we don't have to wait for it) //
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
QueryStatManager.getInstance().storeStatsNow();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// stat manager expects to be ran in a thread, where it needs to clear context, so reset context after it //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
QContext.init(qInstance, new QSession());
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// query to see that some stats were inserted //
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
queryInput = new QueryInput();
|
||||||
|
queryInput.setTableName(QueryStat.TABLE_NAME);
|
||||||
|
QContext.pushAction(queryInput);
|
||||||
|
queryOutput = new QueryAction().execute(queryInput);
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// selecting all of those associations should have caused (at least?) 4 queries. //
|
||||||
|
// this is the most basic test here, but we'll take it. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
assertThat(queryOutput.getRecords().size()).isGreaterThanOrEqualTo(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -30,8 +30,10 @@ import org.junit.jupiter.api.AfterEach;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -224,6 +226,53 @@ class QMetaDataVariableInterpreterTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testGetBooleanFromPropertyOrEnvironment()
|
||||||
|
{
|
||||||
|
QMetaDataVariableInterpreter interpreter = new QMetaDataVariableInterpreter();
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
// if neither prop nor env is set, get back the default //
|
||||||
|
//////////////////////////////////////////////////////////
|
||||||
|
assertFalse(interpreter.getBooleanFromPropertyOrEnvironment("notSet", "NOT_SET", false));
|
||||||
|
assertTrue(interpreter.getBooleanFromPropertyOrEnvironment("notSet", "NOT_SET", true));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
// unrecognized values are same as not set //
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
System.setProperty("unrecognized", "asdf");
|
||||||
|
interpreter.setEnvironmentOverrides(Map.of("UNRECOGNIZED", "1234"));
|
||||||
|
assertFalse(interpreter.getBooleanFromPropertyOrEnvironment("unrecognized", "UNRECOGNIZED", false));
|
||||||
|
assertTrue(interpreter.getBooleanFromPropertyOrEnvironment("unrecognized", "UNRECOGNIZED", true));
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
// if only prop is set, get it //
|
||||||
|
/////////////////////////////////
|
||||||
|
assertFalse(interpreter.getBooleanFromPropertyOrEnvironment("foo.enabled", "FOO_ENABLED", false));
|
||||||
|
System.setProperty("foo.enabled", "true");
|
||||||
|
assertTrue(interpreter.getBooleanFromPropertyOrEnvironment("foo.enabled", "FOO_ENABLED", false));
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// if only env is set, get it //
|
||||||
|
////////////////////////////////
|
||||||
|
assertFalse(interpreter.getBooleanFromPropertyOrEnvironment("bar.enabled", "BAR_ENABLED", false));
|
||||||
|
interpreter.setEnvironmentOverrides(Map.of("BAR_ENABLED", "true"));
|
||||||
|
assertTrue(interpreter.getBooleanFromPropertyOrEnvironment("bar.enabled", "BAR_ENABLED", false));
|
||||||
|
|
||||||
|
///////////////////////////////////
|
||||||
|
// if both are set, get the prop //
|
||||||
|
///////////////////////////////////
|
||||||
|
System.setProperty("baz.enabled", "true");
|
||||||
|
interpreter.setEnvironmentOverrides(Map.of("BAZ_ENABLED", "false"));
|
||||||
|
assertTrue(interpreter.getBooleanFromPropertyOrEnvironment("baz.enabled", "BAZ_ENABLED", true));
|
||||||
|
assertTrue(interpreter.getBooleanFromPropertyOrEnvironment("baz.enabled", "BAZ_ENABLED", false));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -23,10 +23,14 @@ package com.kingsrook.qqq.backend.core.model.data;
|
|||||||
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.testentities.Item;
|
import com.kingsrook.qqq.backend.core.model.data.testentities.Item;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.testentities.ItemWithPrimitives;
|
import com.kingsrook.qqq.backend.core.model.data.testentities.ItemWithPrimitives;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.testentities.LineItem;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.testentities.Order;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
@ -34,6 +38,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@ -304,7 +309,103 @@ class QRecordEntityTest extends BaseTest
|
|||||||
|
|
||||||
assertEquals(QFieldType.STRING, qTableMetaData.getField("sku").getType());
|
assertEquals(QFieldType.STRING, qTableMetaData.getField("sku").getType());
|
||||||
assertEquals(QFieldType.INTEGER, qTableMetaData.getField("quantity").getType());
|
assertEquals(QFieldType.INTEGER, qTableMetaData.getField("quantity").getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testOrderWithAssociationsToQRecord() throws QException
|
||||||
|
{
|
||||||
|
Order order = new Order();
|
||||||
|
order.setOrderNo("ORD001");
|
||||||
|
order.setLineItems(List.of(
|
||||||
|
new LineItem().withSku("ABC").withQuantity(1),
|
||||||
|
new LineItem().withSku("DEF").withQuantity(2)
|
||||||
|
));
|
||||||
|
|
||||||
|
QRecord qRecord = order.toQRecord();
|
||||||
|
assertEquals("ORD001", qRecord.getValueString("orderNo"));
|
||||||
|
List<QRecord> lineItems = qRecord.getAssociatedRecords().get("lineItems");
|
||||||
|
assertNotNull(lineItems);
|
||||||
|
assertEquals(2, lineItems.size());
|
||||||
|
assertEquals("ABC", lineItems.get(0).getValueString("sku"));
|
||||||
|
assertEquals(1, lineItems.get(0).getValueInteger("quantity"));
|
||||||
|
assertEquals("DEF", lineItems.get(1).getValueString("sku"));
|
||||||
|
assertEquals(2, lineItems.get(1).getValueInteger("quantity"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testOrderWithoutAssociationsToQRecord() throws QException
|
||||||
|
{
|
||||||
|
Order order = new Order();
|
||||||
|
order.setOrderNo("ORD001");
|
||||||
|
order.setLineItems(null);
|
||||||
|
|
||||||
|
QRecord qRecord = order.toQRecord();
|
||||||
|
assertEquals("ORD001", qRecord.getValueString("orderNo"));
|
||||||
|
List<QRecord> lineItems = qRecord.getAssociatedRecords().get("lineItems");
|
||||||
|
assertNull(lineItems);
|
||||||
|
|
||||||
|
order.setLineItems(new ArrayList<>());
|
||||||
|
qRecord = order.toQRecord();
|
||||||
|
lineItems = qRecord.getAssociatedRecords().get("lineItems");
|
||||||
|
assertNotNull(lineItems);
|
||||||
|
assertEquals(0, lineItems.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testQRecordWithAssociationsToOrder() throws QException
|
||||||
|
{
|
||||||
|
QRecord qRecord = new QRecord()
|
||||||
|
.withValue("orderNo", "ORD002")
|
||||||
|
.withAssociatedRecords("lineItems", List.of(
|
||||||
|
new QRecord().withValue("sku", "AB12").withValue("quantity", 42),
|
||||||
|
new QRecord().withValue("sku", "XY89").withValue("quantity", 47)
|
||||||
|
));
|
||||||
|
|
||||||
|
Order order = qRecord.toEntity(Order.class);
|
||||||
|
assertEquals("ORD002", order.getOrderNo());
|
||||||
|
assertEquals(2, order.getLineItems().size());
|
||||||
|
assertEquals("AB12", order.getLineItems().get(0).getSku());
|
||||||
|
assertEquals(42, order.getLineItems().get(0).getQuantity());
|
||||||
|
assertEquals("XY89", order.getLineItems().get(1).getSku());
|
||||||
|
assertEquals(47, order.getLineItems().get(1).getQuantity());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testQRecordWithoutAssociationsToOrder() throws QException
|
||||||
|
{
|
||||||
|
QRecord qRecord = new QRecord().withValue("orderNo", "ORD002");
|
||||||
|
Order order = qRecord.toEntity(Order.class);
|
||||||
|
assertEquals("ORD002", order.getOrderNo());
|
||||||
|
assertNull(order.getLineItems());
|
||||||
|
|
||||||
|
qRecord.withAssociatedRecords("lineItems", null);
|
||||||
|
order = qRecord.toEntity(Order.class);
|
||||||
|
assertNull(order.getLineItems());
|
||||||
|
|
||||||
|
qRecord.withAssociatedRecords("lineItems", new ArrayList<>());
|
||||||
|
order = qRecord.toEntity(Order.class);
|
||||||
|
assertNotNull(order.getLineItems());
|
||||||
|
assertEquals(0, order.getLineItems().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -43,6 +43,7 @@ public class Item extends QRecordEntity
|
|||||||
@QField(isEditable = false, displayFormat = DisplayFormat.COMMAS)
|
@QField(isEditable = false, displayFormat = DisplayFormat.COMMAS)
|
||||||
private Integer quantity;
|
private Integer quantity;
|
||||||
|
|
||||||
|
@QField()
|
||||||
private BigDecimal price;
|
private BigDecimal price;
|
||||||
|
|
||||||
@QField(backendName = "is_featured")
|
@QField(backendName = "is_featured")
|
||||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.model.data.testentities;
|
|||||||
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
|
|
||||||
|
|
||||||
@ -31,11 +32,20 @@ import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class ItemWithPrimitives extends QRecordEntity
|
public class ItemWithPrimitives extends QRecordEntity
|
||||||
{
|
{
|
||||||
private String sku;
|
@QField()
|
||||||
private String description;
|
private String sku;
|
||||||
private int quantity;
|
|
||||||
|
@QField()
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@QField()
|
||||||
|
private int quantity;
|
||||||
|
|
||||||
|
@QField()
|
||||||
private BigDecimal price;
|
private BigDecimal price;
|
||||||
private boolean featured;
|
|
||||||
|
@QField()
|
||||||
|
private boolean featured;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. 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.model.data.testentities;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Sample of an entity that can be converted to & from a QRecord
|
||||||
|
*******************************************************************************/
|
||||||
|
public class LineItem extends QRecordEntity
|
||||||
|
{
|
||||||
|
@QField()
|
||||||
|
private String sku;
|
||||||
|
|
||||||
|
@QField()
|
||||||
|
private Integer quantity;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for sku
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getSku()
|
||||||
|
{
|
||||||
|
return (this.sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for sku
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setSku(String sku)
|
||||||
|
{
|
||||||
|
this.sku = sku;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for sku
|
||||||
|
*******************************************************************************/
|
||||||
|
public LineItem withSku(String sku)
|
||||||
|
{
|
||||||
|
this.sku = sku;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for quantity
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getQuantity()
|
||||||
|
{
|
||||||
|
return (this.quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for quantity
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQuantity(Integer quantity)
|
||||||
|
{
|
||||||
|
this.quantity = quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for quantity
|
||||||
|
*******************************************************************************/
|
||||||
|
public LineItem withQuantity(Integer quantity)
|
||||||
|
{
|
||||||
|
this.quantity = quantity;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. 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.model.data.testentities;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QAssociation;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Sample of an entity that can be converted to & from a QRecord
|
||||||
|
*******************************************************************************/
|
||||||
|
public class Order extends QRecordEntity
|
||||||
|
{
|
||||||
|
@QField()
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
@QAssociation(name = "lineItems")
|
||||||
|
private List<LineItem> lineItems;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for orderNo
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getOrderNo()
|
||||||
|
{
|
||||||
|
return (this.orderNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for orderNo
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setOrderNo(String orderNo)
|
||||||
|
{
|
||||||
|
this.orderNo = orderNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for orderNo
|
||||||
|
*******************************************************************************/
|
||||||
|
public Order withOrderNo(String orderNo)
|
||||||
|
{
|
||||||
|
this.orderNo = orderNo;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for lineItems
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<LineItem> getLineItems()
|
||||||
|
{
|
||||||
|
return (this.lineItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for lineItems
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setLineItems(List<LineItem> lineItems)
|
||||||
|
{
|
||||||
|
this.lineItems = lineItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for lineItems
|
||||||
|
*******************************************************************************/
|
||||||
|
public Order withLineItems(List<LineItem> lineItems)
|
||||||
|
{
|
||||||
|
this.lineItems = lineItems;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -69,6 +69,16 @@ class QTableMetaDataTest extends BaseTest
|
|||||||
|
|
||||||
// table:false & backend:false = false
|
// table:false & backend:false = false
|
||||||
assertFalse(new QTableMetaData().withoutCapability(capability).isCapabilityEnabled(new QBackendMetaData().withoutCapability(capability), capability));
|
assertFalse(new QTableMetaData().withoutCapability(capability).isCapabilityEnabled(new QBackendMetaData().withoutCapability(capability), capability));
|
||||||
|
|
||||||
|
// backend false, but then true = true
|
||||||
|
assertTrue(new QTableMetaData().isCapabilityEnabled(new QBackendMetaData().withoutCapability(capability).withCapability(capability), capability));
|
||||||
|
|
||||||
|
// backend true, but then false = false
|
||||||
|
assertFalse(new QTableMetaData().isCapabilityEnabled(new QBackendMetaData().withCapability(capability).withoutCapability(capability), capability));
|
||||||
|
|
||||||
|
// table true, but then false = true
|
||||||
|
assertFalse(new QTableMetaData().withCapability(capability).withoutCapability(capability).isCapabilityEnabled(new QBackendMetaData(), capability));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -28,6 +28,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
import com.kingsrook.qqq.backend.module.filesystem.TestUtils;
|
import com.kingsrook.qqq.backend.module.filesystem.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
@ -36,6 +37,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Unit test for FilesystemBackendMetaData
|
** Unit test for FilesystemBackendMetaData
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@Disabled("This concept doesn't seem right any more. We will want/need custom JSON/YAML serialization, so, let us disable this test, at least for now, and maybe permanently")
|
||||||
class FilesystemBackendMetaDataTest
|
class FilesystemBackendMetaDataTest
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ class FilesystemBackendMetaDataTest
|
|||||||
System.out.println(JsonUtils.prettyPrint(json));
|
System.out.println(JsonUtils.prettyPrint(json));
|
||||||
System.out.println(json);
|
System.out.println(json);
|
||||||
String expectToContain = """
|
String expectToContain = """
|
||||||
"local-filesystem":{"basePath":"/tmp/filesystem-tests/0","backendType":"filesystem","name":"local-filesystem","usesVariants":false}""";
|
"local-filesystem":{"disabledCapabilities":["QUERY_STATS"],"basePath":"/tmp/filesystem-tests/0","backendType":"filesystem","name":"local-filesystem","usesVariants":false}""";
|
||||||
assertTrue(json.contains(expectToContain));
|
assertTrue(json.contains(expectToContain));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
import com.kingsrook.qqq.backend.module.filesystem.TestUtils;
|
import com.kingsrook.qqq.backend.module.filesystem.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
@ -35,6 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Unit test for S3BackendMetaData
|
** Unit test for S3BackendMetaData
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@Disabled("This concept doesn't seem right any more. We will want/need custom JSON/YAML serialization, so, let us disable this test, at least for now, and maybe permanently")
|
||||||
class S3BackendMetaDataTest
|
class S3BackendMetaDataTest
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
@ -86,6 +87,8 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
|||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(AbstractRDBMSAction.class);
|
private static final QLogger LOG = QLogger.getLogger(AbstractRDBMSAction.class);
|
||||||
|
|
||||||
|
protected QueryStat queryStat;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -1037,4 +1040,47 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
|||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
protected void setSqlAndJoinsInQueryStat(CharSequence sql, JoinsContext joinsContext)
|
||||||
|
{
|
||||||
|
if(queryStat != null)
|
||||||
|
{
|
||||||
|
queryStat.setQueryText(sql.toString());
|
||||||
|
|
||||||
|
if(CollectionUtils.nullSafeHasContents(joinsContext.getQueryJoins()))
|
||||||
|
{
|
||||||
|
Set<String> joinTableNames = new HashSet<>();
|
||||||
|
for(QueryJoin queryJoin : joinsContext.getQueryJoins())
|
||||||
|
{
|
||||||
|
joinTableNames.add(queryJoin.getJoinTable());
|
||||||
|
}
|
||||||
|
queryStat.setJoinTableNames(joinTableNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for queryStat
|
||||||
|
*******************************************************************************/
|
||||||
|
public QueryStat getQueryStat()
|
||||||
|
{
|
||||||
|
return (this.queryStat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for queryStat
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setQueryStat(QueryStat queryStat)
|
||||||
|
{
|
||||||
|
this.queryStat = queryStat;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,8 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
|
|||||||
|
|
||||||
// todo sql customization - can edit sql and/or param list
|
// todo sql customization - can edit sql and/or param list
|
||||||
|
|
||||||
|
setSqlAndJoinsInQueryStat(sql, joinsContext);
|
||||||
|
|
||||||
AggregateOutput rs = new AggregateOutput();
|
AggregateOutput rs = new AggregateOutput();
|
||||||
List<AggregateResult> results = new ArrayList<>();
|
List<AggregateResult> results = new ArrayList<>();
|
||||||
rs.setResults(results);
|
rs.setResults(results);
|
||||||
@ -104,6 +106,8 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
|
|||||||
{
|
{
|
||||||
while(resultSet.next())
|
while(resultSet.next())
|
||||||
{
|
{
|
||||||
|
setQueryStatFirstResultTime();
|
||||||
|
|
||||||
AggregateResult result = new AggregateResult();
|
AggregateResult result = new AggregateResult();
|
||||||
results.add(result);
|
results.add(result);
|
||||||
|
|
||||||
|
@ -77,6 +77,8 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
|
|||||||
sql += " WHERE " + makeWhereClause(countInput.getInstance(), countInput.getSession(), table, joinsContext, filter, params);
|
sql += " WHERE " + makeWhereClause(countInput.getInstance(), countInput.getSession(), table, joinsContext, filter, params);
|
||||||
// todo sql customization - can edit sql and/or param list
|
// todo sql customization - can edit sql and/or param list
|
||||||
|
|
||||||
|
setSqlAndJoinsInQueryStat(sql, joinsContext);
|
||||||
|
|
||||||
CountOutput rs = new CountOutput();
|
CountOutput rs = new CountOutput();
|
||||||
try(Connection connection = getConnection(countInput))
|
try(Connection connection = getConnection(countInput))
|
||||||
{
|
{
|
||||||
@ -86,6 +88,8 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
|
|||||||
{
|
{
|
||||||
if(resultSet.next())
|
if(resultSet.next())
|
||||||
{
|
{
|
||||||
|
setQueryStatFirstResultTime();
|
||||||
|
|
||||||
rs.setCount(resultSet.getInt("record_count"));
|
rs.setCount(resultSet.getInt("record_count"));
|
||||||
|
|
||||||
if(BooleanUtils.isTrue(countInput.getIncludeDistinctCount()))
|
if(BooleanUtils.isTrue(countInput.getIncludeDistinctCount()))
|
||||||
|
@ -100,6 +100,8 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
|||||||
|
|
||||||
// todo sql customization - can edit sql and/or param list
|
// todo sql customization - can edit sql and/or param list
|
||||||
|
|
||||||
|
setSqlAndJoinsInQueryStat(sql, joinsContext);
|
||||||
|
|
||||||
Connection connection;
|
Connection connection;
|
||||||
boolean needToCloseConnection = false;
|
boolean needToCloseConnection = false;
|
||||||
if(queryInput.getTransaction() != null && queryInput.getTransaction() instanceof RDBMSTransaction rdbmsTransaction)
|
if(queryInput.getTransaction() != null && queryInput.getTransaction() instanceof RDBMSTransaction rdbmsTransaction)
|
||||||
@ -145,6 +147,8 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
|||||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||||
while(resultSet.next())
|
while(resultSet.next())
|
||||||
{
|
{
|
||||||
|
setQueryStatFirstResultTime();
|
||||||
|
|
||||||
QRecord record = new QRecord();
|
QRecord record = new QRecord();
|
||||||
record.setTableName(table.getName());
|
record.setTableName(table.getName());
|
||||||
LinkedHashMap<String, Serializable> values = new LinkedHashMap<>();
|
LinkedHashMap<String, Serializable> values = new LinkedHashMap<>();
|
||||||
|
@ -28,6 +28,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
|||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.BaseTest;
|
import com.kingsrook.qqq.backend.module.rdbms.BaseTest;
|
||||||
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
@ -36,6 +37,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Unit test for RDBMSBackendMetaData
|
** Unit test for RDBMSBackendMetaData
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@Disabled("This concept doesn't seem right any more. We will want/need custom JSON/YAML serialization, so, let us disable this test, at least for now, and maybe permanently")
|
||||||
class RDBMSBackendMetaDataTest extends BaseTest
|
class RDBMSBackendMetaDataTest extends BaseTest
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user