Compare commits

..

6 Commits

20 changed files with 50 additions and 809 deletions

View File

@ -1,86 +0,0 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.dashboard.widgets;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.AlertData;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.WidgetType;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
/*******************************************************************************
** Widget that can add an Alert to a process screen.
**
** In the process, you'll want values:
** - alertType - name of entry in AlertType enum (ERROR, WARNING, SUCCESS)
** - alertHtml - html to display inside the alert (other than its icon)
*******************************************************************************/
public class ProcessAlertWidget extends AbstractWidgetRenderer implements MetaDataProducerInterface<QWidgetMetaData>
{
public static final String NAME = "ProcessAlertWidget";
/*******************************************************************************
**
*******************************************************************************/
@Override
public RenderWidgetOutput render(RenderWidgetInput input) throws QException
{
AlertData.AlertType alertType = AlertData.AlertType.WARNING;
if(input.getQueryParams().containsKey("alertType"))
{
alertType = AlertData.AlertType.valueOf(input.getQueryParams().get("alertType"));
}
String html = "Warning";
if(input.getQueryParams().containsKey("alertHtml"))
{
html = input.getQueryParams().get("alertHtml");
}
return (new RenderWidgetOutput(new AlertData(alertType, html)));
}
/*******************************************************************************
**
*******************************************************************************/
@Override
public QWidgetMetaData produce(QInstance qInstance) throws QException
{
return new QWidgetMetaData()
.withType(WidgetType.ALERT.getType())
.withGridColumns(12)
.withName(NAME)
.withIsCard(false)
.withShowReloadButton(false)
.withCodeReference(new QCodeReference(getClass()));
}
}

View File

@ -384,9 +384,9 @@ public class QInstanceEnricher
process.setLabel(nameToLabel(process.getName())); process.setLabel(nameToLabel(process.getName()));
} }
for(QStepMetaData step : CollectionUtils.nonNullMap(process.getAllSteps()).values()) if(process.getStepList() != null)
{ {
enrichStep(step); process.getStepList().forEach(this::enrichStep);
} }
for(QSupplementalProcessMetaData supplementalProcessMetaData : CollectionUtils.nonNullMap(process.getSupplementalMetaData()).values()) for(QSupplementalProcessMetaData supplementalProcessMetaData : CollectionUtils.nonNullMap(process.getSupplementalMetaData()).values())

View File

