Refactor setup of backend variants to use a dedicated sub-object, with more flexible "backend setting" fields as a map based with enum keys, rather than dedicated set of methods

This commit is contained in:
2025-02-14 19:55:04 -06:00
parent f0c07caba8
commit be25fc1272
10 changed files with 501 additions and 142 deletions

View File

@ -815,13 +815,14 @@ public class RunProcessAction
{ {
QSession session = QContext.getQSession(); QSession session = QContext.getQSession();
QBackendMetaData backendMetaData = QContext.getQInstance().getBackend(process.getVariantBackend()); QBackendMetaData backendMetaData = QContext.getQInstance().getBackend(process.getVariantBackend());
if(session.getBackendVariants() == null || !session.getBackendVariants().containsKey(backendMetaData.getVariantOptionsTableTypeValue())) String variantTypeKey = backendMetaData.getBackendVariantsConfig().getVariantTypeKey();
if(session.getBackendVariants() == null || !session.getBackendVariants().containsKey(variantTypeKey))
{ {
LOG.warn("Could not find Backend Variant information for Backend '" + backendMetaData.getName() + "'"); LOG.warn("Could not find Backend Variant information for Backend '" + backendMetaData.getName() + "'");
} }
else else
{ {
basepullKeyValue += "-" + session.getBackendVariants().get(backendMetaData.getVariantOptionsTableTypeValue()); basepullKeyValue += "-" + session.getBackendVariants().get(variantTypeKey);
} }
} }

View File

