Update to avoid stack-overflow if validation causes validation to happen again

This commit is contained in:
2024-06-19 16:49:04 -05:00
parent 564a5e1095
commit fcae58168e
3 changed files with 85 additions and 13 deletions

View File

@ -0,0 +1,34 @@
/*
* 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.instances;
/*******************************************************************************
** Object used to record state of a QInstance having been validated.
**
*******************************************************************************/
public enum QInstanceValidationState
{
PENDING,
RUNNING,
COMPLETE
}

View File

@ -139,14 +139,20 @@ public class QInstanceValidator
*******************************************************************************/ *******************************************************************************/
public void validate(QInstance qInstance) throws QInstanceValidationException public void validate(QInstance qInstance) throws QInstanceValidationException
{ {
if(qInstance.getHasBeenValidated()) if(qInstance.getHasBeenValidated() || qInstance.getValidationIsRunning())
{ {
////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
// don't re-validate if previously done // // don't re-validate if previously complete or currently running (avoids recursive re-validation chaos!) //
////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
return; return;
} }
////////////////////////////////////
// mark validation as running now //
////////////////////////////////////
QInstanceValidationKey validationKey = new QInstanceValidationKey();
qInstance.setValidationIsRunning(validationKey);
///////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////
// the enricher will build a join graph (if there are any joins). we'd like to only do that // // the enricher will build a join graph (if there are any joins). we'd like to only do that //
// once, during the enrichment/validation work, so, capture it, and store it back in the instance. // // once, during the enrichment/validation work, so, capture it, and store it back in the instance. //
@ -207,9 +213,11 @@ public class QInstanceValidator
throw (new QInstanceValidationException(errors)); throw (new QInstanceValidationException(errors));
} }
QInstanceValidationKey validationKey = new QInstanceValidationKey(); //////////////////////////////
qInstance.setHasBeenValidated(validationKey); // mark validation complete //
//////////////////////////////
qInstance.setJoinGraph(validationKey, joinGraph); qInstance.setJoinGraph(validationKey, joinGraph);
qInstance.setHasBeenValidated(validationKey);
} }

View File

@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.instances.QInstanceHelpContentManager; import com.kingsrook.qqq.backend.core.instances.QInstanceHelpContentManager;
import com.kingsrook.qqq.backend.core.instances.QInstanceValidationKey; import com.kingsrook.qqq.backend.core.instances.QInstanceValidationKey;
import com.kingsrook.qqq.backend.core.instances.QInstanceValidationState;
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput; import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput; import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
@ -112,10 +113,13 @@ public class QInstance
private QPermissionRules defaultPermissionRules = QPermissionRules.defaultInstance(); private QPermissionRules defaultPermissionRules = QPermissionRules.defaultInstance();
private QAuditRules defaultAuditRules = QAuditRules.defaultInstanceLevelNone(); private QAuditRules defaultAuditRules = QAuditRules.defaultInstanceLevelNone();
// todo - lock down the object (no more changes allowed) after it's been validated? //////////////////////////////////////////////////////////////////////////////////////
// todo - lock down the object (no more changes allowed) after it's been validated? //
// if doing so, may need to copy all of the collections into read-only versions... //
//////////////////////////////////////////////////////////////////////////////////////
@JsonIgnore @JsonIgnore
private boolean hasBeenValidated = false; private QInstanceValidationState validationState = QInstanceValidationState.PENDING;
private Map<String, String> memoizedTablePaths = new HashMap<>(); private Map<String, String> memoizedTablePaths = new HashMap<>();
private Map<String, String> memoizedProcessPaths = new HashMap<>(); private Map<String, String> memoizedProcessPaths = new HashMap<>();
@ -799,32 +803,58 @@ public class QInstance
*******************************************************************************/ *******************************************************************************/
public boolean getHasBeenValidated() public boolean getHasBeenValidated()
{ {
return hasBeenValidated; return validationState.equals(QInstanceValidationState.COMPLETE);
} }
/******************************************************************************* /*******************************************************************************
** If pass a QInstanceValidationKey (which can only be instantiated by the validator), ** If pass a QInstanceValidationKey (which can only be instantiated by the validator),
** then the hasBeenValidated field will be set to true. ** then the validationState will be set to COMPLETE.
** **
** Else, if passed a null, hasBeenValidated will be reset to false - e.g., to ** Else, if passed a null, the validationState will be reset to PENDING. e.g., to
** re-trigger validation (can be useful in tests). ** re-trigger validation (can be useful in tests).
*******************************************************************************/ *******************************************************************************/
public void setHasBeenValidated(QInstanceValidationKey key) public void setHasBeenValidated(QInstanceValidationKey key)
{ {
if(key == null) if(key == null)
{ {
this.hasBeenValidated = false; this.validationState = QInstanceValidationState.PENDING;
} }
else else
{ {
this.hasBeenValidated = true; this.validationState = QInstanceValidationState.COMPLETE;
} }
} }
/*******************************************************************************
** If pass a QInstanceValidationKey (which can only be instantiated by the validator),
** then the validationState set to RUNNING.
**
*******************************************************************************/
public void setValidationIsRunning(QInstanceValidationKey key)
{
if(key != null)
{
this.validationState = QInstanceValidationState.RUNNING;
}
}
/*******************************************************************************
** check if the instance is currently running validation.
**
*******************************************************************************/
public boolean getValidationIsRunning()
{
return validationState.equals(QInstanceValidationState.RUNNING);
}
/******************************************************************************* /*******************************************************************************
** Getter for branding ** Getter for branding
** **