@ -1,196 +0,0 @@
/*
* 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.dashboard.widgets;
import java.util.List;
/*******************************************************************************
** Model containing datastructure expected by frontend filter and columns setup widget
**
*******************************************************************************/
public class FilterAndColumnsSetupData extends QWidgetData
{
private String tableName;
private Boolean allowVariables = false;
private Boolean hideColumns = false;
private List<String> filterDefaultFieldNames;
/*******************************************************************************
**
*******************************************************************************/
public FilterAndColumnsSetupData()
{
}
/*******************************************************************************
**
*******************************************************************************/
public FilterAndColumnsSetupData(String tableName, Boolean allowVariables, Boolean hideColumns, List<String> filterDefaultFieldNames)
{
this.tableName = tableName;
this.allowVariables = allowVariables;
this.hideColumns = hideColumns;
this.filterDefaultFieldNames = filterDefaultFieldNames;
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public String getType()
{
return WidgetType.FILTER_AND_COLUMNS_SETUP.getType();
}
/*******************************************************************************
** 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 FilterAndColumnsSetupData withTableName(String tableName)
{
this.tableName = tableName;
return (this);
}
/*******************************************************************************
** Getter for hideColumns
*******************************************************************************/
public Boolean getHideColumns()
{
return (this.hideColumns);
}
/*******************************************************************************
** Setter for hideColumns
*******************************************************************************/
public void setHideColumns(Boolean hideColumns)
{
this.hideColumns = hideColumns;
}
/*******************************************************************************
** Fluent setter for hideColumns
*******************************************************************************/
public FilterAndColumnsSetupData withHideColumns(Boolean hideColumns)
{
this.hideColumns = hideColumns;
return (this);
}
/*******************************************************************************
** Getter for filterDefaultFieldNames
*******************************************************************************/
public List<String> getFilterDefaultFieldNames()
{
return (this.filterDefaultFieldNames);
}
/*******************************************************************************
** Setter for filterDefaultFieldNames
*******************************************************************************/
public void setFilterDefaultFieldNames(List<String> filterDefaultFieldNames)
{
this.filterDefaultFieldNames = filterDefaultFieldNames;
}
/*******************************************************************************
** Fluent setter for filterDefaultFieldNames
*******************************************************************************/
public FilterAndColumnsSetupData withFilterDefaultFieldNames(List<String> filterDefaultFieldNames)
{
this.filterDefaultFieldNames = filterDefaultFieldNames;
return (this);
}
/*******************************************************************************
** Getter for allowVariables
*******************************************************************************/
public Boolean getAllowVariables()
{
return (this.allowVariables);
}
/*******************************************************************************
** Setter for allowVariables
*******************************************************************************/
public void setAllowVariables(Boolean allowVariables)
{
this.allowVariables = allowVariables;
}
/*******************************************************************************
** Fluent setter for allowVariables
*******************************************************************************/
public FilterAndColumnsSetupData withAllowVariables(Boolean allowVariables)
{
this.allowVariables = allowVariables;
return (this);
}
}

View File

@ -1,45 +0,0 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.savedreports;
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.FilterAndColumnsSetupData;
/*******************************************************************************
**
*******************************************************************************/
public class SavedReportsFilterAndColumnsSetupRenderer extends AbstractWidgetRenderer
{
/*******************************************************************************
**
*******************************************************************************/
@Override
public RenderWidgetOutput render(RenderWidgetInput input) throws QException
{
return (new RenderWidgetOutput(new FilterAndColumnsSetupData(null, true, false, null)));
}
}

View File

@ -236,7 +236,7 @@ public class SavedReportsMetaDataProvider
.withLabel("Filters and Columns") .withLabel("Filters and Columns")
.withIsCard(true) .withIsCard(true)
.withType(WidgetType.FILTER_AND_COLUMNS_SETUP.getType()) .withType(WidgetType.FILTER_AND_COLUMNS_SETUP.getType())
.withCodeReference(new QCodeReference(SavedReportsFilterAndColumnsSetupRenderer.class)); .withCodeReference(new QCodeReference(DefaultWidgetRenderer.class));
} }

View File

@ -1,125 +0,0 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.session;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.security.QSecurityKeyType;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
/*******************************************************************************
** Special session, indicating that an action being executed is being done not
** on behalf of a (human or otherwise) user - but instead, is the application/
** system itself.
**
** Generally this means, escalated privileges - e.g., permission to all tables,
** processes etc, and all security keys (e.g., all-access keys).
*******************************************************************************/
public class QSystemUserSession extends QSession
{
private static final QLogger LOG = QLogger.getLogger(QSystemUserSession.class);
private static List<String> allAccessKeyNames = null;
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QSystemUserSession()
{
super();
////////////////////////////////////////////////////////
// always give system user all of the all-access keys //
////////////////////////////////////////////////////////
for(String allAccessKeyName : getAllAccessKeyNames())
{
withSecurityKeyValue(allAccessKeyName, true);
}
}
/*******************************************************************************
** System User Sessions should always have permission to all the things.
*******************************************************************************/
@Override
public boolean hasPermission(String permissionName)
{
return (true);
}
/*******************************************************************************
**
*******************************************************************************/
private List<String> getAllAccessKeyNames()
{
if(allAccessKeyNames == null)
{
QInstance qInstance = QContext.getQInstance();
if(qInstance == null)
{
LOG.warn("QInstance was not set in context when creating a QSystemUserSession and trying to prime allAccessKeyNames... This SystemUserSession will NOT have any allAccessKeys.");
return (Collections.emptyList());
}
///////////////////////////////////////////////////////////////////////////////////////
// ideally only 1 thread would do this, but, it's cheap, so don't bother locking. //
// and, if multiple get in, only the last one will assign to the field, so, s/b fine //
///////////////////////////////////////////////////////////////////////////////////////
List<String> list = new ArrayList<>();
for(QSecurityKeyType securityKeyType : qInstance.getSecurityKeyTypes().values())
{
if(StringUtils.hasContent(securityKeyType.getAllAccessKeyName()))
{
list.add(securityKeyType.getAllAccessKeyName());
}
}
LOG.info("Initialized allAccessKeyNames for SystemUserSessions as: " + list);
allAccessKeyNames = list;
}
return (allAccessKeyNames);
}
/*******************************************************************************
** Meant for use in tests - to explicitly null-out the allAccessKeyNames field.
*******************************************************************************/
static void unsetAllAccessKeyNames()
{
allAccessKeyNames = null;
}
}