@ -108,12 +108,15 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.Automatio
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAutomationDetails; import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAutomationDetails;
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheOf; import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheOf;
import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase; import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase;
import com.kingsrook.qqq.backend.core.model.metadata.variants.BackendVariantSetting;
import com.kingsrook.qqq.backend.core.model.metadata.variants.BackendVariantsConfig;
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleCustomizerInterface; import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleCustomizerInterface;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils; 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 com.kingsrook.qqq.backend.core.utils.StringUtils; 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 com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeLambda; import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeLambda;
import org.apache.commons.lang.BooleanUtils;
import org.quartz.CronExpression; import org.quartz.CronExpression;
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
@ -543,6 +546,51 @@ public class QInstanceValidator
{ {
assertCondition(Objects.equals(backendName, backend.getName()), "Inconsistent naming for backend: " + backendName + "/" + backend.getName() + "."); assertCondition(Objects.equals(backendName, backend.getName()), "Inconsistent naming for backend: " + backendName + "/" + backend.getName() + ".");
///////////////////////
// validate variants //
///////////////////////
BackendVariantsConfig backendVariantsConfig = backend.getBackendVariantsConfig();
if(BooleanUtils.isTrue(backend.getUsesVariants()))
{
if(assertCondition(backendVariantsConfig != null, "Missing backendVariantsConfig in backend [" + backendName + "] which is marked as usesVariants"))
{
assertCondition(StringUtils.hasContent(backendVariantsConfig.getVariantTypeKey()), "Missing variantTypeKey in backendVariantsConfig in [" + backendName + "]");
String optionsTableName = backendVariantsConfig.getOptionsTableName();
QTableMetaData optionsTable = qInstance.getTable(optionsTableName);
if(assertCondition(StringUtils.hasContent(optionsTableName), "Missing optionsTableName in backendVariantsConfig in [" + backendName + "]"))
{
if(assertCondition(optionsTable != null, "Unrecognized optionsTableName [" + optionsTableName + "] in backendVariantsConfig in [" + backendName + "]"))
{
QQueryFilter optionsFilter = backendVariantsConfig.getOptionsFilter();
if(optionsFilter != null)
{
validateQueryFilter(qInstance, "optionsFilter in backendVariantsConfig in backend [" + backendName + "]: ", optionsTable, optionsFilter, null);
}
}
}
Map<BackendVariantSetting, String> backendSettingSourceFieldNameMap = backendVariantsConfig.getBackendSettingSourceFieldNameMap();
if(assertCondition(CollectionUtils.nullSafeHasContents(backendSettingSourceFieldNameMap), "Missing or empty backendSettingSourceFieldNameMap in backendVariantsConfig in [" + backendName + "]"))
{
if(optionsTable != null)
{
for(Map.Entry<BackendVariantSetting, String> entry : backendSettingSourceFieldNameMap.entrySet())
{
assertCondition(optionsTable.getFields().containsKey(entry.getValue()), "Unrecognized fieldName [" + entry.getValue() + "] in backendSettingSourceFieldNameMap in backendVariantsConfig in [" + backendName + "]");
}
}
}
}
}
else
{
assertCondition(backendVariantsConfig == null, "Should not have a backendVariantsConfig in backend [" + backendName + "] which is not marked as usesVariants");
}
///////////////////////////////////////////
// let the backend do its own validation //
///////////////////////////////////////////
backend.performValidation(this); backend.performValidation(this);
runPlugins(QBackendMetaData.class, backend, qInstance); runPlugins(QBackendMetaData.class, backend, qInstance);
@ -1616,12 +1664,12 @@ public class QInstanceValidator
for(QFieldMetaData field : process.getInputFields()) for(QFieldMetaData field : process.getInputFields())
{ {
validateFieldPossibleValueSourceAttributes(qInstance, field, "Process " + processName + ", input field " + field.getName()); validateFieldPossibleValueSourceAttributes(qInstance, field, "Process " + processName + ", input field " + field.getName() + " ");
} }
for(QFieldMetaData field : process.getOutputFields()) for(QFieldMetaData field : process.getOutputFields())
{ {
validateFieldPossibleValueSourceAttributes(qInstance, field, "Process " + processName + ", output field " + field.getName()); validateFieldPossibleValueSourceAttributes(qInstance, field, "Process " + processName + ", output field " + field.getName() + " ");
} }
if(process.getCancelStep() != null) if(process.getCancelStep() != null)

View File

@ -26,8 +26,13 @@ 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;
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator; import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.metadata.serialization.QBackendMetaDataDeserializer; import com.kingsrook.qqq.backend.core.model.metadata.serialization.QBackendMetaDataDeserializer;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability; import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
import com.kingsrook.qqq.backend.core.model.metadata.variants.BackendVariantsConfig;
import com.kingsrook.qqq.backend.core.model.metadata.variants.LegacyBackendVariantSetting;
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface; import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
@ -45,21 +50,18 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
private Set<Capability> enabledCapabilities = new HashSet<>(); private Set<Capability> enabledCapabilities = new HashSet<>();
private Set<Capability> disabledCapabilities = new HashSet<>(); private Set<Capability> disabledCapabilities = new HashSet<>();
private Boolean usesVariants = false; private Boolean usesVariants = false;
private String variantOptionsTableIdField; private BackendVariantsConfig backendVariantsConfig;
private String variantOptionsTableNameField;
private String variantOptionsTableTypeField;
private String variantOptionsTableTypeValue;
private String variantOptionsTableUsernameField;
private String variantOptionsTablePasswordField;
private String variantOptionsTableApiKeyField;
private String variantOptionsTableClientIdField;
private String variantOptionsTableClientSecretField;
private String variantOptionsTableName;
// todo - at some point, we may want to apply this to secret properties on subclasses? // todo - at some point, we may want to apply this to secret properties on subclasses?
// @JsonFilter("secretsFilter") // @JsonFilter("secretsFilter")
@Deprecated(since = "Replaced by filter in backendVariantsConfig - but leaving as field to pair with ...TypeValue for building filter")
private String variantOptionsTableTypeField; // a field on which to filter the variant-options table, to limit which records in it are available as variants
@Deprecated(since = "Replaced by variantTypeKey and value in filter in backendVariantsConfig - but leaving as field to pair with ...TypeField for building filter")
private String variantOptionsTableTypeValue; // value for the type-field, to limit which records in it are available as variants; but also, the key in the session.backendVariants map!
/******************************************************************************* /*******************************************************************************
@ -394,22 +396,15 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/*******************************************************************************
** Getter for variantOptionsTableIdField
*******************************************************************************/
public String getVariantOptionsTableIdField()
{
return (this.variantOptionsTableIdField);
}
/******************************************************************************* /*******************************************************************************
** Setter for variantOptionsTableIdField ** Setter for variantOptionsTableIdField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "backendVariantsConfig will infer this from the variant options table's primary key")
public void setVariantOptionsTableIdField(String variantOptionsTableIdField) public void setVariantOptionsTableIdField(String variantOptionsTableIdField)
{ {
this.variantOptionsTableIdField = variantOptionsTableIdField; /////////////////////////////////////////////////
// noop as we migrate to backendVariantsConfig //
/////////////////////////////////////////////////
} }
@ -417,30 +412,24 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** Fluent setter for variantOptionsTableIdField ** Fluent setter for variantOptionsTableIdField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "backendVariantsConfig will infer this from the variant options table's primary key")
public QBackendMetaData withVariantOptionsTableIdField(String variantOptionsTableIdField) public QBackendMetaData withVariantOptionsTableIdField(String variantOptionsTableIdField)
{ {
this.variantOptionsTableIdField = variantOptionsTableIdField; this.setVariantOptionsTableIdField(variantOptionsTableIdField);
return (this); return (this);
} }
/*******************************************************************************
** Getter for variantOptionsTableNameField
*******************************************************************************/
public String getVariantOptionsTableNameField()
{
return (this.variantOptionsTableNameField);
}
/******************************************************************************* /*******************************************************************************
** Setter for variantOptionsTableNameField ** Setter for variantOptionsTableNameField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "backendVariantsConfig will infer this from the variant options table's recordLabel")
public void setVariantOptionsTableNameField(String variantOptionsTableNameField) public void setVariantOptionsTableNameField(String variantOptionsTableNameField)
{ {
this.variantOptionsTableNameField = variantOptionsTableNameField; /////////////////////////////////////////////////
// noop as we migrate to backendVariantsConfig //
/////////////////////////////////////////////////
} }
@ -448,30 +437,28 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** Fluent setter for variantOptionsTableNameField ** Fluent setter for variantOptionsTableNameField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "backendVariantsConfig will infer this from the variant options table's recordLabel")
public QBackendMetaData withVariantOptionsTableNameField(String variantOptionsTableNameField) public QBackendMetaData withVariantOptionsTableNameField(String variantOptionsTableNameField)
{ {
this.variantOptionsTableNameField = variantOptionsTableNameField; this.setVariantOptionsTableNameField(variantOptionsTableNameField);
return (this); return (this);
} }
/*******************************************************************************
** Getter for variantOptionsTableTypeField
*******************************************************************************/
public String getVariantOptionsTableTypeField()
{
return (this.variantOptionsTableTypeField);
}
/******************************************************************************* /*******************************************************************************
** Setter for variantOptionsTableTypeField ** Setter for variantOptionsTableTypeField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by fieldName in filter in backendVariantsConfig - but leaving as field to pair with ...TypeValue for building filter")
public void setVariantOptionsTableTypeField(String variantOptionsTableTypeField) public void setVariantOptionsTableTypeField(String variantOptionsTableTypeField)
{ {
this.getOrWithNewBackendVariantsConfig().setVariantTypeKey(variantOptionsTableTypeField);
this.variantOptionsTableTypeField = variantOptionsTableTypeField; this.variantOptionsTableTypeField = variantOptionsTableTypeField;
if(this.variantOptionsTableTypeValue != null)
{
this.getOrWithNewBackendVariantsConfig().setOptionsFilter(new QQueryFilter(new QFilterCriteria(variantOptionsTableTypeField, QCriteriaOperator.EQUALS, variantOptionsTableTypeValue)));
}
} }
@ -479,30 +466,26 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** Fluent setter for variantOptionsTableTypeField ** Fluent setter for variantOptionsTableTypeField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by fieldName in filter in backendVariantsConfig - but leaving as field to pair with ...TypeValue for building filter")
public QBackendMetaData withVariantOptionsTableTypeField(String variantOptionsTableTypeField) public QBackendMetaData withVariantOptionsTableTypeField(String variantOptionsTableTypeField)
{ {
this.variantOptionsTableTypeField = variantOptionsTableTypeField; this.setVariantOptionsTableTypeField(variantOptionsTableTypeField);
return (this); return (this);
} }
/*******************************************************************************
** Getter for variantOptionsTableTypeValue
*******************************************************************************/
public String getVariantOptionsTableTypeValue()
{
return (this.variantOptionsTableTypeValue);
}
/******************************************************************************* /*******************************************************************************
** Setter for variantOptionsTableTypeValue ** Setter for variantOptionsTableTypeValue
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by variantTypeKey and value in filter in backendVariantsConfig - but leaving as field to pair with ...TypeField for building filter")
public void setVariantOptionsTableTypeValue(String variantOptionsTableTypeValue) public void setVariantOptionsTableTypeValue(String variantOptionsTableTypeValue)
{ {
this.variantOptionsTableTypeValue = variantOptionsTableTypeValue; this.variantOptionsTableTypeValue = variantOptionsTableTypeValue;
if(this.variantOptionsTableTypeField != null)
{
this.getOrWithNewBackendVariantsConfig().setOptionsFilter(new QQueryFilter(new QFilterCriteria(variantOptionsTableTypeField, QCriteriaOperator.EQUALS, variantOptionsTableTypeValue)));
}
} }
@ -510,30 +493,22 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** Fluent setter for variantOptionsTableTypeValue ** Fluent setter for variantOptionsTableTypeValue
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by variantTypeKey and value in filter in backendVariantsConfig - but leaving as field to pair with ...TypeField for building filter")
public QBackendMetaData withVariantOptionsTableTypeValue(String variantOptionsTableTypeValue) public QBackendMetaData withVariantOptionsTableTypeValue(String variantOptionsTableTypeValue)
{ {
this.variantOptionsTableTypeValue = variantOptionsTableTypeValue; this.setVariantOptionsTableTypeValue(variantOptionsTableTypeValue);
return (this); return (this);
} }
/*******************************************************************************
** Getter for variantOptionsTableUsernameField
*******************************************************************************/
public String getVariantOptionsTableUsernameField()
{
return (this.variantOptionsTableUsernameField);
}
/******************************************************************************* /*******************************************************************************
** Setter for variantOptionsTableUsernameField ** Setter for variantOptionsTableUsernameField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap")
public void setVariantOptionsTableUsernameField(String variantOptionsTableUsernameField) public void setVariantOptionsTableUsernameField(String variantOptionsTableUsernameField)
{ {
this.variantOptionsTableUsernameField = variantOptionsTableUsernameField; this.getOrWithNewBackendVariantsConfig().withBackendSettingSourceFieldName(LegacyBackendVariantSetting.USERNAME, variantOptionsTableUsernameField);
} }
@ -541,30 +516,22 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** Fluent setter for variantOptionsTableUsernameField ** Fluent setter for variantOptionsTableUsernameField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap")
public QBackendMetaData withVariantOptionsTableUsernameField(String variantOptionsTableUsernameField) public QBackendMetaData withVariantOptionsTableUsernameField(String variantOptionsTableUsernameField)
{ {
this.variantOptionsTableUsernameField = variantOptionsTableUsernameField; this.setVariantOptionsTableUsernameField(variantOptionsTableUsernameField);
return (this); return (this);
} }
/*******************************************************************************
** Getter for variantOptionsTablePasswordField
*******************************************************************************/
public String getVariantOptionsTablePasswordField()
{
return (this.variantOptionsTablePasswordField);
}
/******************************************************************************* /*******************************************************************************
** Setter for variantOptionsTablePasswordField ** Setter for variantOptionsTablePasswordField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap")
public void setVariantOptionsTablePasswordField(String variantOptionsTablePasswordField) public void setVariantOptionsTablePasswordField(String variantOptionsTablePasswordField)
{ {
this.variantOptionsTablePasswordField = variantOptionsTablePasswordField; this.getOrWithNewBackendVariantsConfig().withBackendSettingSourceFieldName(LegacyBackendVariantSetting.PASSWORD, variantOptionsTablePasswordField);
} }
@ -572,30 +539,22 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** Fluent setter for variantOptionsTablePasswordField ** Fluent setter for variantOptionsTablePasswordField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap")
public QBackendMetaData withVariantOptionsTablePasswordField(String variantOptionsTablePasswordField) public QBackendMetaData withVariantOptionsTablePasswordField(String variantOptionsTablePasswordField)
{ {
this.variantOptionsTablePasswordField = variantOptionsTablePasswordField; this.setVariantOptionsTablePasswordField(variantOptionsTablePasswordField);
return (this); return (this);
} }
/*******************************************************************************
** Getter for variantOptionsTableApiKeyField
*******************************************************************************/
public String getVariantOptionsTableApiKeyField()
{
return (this.variantOptionsTableApiKeyField);
}
/******************************************************************************* /*******************************************************************************
** Setter for variantOptionsTableApiKeyField ** Setter for variantOptionsTableApiKeyField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap")
public void setVariantOptionsTableApiKeyField(String variantOptionsTableApiKeyField) public void setVariantOptionsTableApiKeyField(String variantOptionsTableApiKeyField)
{ {
this.variantOptionsTableApiKeyField = variantOptionsTableApiKeyField; this.getOrWithNewBackendVariantsConfig().withBackendSettingSourceFieldName(LegacyBackendVariantSetting.API_KEY, variantOptionsTableApiKeyField);
} }
@ -603,30 +562,22 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** Fluent setter for variantOptionsTableApiKeyField ** Fluent setter for variantOptionsTableApiKeyField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap")
public QBackendMetaData withVariantOptionsTableApiKeyField(String variantOptionsTableApiKeyField) public QBackendMetaData withVariantOptionsTableApiKeyField(String variantOptionsTableApiKeyField)
{ {
this.variantOptionsTableApiKeyField = variantOptionsTableApiKeyField; this.setVariantOptionsTableApiKeyField(variantOptionsTableApiKeyField);
return (this); return (this);
} }
/*******************************************************************************
** Getter for variantOptionsTableName
*******************************************************************************/
public String getVariantOptionsTableName()
{
return (this.variantOptionsTableName);
}
/******************************************************************************* /*******************************************************************************
** Setter for variantOptionsTableName ** Setter for variantOptionsTableName
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.tableName")
public void setVariantOptionsTableName(String variantOptionsTableName) public void setVariantOptionsTableName(String variantOptionsTableName)
{ {
this.variantOptionsTableName = variantOptionsTableName; this.getOrWithNewBackendVariantsConfig().withOptionsTableName(variantOptionsTableName);
} }
@ -634,9 +585,10 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** Fluent setter for variantOptionsTableName ** Fluent setter for variantOptionsTableName
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.tableName")
public QBackendMetaData withVariantOptionsTableName(String variantOptionsTableName) public QBackendMetaData withVariantOptionsTableName(String variantOptionsTableName)
{ {
this.variantOptionsTableName = variantOptionsTableName; this.setVariantOptionsTableName(variantOptionsTableName);
return (this); return (this);
} }
@ -651,22 +603,15 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
qInstance.addBackend(this); qInstance.addBackend(this);
} }
/*******************************************************************************
** Getter for variantOptionsTableClientIdField
*******************************************************************************/
public String getVariantOptionsTableClientIdField()
{
return (this.variantOptionsTableClientIdField);
}
/******************************************************************************* /*******************************************************************************
** Setter for variantOptionsTableClientIdField ** Setter for variantOptionsTableClientIdField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap")
public void setVariantOptionsTableClientIdField(String variantOptionsTableClientIdField) public void setVariantOptionsTableClientIdField(String variantOptionsTableClientIdField)
{ {
this.variantOptionsTableClientIdField = variantOptionsTableClientIdField; this.getOrWithNewBackendVariantsConfig().withBackendSettingSourceFieldName(LegacyBackendVariantSetting.CLIENT_ID, variantOptionsTableClientIdField);
} }
@ -674,30 +619,22 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** Fluent setter for variantOptionsTableClientIdField ** Fluent setter for variantOptionsTableClientIdField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap")
public QBackendMetaData withVariantOptionsTableClientIdField(String variantOptionsTableClientIdField) public QBackendMetaData withVariantOptionsTableClientIdField(String variantOptionsTableClientIdField)
{ {
this.variantOptionsTableClientIdField = variantOptionsTableClientIdField; this.setVariantOptionsTableClientIdField(variantOptionsTableClientIdField);
return (this); return (this);
} }
/*******************************************************************************
** Getter for variantOptionsTableClientSecretField
*******************************************************************************/
public String getVariantOptionsTableClientSecretField()
{
return (this.variantOptionsTableClientSecretField);
}
/******************************************************************************* /*******************************************************************************
** Setter for variantOptionsTableClientSecretField ** Setter for variantOptionsTableClientSecretField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap")
public void setVariantOptionsTableClientSecretField(String variantOptionsTableClientSecretField) public void setVariantOptionsTableClientSecretField(String variantOptionsTableClientSecretField)
{ {
this.variantOptionsTableClientSecretField = variantOptionsTableClientSecretField; this.getOrWithNewBackendVariantsConfig().withBackendSettingSourceFieldName(LegacyBackendVariantSetting.CLIENT_SECRET, variantOptionsTableClientSecretField);
} }
@ -705,11 +642,55 @@ public class QBackendMetaData implements TopLevelMetaDataInterface
/******************************************************************************* /*******************************************************************************
** Fluent setter for variantOptionsTableClientSecretField ** Fluent setter for variantOptionsTableClientSecretField
*******************************************************************************/ *******************************************************************************/
@Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap")
public QBackendMetaData withVariantOptionsTableClientSecretField(String variantOptionsTableClientSecretField) public QBackendMetaData withVariantOptionsTableClientSecretField(String variantOptionsTableClientSecretField)
{ {
this.variantOptionsTableClientSecretField = variantOptionsTableClientSecretField; this.setVariantOptionsTableClientSecretField(variantOptionsTableClientSecretField);
return (this); return (this);
} }
/*******************************************************************************
** Getter for backendVariantsConfig
*******************************************************************************/
public BackendVariantsConfig getBackendVariantsConfig()
{
return (this.backendVariantsConfig);
}
/*******************************************************************************
** Setter for backendVariantsConfig
*******************************************************************************/
public void setBackendVariantsConfig(BackendVariantsConfig backendVariantsConfig)
{
this.backendVariantsConfig = backendVariantsConfig;
}
/*******************************************************************************
** Fluent setter for backendVariantsConfig
*******************************************************************************/
public QBackendMetaData withBackendVariantsConfig(BackendVariantsConfig backendVariantsConfig)
{
this.backendVariantsConfig = backendVariantsConfig;
return (this);
}
/***************************************************************************
**
***************************************************************************/
private BackendVariantsConfig getOrWithNewBackendVariantsConfig()
{
if(backendVariantsConfig == null)
{
setBackendVariantsConfig(new BackendVariantsConfig());
}
return backendVariantsConfig;
}
} }

View File

@ -86,7 +86,6 @@ public class QFrontendTableMetaData
////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
@ -170,7 +169,7 @@ public class QFrontendTableMetaData
if(backend != null && backend.getUsesVariants()) if(backend != null && backend.getUsesVariants())
{ {
usesVariants = true; usesVariants = true;
variantTableLabel = QContext.getQInstance().getTable(backend.getVariantOptionsTableName()).getLabel(); variantTableLabel = QContext.getQInstance().getTable(backend.getBackendVariantsConfig().getOptionsTableName()).getLabel();
} }
this.helpContents = tableMetaData.getHelpContent(); this.helpContents = tableMetaData.getHelpContent();

View File

@ -0,0 +1,31 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. 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.metadata.variants;
/*******************************************************************************
** interface to be implemented by enums (presumably) that define the possible
** settings a particular backend type can get from a variant record.
*******************************************************************************/
public interface BackendVariantSetting
{
}

View File

@ -0,0 +1,189 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. 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.metadata.variants;
import java.util.HashMap;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
/*******************************************************************************
** Configs for how a backend that uses variants works. Specifically:
**
** - the variant "type key" - e.g., key for variants map in session.
** - what table supplies the variant options (optionsTableName
** - an optional filter to apply to that options table
** - a map of the settings that a backend gets from its variant table to the
** field names in that table that they come from. e.g., a backend may have a
** username attribute, whose value comes from a field named "theUser" in the
** variant options table.
*******************************************************************************/
public class BackendVariantsConfig
{
private String variantTypeKey;
private String optionsTableName;
private QQueryFilter optionsFilter;
private Map<BackendVariantSetting, String> backendSettingSourceFieldNameMap;
/*******************************************************************************
** Getter for tableName
*******************************************************************************/
public String getOptionsTableName()
{
return (this.optionsTableName);
}
/*******************************************************************************
** Setter for tableName
*******************************************************************************/
public void setOptionsTableName(String optionsTableName)
{
this.optionsTableName = optionsTableName;
}
/*******************************************************************************
** Getter for filter
*******************************************************************************/
public QQueryFilter getOptionsFilter()
{
return (this.optionsFilter);
}
/*******************************************************************************
** Setter for filter
*******************************************************************************/
public void setOptionsFilter(QQueryFilter optionsFilter)
{
this.optionsFilter = optionsFilter;
}
/*******************************************************************************
** Getter for backendSettingSourceFieldNameMap
*******************************************************************************/
public Map<BackendVariantSetting, String> getBackendSettingSourceFieldNameMap()
{
return (this.backendSettingSourceFieldNameMap);
}
/*******************************************************************************
** Setter for backendSettingSourceFieldNameMap
*******************************************************************************/
public void setBackendSettingSourceFieldNameMap(Map<BackendVariantSetting, String> backendSettingSourceFieldNameMap)
{
this.backendSettingSourceFieldNameMap = backendSettingSourceFieldNameMap;
}
/*******************************************************************************
** Fluent setter for backendSettingSourceFieldNameMap
*******************************************************************************/
public BackendVariantsConfig withBackendSettingSourceFieldName(BackendVariantSetting backendVariantSetting, String sourceFieldName)
{
if(this.backendSettingSourceFieldNameMap == null)
{
this.backendSettingSourceFieldNameMap = new HashMap<>();
}
this.backendSettingSourceFieldNameMap.put(backendVariantSetting, sourceFieldName);
return (this);
}
/*******************************************************************************
** Fluent setter for backendSettingSourceFieldNameMap
*******************************************************************************/
public BackendVariantsConfig withBackendSettingSourceFieldNameMap(Map<BackendVariantSetting, String> backendSettingSourceFieldNameMap)
{
this.backendSettingSourceFieldNameMap = backendSettingSourceFieldNameMap;
return (this);
}
/*******************************************************************************
** Getter for variantTypeKey
*******************************************************************************/
public String getVariantTypeKey()
{
return (this.variantTypeKey);
}
/*******************************************************************************
** Setter for variantTypeKey
*******************************************************************************/
public void setVariantTypeKey(String variantTypeKey)
{
this.variantTypeKey = variantTypeKey;
}
/*******************************************************************************
** Fluent setter for variantTypeKey
*******************************************************************************/
public BackendVariantsConfig withVariantTypeKey(String variantTypeKey)
{
this.variantTypeKey = variantTypeKey;
return (this);
}
/*******************************************************************************
** Fluent setter for optionsTableName
*******************************************************************************/
public BackendVariantsConfig withOptionsTableName(String optionsTableName)
{
this.optionsTableName = optionsTableName;
return (this);
}
/*******************************************************************************
** Fluent setter for optionsFilter
*******************************************************************************/
public BackendVariantsConfig withOptionsFilter(QQueryFilter optionsFilter)
{
this.optionsFilter = optionsFilter;
return (this);
}
}