View File

@ -72,7 +72,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0AuthenticationMetaData; import com.kingsrook.qqq.backend.core.model.metadata.authentication.Auth0AuthenticationMetaData;
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.session.QSession; import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
import com.kingsrook.qqq.backend.core.model.session.QUser; import com.kingsrook.qqq.backend.core.model.session.QUser;
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleCustomizerInterface; import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleCustomizerInterface;
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface; import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
@ -492,11 +491,6 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
return (true); return (true);
} }
if(session instanceof QSystemUserSession)
{
return (true);
}
if(session == null) if(session == null)
{ {
return (false); return (false);

View File

@ -51,7 +51,6 @@ 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.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.authentication.TableBasedAuthenticationMetaData; import com.kingsrook.qqq.backend.core.model.metadata.authentication.TableBasedAuthenticationMetaData;
import com.kingsrook.qqq.backend.core.model.session.QSession; import com.kingsrook.qqq.backend.core.model.session.QSession;
import com.kingsrook.qqq.backend.core.model.session.QSystemUserSession;
import com.kingsrook.qqq.backend.core.model.session.QUser; import com.kingsrook.qqq.backend.core.model.session.QUser;
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface; import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleInterface;
import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider; import com.kingsrook.qqq.backend.core.state.InMemoryStateProvider;
@ -257,11 +256,6 @@ public class TableBasedAuthenticationModule implements QAuthenticationModuleInte
return (true); return (true);
} }
if(session instanceof QSystemUserSession)
{
return (true);
}
if(session == null) if(session == null)
{ {
return (false); return (false);

View File

@ -173,14 +173,6 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe
transformStep.postRun(postRunInput, postRunOutput); transformStep.postRun(postRunInput, postRunOutput);
loadStep.postRun(postRunInput, postRunOutput); loadStep.postRun(postRunInput, postRunOutput);
//////////////////////////////////////////////////////////////////////
// propagate data from inner-step state to process-level step state //
//////////////////////////////////////////////////////////////////////
if(postRunOutput.getUpdatedFrontendStepList() != null)
{
runBackendStepOutput.setUpdatedFrontendStepList(postRunOutput.getUpdatedFrontendStepList());
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// explicitly copy values back into the runStepOutput from the post-run output // // explicitly copy values back into the runStepOutput from the post-run output //
// this might not be needed, since they (presumably) share a processState object, but just in case that changes... // // this might not be needed, since they (presumably) share a processState object, but just in case that changes... //
@ -278,15 +270,6 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe
transformStep.runOnePage(streamedBackendStepInput, streamedBackendStepOutput); transformStep.runOnePage(streamedBackendStepInput, streamedBackendStepOutput);
List<AuditInput> auditInputListFromTransform = streamedBackendStepOutput.getAuditInputList(); List<AuditInput> auditInputListFromTransform = streamedBackendStepOutput.getAuditInputList();
//////////////////////////////////////////////////////////////////////
// propagate data from inner-step state to process-level step state //
//////////////////////////////////////////////////////////////////////
if(streamedBackendStepOutput.getUpdatedFrontendStepList() != null)
{
runBackendStepOutput.getProcessState().setStepList(streamedBackendStepOutput.getProcessState().getStepList());
runBackendStepOutput.setUpdatedFrontendStepList(streamedBackendStepOutput.getUpdatedFrontendStepList());
}
//////////////////////////////////////////////// ////////////////////////////////////////////////
// pass the records through the load function // // pass the records through the load function //
//////////////////////////////////////////////// ////////////////////////////////////////////////
@ -296,15 +279,6 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe
loadStep.runOnePage(streamedBackendStepInput, streamedBackendStepOutput); loadStep.runOnePage(streamedBackendStepInput, streamedBackendStepOutput);
List<AuditInput> auditInputListFromLoad = streamedBackendStepOutput.getAuditInputList(); List<AuditInput> auditInputListFromLoad = streamedBackendStepOutput.getAuditInputList();
//////////////////////////////////////////////////////////////////////
// propagate data from inner-step state to process-level step state //
//////////////////////////////////////////////////////////////////////
if(streamedBackendStepOutput.getUpdatedFrontendStepList() != null)
{
runBackendStepOutput.getProcessState().setStepList(streamedBackendStepOutput.getProcessState().getStepList());
runBackendStepOutput.setUpdatedFrontendStepList(streamedBackendStepOutput.getUpdatedFrontendStepList());
}
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// copy a small number of records to the output list // // copy a small number of records to the output list //
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////

View File

@ -145,9 +145,7 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
BackendStepPostRunInput postRunInput = new BackendStepPostRunInput(runBackendStepInput); BackendStepPostRunInput postRunInput = new BackendStepPostRunInput(runBackendStepInput);
transformStep.postRun(postRunInput, postRunOutput); transformStep.postRun(postRunInput, postRunOutput);
////////////////////////////////////////////////////////////////////// // todo figure out what kind of test we can get on this
// propagate data from inner-step state to process-level step state //
//////////////////////////////////////////////////////////////////////
if(postRunOutput.getUpdatedFrontendStepList() != null) if(postRunOutput.getUpdatedFrontendStepList() != null)
{ {
runBackendStepOutput.setUpdatedFrontendStepList(postRunOutput.getUpdatedFrontendStepList()); runBackendStepOutput.setUpdatedFrontendStepList(postRunOutput.getUpdatedFrontendStepList());
@ -216,15 +214,6 @@ public class StreamedETLPreviewStep extends BaseStreamedETLStep implements Backe
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
transformStep.runOnePage(streamedBackendStepInput, streamedBackendStepOutput); transformStep.runOnePage(streamedBackendStepInput, streamedBackendStepOutput);
//////////////////////////////////////////////////////////////////////
// propagate data from inner-step state to process-level step state //
//////////////////////////////////////////////////////////////////////
if(streamedBackendStepOutput.getUpdatedFrontendStepList() != null)
{
runBackendStepOutput.getProcessState().setStepList(streamedBackendStepOutput.getProcessState().getStepList());
runBackendStepOutput.setUpdatedFrontendStepList(streamedBackendStepOutput.getUpdatedFrontendStepList());
}
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
// add the transformed records to the output list // // add the transformed records to the output list //
//////////////////////////////////////////////////// ////////////////////////////////////////////////////

View File

@ -142,9 +142,6 @@ public class StreamedETLValidateStep extends BaseStreamedETLStep implements Back
BackendStepPostRunInput postRunInput = new BackendStepPostRunInput(runBackendStepInput); BackendStepPostRunInput postRunInput = new BackendStepPostRunInput(runBackendStepInput);
transformStep.postRun(postRunInput, postRunOutput); transformStep.postRun(postRunInput, postRunOutput);
//////////////////////////////////////////////////////////////////////
// propagate data from inner-step state to process-level step state //
//////////////////////////////////////////////////////////////////////
if(postRunOutput.getUpdatedFrontendStepList() != null) if(postRunOutput.getUpdatedFrontendStepList() != null)
{ {
runBackendStepOutput.setUpdatedFrontendStepList(postRunOutput.getUpdatedFrontendStepList()); runBackendStepOutput.setUpdatedFrontendStepList(postRunOutput.getUpdatedFrontendStepList());
@ -180,15 +177,6 @@ public class StreamedETLValidateStep extends BaseStreamedETLStep implements Back
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
transformStep.runOnePage(streamedBackendStepInput, streamedBackendStepOutput); transformStep.runOnePage(streamedBackendStepInput, streamedBackendStepOutput);
//////////////////////////////////////////////////////////////////////
// propagate data from inner-step state to process-level step state //
//////////////////////////////////////////////////////////////////////
if(streamedBackendStepOutput.getUpdatedFrontendStepList() != null)
{
runBackendStepOutput.getProcessState().setStepList(streamedBackendStepOutput.getProcessState().getStepList());
runBackendStepOutput.setUpdatedFrontendStepList(streamedBackendStepOutput.getUpdatedFrontendStepList());
}
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////
// copy a small number of records to the output list // // copy a small number of records to the output list //
/////////////////////////////////////////////////////// ///////////////////////////////////////////////////////

View File

@ -103,13 +103,12 @@ public class ProcessLockUtils
// if inserting failed... see if we can get existing lock // // if inserting failed... see if we can get existing lock //
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
StringBuilder existingLockDetails = new StringBuilder(); StringBuilder existingLockDetails = new StringBuilder();
ProcessLock existingLock = null;
if(CollectionUtils.nullSafeHasContents(insertOutputRecord.getErrors())) if(CollectionUtils.nullSafeHasContents(insertOutputRecord.getErrors()))
{ {
QRecord existingLockRecord = new GetAction().executeForRecord(new GetInput(ProcessLock.TABLE_NAME).withUniqueKey(Map.of("key", key, "processLockTypeId", lockType.getId()))); QRecord existingLockRecord = new GetAction().executeForRecord(new GetInput(ProcessLock.TABLE_NAME).withUniqueKey(Map.of("key", key, "processLockTypeId", lockType.getId())));
if(existingLockRecord != null) if(existingLockRecord != null)
{ {
existingLock = new ProcessLock(existingLockRecord); ProcessLock existingLock = new ProcessLock(existingLockRecord);
if(StringUtils.hasContent(existingLock.getUserId())) if(StringUtils.hasContent(existingLock.getUserId()))
{ {
existingLockDetails.append("Held by: ").append(existingLock.getUserId()); existingLockDetails.append("Held by: ").append(existingLock.getUserId());
@ -154,8 +153,7 @@ public class ProcessLockUtils
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
LOG.info("Errors in process lock record after attempted insert", logPair("errors", insertOutputRecord.getErrors()), LOG.info("Errors in process lock record after attempted insert", logPair("errors", insertOutputRecord.getErrors()),
logPair("key", key), logPair("type", typeName), logPair("details", details)); logPair("key", key), logPair("type", typeName), logPair("details", details));
throw (new UnableToObtainProcessLockException("A Process Lock already exists for key [" + key + "] of type [" + typeName + "], " + existingLockDetails) throw (new UnableToObtainProcessLockException("A Process Lock already exists for key [" + key + "] of type [" + typeName + "], " + existingLockDetails));
.withExistingLock(existingLock));
} }
LOG.info("Created process lock", logPair("id", processLock.getId()), LOG.info("Created process lock", logPair("id", processLock.getId()),
@ -204,15 +202,12 @@ public class ProcessLockUtils
} }
} }
/////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
// this variable can never be null with current code-path, but prefer to be defensive regardless // // var can never be null with current code-path, but prefer defensiveness regardless. //
/////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("ConstantValue") @SuppressWarnings("ConstantValue")
String suffix = lastCaughtUnableToObtainProcessLockException == null ? "" : ": " + lastCaughtUnableToObtainProcessLockException.getMessage(); String suffix = lastCaughtUnableToObtainProcessLockException == null ? "" : ": " + lastCaughtUnableToObtainProcessLockException.getMessage();
throw (new UnableToObtainProcessLockException("Unable to obtain process lock for key [" + key + "] in type [" + type + "] after [" + maxWait + "]" + suffix));
//noinspection ConstantValue
throw (new UnableToObtainProcessLockException("Unable to obtain process lock for key [" + key + "] in type [" + type + "] after [" + maxWait + "]" + suffix)
.withExistingLock(lastCaughtUnableToObtainProcessLockException == null ? null : lastCaughtUnableToObtainProcessLockException.getExistingLock()));
} }

View File

@ -30,9 +30,6 @@ import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
*******************************************************************************/ *******************************************************************************/
public class UnableToObtainProcessLockException extends QUserFacingException public class UnableToObtainProcessLockException extends QUserFacingException
{ {
private ProcessLock existingLock;
/******************************************************************************* /*******************************************************************************
** **
@ -52,35 +49,4 @@ public class UnableToObtainProcessLockException extends QUserFacingException
super(message, cause); super(message, cause);
} }
/*******************************************************************************
** Getter for existingLock
*******************************************************************************/
public ProcessLock getExistingLock()
{
return (this.existingLock);
}
/*******************************************************************************
** Setter for existingLock
*******************************************************************************/
public void setExistingLock(ProcessLock existingLock)
{
this.existingLock = existingLock;
}
/*******************************************************************************
** Fluent setter for existingLock
*******************************************************************************/
public UnableToObtainProcessLockException withExistingLock(ProcessLock existingLock)
{
this.existingLock = existingLock;
return (this);
}
} }

View File

@ -23,18 +23,15 @@ package com.kingsrook.qqq.backend.core.scheduler.quartz.processes;
import java.util.List; import java.util.List;
import java.util.function.BiFunction;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface; import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
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.MetaDataProducerMultiOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
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;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon; import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractLoadStep; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractLoadStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
@ -46,19 +43,20 @@ import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzScheduler;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class PauseQuartzJobsProcess extends AbstractLoadStep implements MetaDataProducerInterface<MetaDataProducerMultiOutput> public class PauseQuartzJobsProcess extends AbstractLoadStep implements MetaDataProducerInterface<QProcessMetaData>
{ {
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@Override @Override
public MetaDataProducerMultiOutput produce(QInstance qInstance) throws QException public QProcessMetaData produce(QInstance qInstance) throws QException
{ {
BiFunction<String, String, QProcessMetaData> processMaker = (String tableName, String label) -> String tableName = "quartzJobDetails";
StreamedETLWithFrontendProcess.processMetaDataBuilder()
return StreamedETLWithFrontendProcess.processMetaDataBuilder()
.withName(getClass().getSimpleName()) .withName(getClass().getSimpleName())
.withLabel(label) .withLabel("Pause Quartz Jobs")
.withPreviewMessage("This is a preview of the jobs that will be paused.") .withPreviewMessage("This is a preview of the jobs that will be paused.")
.withTableName(tableName) .withTableName(tableName)
.withSourceTable(tableName) .withSourceTable(tableName)
@ -70,15 +68,8 @@ public class PauseQuartzJobsProcess extends AbstractLoadStep implements MetaData
.withReviewStepRecordFields(List.of( .withReviewStepRecordFields(List.of(
new QFieldMetaData("id", QFieldType.LONG), new QFieldMetaData("id", QFieldType.LONG),
new QFieldMetaData("jobName", QFieldType.STRING), new QFieldMetaData("jobName", QFieldType.STRING),
new QFieldMetaData("jobGroup", QFieldType.STRING), new QFieldMetaData("jobGroup", QFieldType.STRING)))
new QFieldMetaData("description", QFieldType.STRING))) .getProcessMetaData();
.getProcessMetaData()
.withPermissionRules(new QPermissionRules().withPermissionBaseName(getClass().getSimpleName()));
MetaDataProducerMultiOutput output = new MetaDataProducerMultiOutput();
output.add(processMaker.apply("quartzJobDetails", "Pause Quartz Jobs"));
output.add(processMaker.apply("quartzTriggers", "Pause Quartz Triggers").withName(getClass().getSimpleName() + "ForTriggers"));
return (output);
} }

View File

@ -23,18 +23,15 @@ package com.kingsrook.qqq.backend.core.scheduler.quartz.processes;
import java.util.List; import java.util.List;
import java.util.function.BiFunction;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface; import com.kingsrook.qqq.backend.core.model.MetaDataProducerInterface;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
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.MetaDataProducerMultiOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
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;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon; import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractLoadStep; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractLoadStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
@ -46,19 +43,20 @@ import com.kingsrook.qqq.backend.core.scheduler.quartz.QuartzScheduler;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public class ResumeQuartzJobsProcess extends AbstractLoadStep implements MetaDataProducerInterface<MetaDataProducerMultiOutput> public class ResumeQuartzJobsProcess extends AbstractLoadStep implements MetaDataProducerInterface<QProcessMetaData>
{ {
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@Override @Override
public MetaDataProducerMultiOutput produce(QInstance qInstance) throws QException public QProcessMetaData produce(QInstance qInstance) throws QException
{ {
BiFunction<String, String, QProcessMetaData> processMaker = (String tableName, String label) -> String tableName = "quartzJobDetails";
StreamedETLWithFrontendProcess.processMetaDataBuilder()
return StreamedETLWithFrontendProcess.processMetaDataBuilder()
.withName(getClass().getSimpleName()) .withName(getClass().getSimpleName())
.withLabel(label) .withLabel("Resume Quartz Jobs")
.withPreviewMessage("This is a preview of the jobs that will be resumed.") .withPreviewMessage("This is a preview of the jobs that will be resumed.")
.withTableName(tableName) .withTableName(tableName)
.withSourceTable(tableName) .withSourceTable(tableName)
@ -70,15 +68,8 @@ public class ResumeQuartzJobsProcess extends AbstractLoadStep implements MetaDat
.withReviewStepRecordFields(List.of( .withReviewStepRecordFields(List.of(
new QFieldMetaData("id", QFieldType.LONG), new QFieldMetaData("id", QFieldType.LONG),
new QFieldMetaData("jobName", QFieldType.STRING), new QFieldMetaData("jobName", QFieldType.STRING),
new QFieldMetaData("jobGroup", QFieldType.STRING), new QFieldMetaData("jobGroup", QFieldType.STRING)))
new QFieldMetaData("description", QFieldType.STRING))) .getProcessMetaData();
.getProcessMetaData()
.withPermissionRules(new QPermissionRules().withPermissionBaseName(getClass().getSimpleName()));
MetaDataProducerMultiOutput output = new MetaDataProducerMultiOutput();
output.add(processMaker.apply("quartzJobDetails", "Resume Quartz Jobs"));
output.add(processMaker.apply("quartzTriggers", "Resume Quartz Triggers").withName(getClass().getSimpleName() + "ForTriggers"));
return (output);
} }

View File

@ -1,72 +0,0 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.actions.dashboard.widgets;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.actions.dashboard.RenderWidgetAction;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.AlertData;
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerHelper;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*******************************************************************************
** Unit test for ProcessAlertWidget
*******************************************************************************/
class ProcessAlertWidgetTest extends BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test() throws QException
{
MetaDataProducerHelper.processAllMetaDataProducersInPackage(QContext.getQInstance(), ProcessAlertWidget.class.getPackageName());
RenderWidgetInput input = new RenderWidgetInput();
input.setWidgetMetaData(QContext.getQInstance().getWidget(ProcessAlertWidget.NAME));
///////////////////////////////////////////////////////////////////////////////////////////
// make sure we run w/o exceptions (and w/ default outputs) if there are no query params //
///////////////////////////////////////////////////////////////////////////////////////////
RenderWidgetOutput output = new RenderWidgetAction().execute(input);
assertEquals(AlertData.AlertType.WARNING, ((AlertData) output.getWidgetData()).getAlertType());
assertEquals("Warning", ((AlertData) output.getWidgetData()).getHtml());
//////////////////////////////////////////////////////
// make sure we input params come through to output //
//////////////////////////////////////////////////////
input.addQueryParam("alertType", "ERROR");
input.addQueryParam("alertHtml", "Do not touch Willy");
output = new RenderWidgetAction().execute(input);
assertEquals(AlertData.AlertType.ERROR, ((AlertData) output.getWidgetData()).getAlertType());
assertEquals("Do not touch Willy", ((AlertData) output.getWidgetData()).getHtml());
}
}

View File

@ -28,7 +28,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
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.DynamicDefaultValueBehavior; import com.kingsrook.qqq.backend.core.model.metadata.fields.DynamicDefaultValueBehavior;
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment; import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
@ -39,8 +38,6 @@ 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.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin; 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.QFieldSection;
@ -235,7 +232,6 @@ class QInstanceEnricherTest extends BaseTest
} }
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -550,26 +546,4 @@ class QInstanceEnricherTest extends BaseTest
assertEquals(DynamicDefaultValueBehavior.MODIFY_DATE, table.getField("modifyDate").getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class)); assertEquals(DynamicDefaultValueBehavior.MODIFY_DATE, table.getField("modifyDate").getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class));
} }
/*******************************************************************************
**
*******************************************************************************/
@Test
void testOptionalProcessSteps()
{
QInstance qInstance = TestUtils.defineInstance();
QProcessMetaData process = new QProcessMetaData();
process.setName("test");
process.withStepList(List.of(new QBackendStepMetaData().withName("execute").withCode(new QCodeReference(TestUtils.IncreaseBirthdateStep.class))));
process.addOptionalStep(new QFrontendStepMetaData()
.withName("screen")
.withViewField(new QFieldMetaData("myField", QFieldType.STRING)));
qInstance.addProcess(process);
new QInstanceEnricher(qInstance).enrich();
assertEquals("My Field", qInstance.getProcess("test").getFrontendStep("screen").getViewFields().get(0).getLabel());
}
} }

View File

@ -1,88 +0,0 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2024. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.session;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import com.kingsrook.qqq.backend.core.BaseTest;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.Auth0AuthenticationModule;
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.FullyAnonymousAuthenticationModule;
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.MockAuthenticationModule;
import com.kingsrook.qqq.backend.core.modules.authentication.implementations.TableBasedAuthenticationModule;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
** Unit test for QSystemUserSession
*******************************************************************************/
class QSystemUserSessionTest extends BaseTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
void test()
{
QSystemUserSession systemUserSession = new QSystemUserSession();
assertEquals(List.of(true), systemUserSession.getSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS));
assertTrue(new Auth0AuthenticationModule().isSessionValid(QContext.getQInstance(), systemUserSession));
assertTrue(new TableBasedAuthenticationModule().isSessionValid(QContext.getQInstance(), systemUserSession));
assertTrue(new MockAuthenticationModule().isSessionValid(QContext.getQInstance(), systemUserSession));
assertTrue(new FullyAnonymousAuthenticationModule().isSessionValid(QContext.getQInstance(), systemUserSession));
assertTrue(systemUserSession.hasPermission(null));
assertTrue(systemUserSession.hasPermission(""));
assertTrue(systemUserSession.hasPermission("anything"));
assertTrue(systemUserSession.hasPermission(UUID.randomUUID().toString()));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testWeDoNotBlowUpIfInstanceIsntInContextWhenPrimingAllAccessKeyNames()
{
QInstance qInstance = QContext.getQInstance();
QSystemUserSession.unsetAllAccessKeyNames();
QContext.clear();
QSystemUserSession systemUserSession = new QSystemUserSession();
assertEquals(Collections.emptyList(), systemUserSession.getSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS));
QContext.setQInstance(qInstance);
systemUserSession = new QSystemUserSession();
assertEquals(List.of(true), systemUserSession.getSecurityKeyValues(TestUtils.SECURITY_KEY_TYPE_STORE_ALL_ACCESS));
}
}

View File

@ -109,8 +109,7 @@ class ProcessLockUtilsTest extends BaseTest
.isInstanceOf(UnableToObtainProcessLockException.class) .isInstanceOf(UnableToObtainProcessLockException.class)
.hasMessageContaining("Held by: " + QContext.getQSession().getUser().getIdReference()) .hasMessageContaining("Held by: " + QContext.getQSession().getUser().getIdReference())
.hasMessageContaining("with details: me") .hasMessageContaining("with details: me")
.hasMessageNotContaining("expiring at: 20") .hasMessageNotContaining("expiring at: 20");
.matches(e -> ((UnableToObtainProcessLockException) e).getExistingLock() != null);
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
// make sure we can create another for a different key // // make sure we can create another for a different key //
@ -180,8 +179,7 @@ class ProcessLockUtilsTest extends BaseTest
.isInstanceOf(UnableToObtainProcessLockException.class) .isInstanceOf(UnableToObtainProcessLockException.class)
.hasMessageContaining("Held by: " + QContext.getQSession().getUser().getIdReference()) .hasMessageContaining("Held by: " + QContext.getQSession().getUser().getIdReference())
.hasMessageContaining("with details: me") .hasMessageContaining("with details: me")
.hasMessageContaining("expiring at: 20") .hasMessageContaining("expiring at: 20");
.matches(e -> ((UnableToObtainProcessLockException) e).getExistingLock() != null);
} }

View File

@ -9,4 +9,3 @@ qqq-middleware-picocli
qqq-middleware-slack qqq-middleware-slack
qqq-middleware-api qqq-middleware-api
qqq-frontend-material-dashboard qqq-frontend-material-dashboard
qqq-bom-pom