View File

@ -0,0 +1,39 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. 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.metadata.variants;
/*******************************************************************************
** temporary class, while we migrate from original way that variants were set up
** e.g., by calling 'variantOptionsTableUsernameField', to the new way, using
** the BackendVariantsConfig which uses a map of enum constants.
**
** so when those deprecated setters are removed, this enum can be too.
*****************************************************************************/
public enum LegacyBackendVariantSetting implements BackendVariantSetting
{
USERNAME,
PASSWORD,
API_KEY,
CLIENT_ID,
CLIENT_SECRET
}

View File

@ -441,11 +441,16 @@ public class QScheduleManager
try try
{ {
HashMap<String, Serializable> parameters = new HashMap<>(paramMap); HashMap<String, Serializable> parameters = new HashMap<>(paramMap);
HashMap<String, Serializable> variantMap = new HashMap<>(Map.of(backendMetaData.getVariantOptionsTableTypeValue(), qRecord.getValue(backendMetaData.getVariantOptionsTableIdField())));
String variantTypeKey = backendMetaData.getBackendVariantsConfig().getVariantTypeKey();
String variantOptionsTableName = backendMetaData.getBackendVariantsConfig().getOptionsTableName();
String variantOptionsTableIdFieldName = QContext.getQInstance().getTable(variantOptionsTableName).getPrimaryKeyField();
HashMap<String, Serializable> variantMap = new HashMap<>(Map.of(variantTypeKey, qRecord.getValue(variantOptionsTableIdFieldName)));
parameters.put("backendVariantData", variantMap); parameters.put("backendVariantData", variantMap);
String identity = schedulableIdentity.getIdentity() + ";" + backendMetaData.getVariantOptionsTableTypeValue() + "=" + qRecord.getValue(backendMetaData.getVariantOptionsTableIdField()); String identity = schedulableIdentity.getIdentity() + ";" + variantTypeKey + "=" + qRecord.getValue(variantOptionsTableIdFieldName);
String description = schedulableIdentity.getDescription() + " for variant: " + backendMetaData.getVariantOptionsTableTypeValue() + "=" + qRecord.getValue(backendMetaData.getVariantOptionsTableIdField()); String description = schedulableIdentity.getDescription() + " for variant: " + variantTypeKey + "=" + qRecord.getValue(variantOptionsTableIdFieldName);
BasicSchedulableIdentity variantIdentity = new BasicSchedulableIdentity(identity, description); BasicSchedulableIdentity variantIdentity = new BasicSchedulableIdentity(identity, description);

View File

@ -34,9 +34,6 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.LogPair; import com.kingsrook.qqq.backend.core.logging.LogPair;
import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput; import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
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;
@ -102,7 +99,8 @@ public class SchedulerUtils
try try
{ {
QBackendMetaData backendMetaData = qInstance.getBackend(process.getVariantBackend()); QBackendMetaData backendMetaData = qInstance.getBackend(process.getVariantBackend());
Map<String, Serializable> thisVariantData = MapBuilder.of(backendMetaData.getVariantOptionsTableTypeValue(), qRecord.getValue(backendMetaData.getVariantOptionsTableIdField())); QTableMetaData variantTable = QContext.getQInstance().getTable(backendMetaData.getBackendVariantsConfig().getOptionsTableName());
Map<String, Serializable> thisVariantData = MapBuilder.of(backendMetaData.getBackendVariantsConfig().getVariantTypeKey(), qRecord.getValue(variantTable.getPrimaryKeyField()));
executeSingleProcess(process, thisVariantData, processInputValues); executeSingleProcess(process, thisVariantData, processInputValues);
} }
catch(Exception e) catch(Exception e)
@ -181,8 +179,8 @@ public class SchedulerUtils
QBackendMetaData backendMetaData = QContext.getQInstance().getBackend(processMetaData.getVariantBackend()); QBackendMetaData backendMetaData = QContext.getQInstance().getBackend(processMetaData.getVariantBackend());
QueryInput queryInput = new QueryInput(); QueryInput queryInput = new QueryInput();
queryInput.setTableName(backendMetaData.getVariantOptionsTableName()); queryInput.setTableName(backendMetaData.getBackendVariantsConfig().getOptionsTableName());
queryInput.setFilter(new QQueryFilter(new QFilterCriteria(backendMetaData.getVariantOptionsTableTypeField(), QCriteriaOperator.EQUALS, backendMetaData.getVariantOptionsTableTypeValue()))); queryInput.setFilter(backendMetaData.getBackendVariantsConfig().getOptionsFilter());
QueryOutput queryOutput = new QueryAction().execute(queryInput); QueryOutput queryOutput = new QueryAction().execute(queryInput);
records = queryOutput.getRecords(); records = queryOutput.getRecords();

View File

@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -55,6 +56,7 @@ 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.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.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.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.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
@ -93,6 +95,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier; import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey; import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction; import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
import com.kingsrook.qqq.backend.core.model.metadata.variants.BackendVariantSetting;
import com.kingsrook.qqq.backend.core.model.metadata.variants.BackendVariantsConfig;
import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleCustomizerInterface; import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModuleCustomizerInterface;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractTransformStep; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractTransformStep;
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
@ -182,6 +186,70 @@ public class QInstanceValidatorTest extends BaseTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testBackendVariants()
{
BackendVariantSetting setting = new BackendVariantSetting() {};
assertValidationFailureReasons((qInstance) -> qInstance.addBackend(new QBackendMetaData()
.withName("variant")
.withUsesVariants(true)),
"Missing backendVariantsConfig in backend [variant] which is marked as usesVariants");
assertValidationFailureReasons((qInstance) -> qInstance.addBackend(new QBackendMetaData()
.withName("variant")
.withUsesVariants(false)
.withBackendVariantsConfig(new BackendVariantsConfig())),
"Should not have a backendVariantsConfig");
assertValidationFailureReasons((qInstance) -> qInstance.addBackend(new QBackendMetaData()
.withName("variant")
.withUsesVariants(null)
.withBackendVariantsConfig(new BackendVariantsConfig())),
"Should not have a backendVariantsConfig");
assertValidationFailureReasons((qInstance) -> qInstance.addBackend(new QBackendMetaData()
.withName("variant")
.withUsesVariants(true)
.withBackendVariantsConfig(new BackendVariantsConfig())),
"Missing variantTypeKey in backendVariantsConfig",
"Missing optionsTableName in backendVariantsConfig",
"Missing or empty backendSettingSourceFieldNameMap");
assertValidationFailureReasons((qInstance) -> qInstance.addBackend(new QBackendMetaData()
.withName("variant")
.withUsesVariants(true)
.withBackendVariantsConfig(new BackendVariantsConfig()
.withVariantTypeKey("myVariant")
.withOptionsTableName("notATable")
.withBackendSettingSourceFieldNameMap(Map.of(setting, "field")))),
"Unrecognized optionsTableName [notATable] in backendVariantsConfig");
assertValidationFailureReasons((qInstance) -> qInstance.addBackend(new QBackendMetaData()
.withName("variant")
.withUsesVariants(true)
.withBackendVariantsConfig(new BackendVariantsConfig()
.withVariantTypeKey("myVariant")
.withOptionsTableName(TestUtils.TABLE_NAME_PERSON)
.withOptionsFilter(new QQueryFilter(new QFilterCriteria("notAField", QCriteriaOperator.EQUALS, 1)))
.withBackendSettingSourceFieldNameMap(Map.of(setting, "firstName")))),
"optionsFilter in backendVariantsConfig in backend [variant]: Criteria fieldName notAField is not a field");
assertValidationFailureReasons((qInstance) -> qInstance.addBackend(new QBackendMetaData()
.withName("variant")
.withUsesVariants(true)
.withBackendVariantsConfig(new BackendVariantsConfig()
.withVariantTypeKey("myVariant")
.withOptionsTableName(TestUtils.TABLE_NAME_PERSON)
.withBackendSettingSourceFieldNameMap(Map.of(setting, "noSuchField")))),
"Unrecognized fieldName [noSuchField] in backendSettingSourceFieldNameMap");
}
/******************************************************************************* /*******************************************************************************
** Test an instance with null tables - should throw. ** Test an instance with null tables - should throw.
** **