diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/processes/RunProcessAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/processes/RunProcessAction.java index a535ae47..806b0f5c 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/processes/RunProcessAction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/processes/RunProcessAction.java @@ -815,13 +815,14 @@ public class RunProcessAction { QSession session = QContext.getQSession(); 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() + "'"); } else { - basepullKeyValue += "-" + session.getBackendVariants().get(backendMetaData.getVariantOptionsTableTypeValue()); + basepullKeyValue += "-" + session.getBackendVariants().get(variantTypeKey); } } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java index dcc06391..fa4d1a23 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java @@ -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.cache.CacheOf; 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.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.ListingHash; import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.core.utils.ValueUtils; import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeLambda; +import org.apache.commons.lang.BooleanUtils; import org.quartz.CronExpression; 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() + "."); + /////////////////////// + // 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 backendSettingSourceFieldNameMap = backendVariantsConfig.getBackendSettingSourceFieldNameMap(); + if(assertCondition(CollectionUtils.nullSafeHasContents(backendSettingSourceFieldNameMap), "Missing or empty backendSettingSourceFieldNameMap in backendVariantsConfig in [" + backendName + "]")) + { + if(optionsTable != null) + { + for(Map.Entry 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); runPlugins(QBackendMetaData.class, backend, qInstance); @@ -1616,12 +1664,12 @@ public class QInstanceValidator 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()) { - validateFieldPossibleValueSourceAttributes(qInstance, field, "Process " + processName + ", output field " + field.getName()); + validateFieldPossibleValueSourceAttributes(qInstance, field, "Process " + processName + ", output field " + field.getName() + " "); } if(process.getCancelStep() != null) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QBackendMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QBackendMetaData.java index 52f6c95f..3d79e591 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QBackendMetaData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/QBackendMetaData.java @@ -26,8 +26,13 @@ import java.util.HashSet; import java.util.Set; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 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.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; @@ -45,21 +50,18 @@ public class QBackendMetaData implements TopLevelMetaDataInterface private Set enabledCapabilities = new HashSet<>(); private Set disabledCapabilities = new HashSet<>(); - private Boolean usesVariants = false; - private String variantOptionsTableIdField; - 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; + private Boolean usesVariants = false; + private BackendVariantsConfig backendVariantsConfig; // todo - at some point, we may want to apply this to secret properties on subclasses? // @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 *******************************************************************************/ + @Deprecated(since = "backendVariantsConfig will infer this from the variant options table's primary key") 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 *******************************************************************************/ + @Deprecated(since = "backendVariantsConfig will infer this from the variant options table's primary key") public QBackendMetaData withVariantOptionsTableIdField(String variantOptionsTableIdField) { - this.variantOptionsTableIdField = variantOptionsTableIdField; + this.setVariantOptionsTableIdField(variantOptionsTableIdField); return (this); } - /******************************************************************************* - ** Getter for variantOptionsTableNameField - *******************************************************************************/ - public String getVariantOptionsTableNameField() - { - return (this.variantOptionsTableNameField); - } - - - /******************************************************************************* ** Setter for variantOptionsTableNameField *******************************************************************************/ + @Deprecated(since = "backendVariantsConfig will infer this from the variant options table's recordLabel") 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 *******************************************************************************/ + @Deprecated(since = "backendVariantsConfig will infer this from the variant options table's recordLabel") public QBackendMetaData withVariantOptionsTableNameField(String variantOptionsTableNameField) { - this.variantOptionsTableNameField = variantOptionsTableNameField; + this.setVariantOptionsTableNameField(variantOptionsTableNameField); return (this); } - /******************************************************************************* - ** Getter for variantOptionsTableTypeField - *******************************************************************************/ - public String getVariantOptionsTableTypeField() - { - return (this.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) { + this.getOrWithNewBackendVariantsConfig().setVariantTypeKey(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 *******************************************************************************/ + @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) { - this.variantOptionsTableTypeField = variantOptionsTableTypeField; + this.setVariantOptionsTableTypeField(variantOptionsTableTypeField); return (this); } - /******************************************************************************* - ** Getter for variantOptionsTableTypeValue - *******************************************************************************/ - public String getVariantOptionsTableTypeValue() - { - return (this.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) { 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 *******************************************************************************/ + @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) { - this.variantOptionsTableTypeValue = variantOptionsTableTypeValue; + this.setVariantOptionsTableTypeValue(variantOptionsTableTypeValue); return (this); } - /******************************************************************************* - ** Getter for variantOptionsTableUsernameField - *******************************************************************************/ - public String getVariantOptionsTableUsernameField() - { - return (this.variantOptionsTableUsernameField); - } - - - /******************************************************************************* ** Setter for variantOptionsTableUsernameField *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap") 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 *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap") public QBackendMetaData withVariantOptionsTableUsernameField(String variantOptionsTableUsernameField) { - this.variantOptionsTableUsernameField = variantOptionsTableUsernameField; + this.setVariantOptionsTableUsernameField(variantOptionsTableUsernameField); return (this); } - /******************************************************************************* - ** Getter for variantOptionsTablePasswordField - *******************************************************************************/ - public String getVariantOptionsTablePasswordField() - { - return (this.variantOptionsTablePasswordField); - } - - - /******************************************************************************* ** Setter for variantOptionsTablePasswordField *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap") 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 *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap") public QBackendMetaData withVariantOptionsTablePasswordField(String variantOptionsTablePasswordField) { - this.variantOptionsTablePasswordField = variantOptionsTablePasswordField; + this.setVariantOptionsTablePasswordField(variantOptionsTablePasswordField); return (this); } - /******************************************************************************* - ** Getter for variantOptionsTableApiKeyField - *******************************************************************************/ - public String getVariantOptionsTableApiKeyField() - { - return (this.variantOptionsTableApiKeyField); - } - - - /******************************************************************************* ** Setter for variantOptionsTableApiKeyField *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap") 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 *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap") public QBackendMetaData withVariantOptionsTableApiKeyField(String variantOptionsTableApiKeyField) { - this.variantOptionsTableApiKeyField = variantOptionsTableApiKeyField; + this.setVariantOptionsTableApiKeyField(variantOptionsTableApiKeyField); return (this); } - /******************************************************************************* - ** Getter for variantOptionsTableName - *******************************************************************************/ - public String getVariantOptionsTableName() - { - return (this.variantOptionsTableName); - } - - - /******************************************************************************* ** Setter for variantOptionsTableName *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.tableName") 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 *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.tableName") public QBackendMetaData withVariantOptionsTableName(String variantOptionsTableName) { - this.variantOptionsTableName = variantOptionsTableName; + this.setVariantOptionsTableName(variantOptionsTableName); return (this); } @@ -651,22 +603,15 @@ public class QBackendMetaData implements TopLevelMetaDataInterface qInstance.addBackend(this); } - /******************************************************************************* - ** Getter for variantOptionsTableClientIdField - *******************************************************************************/ - public String getVariantOptionsTableClientIdField() - { - return (this.variantOptionsTableClientIdField); - } - /******************************************************************************* ** Setter for variantOptionsTableClientIdField *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap") 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 *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap") public QBackendMetaData withVariantOptionsTableClientIdField(String variantOptionsTableClientIdField) { - this.variantOptionsTableClientIdField = variantOptionsTableClientIdField; + this.setVariantOptionsTableClientIdField(variantOptionsTableClientIdField); return (this); } - /******************************************************************************* - ** Getter for variantOptionsTableClientSecretField - *******************************************************************************/ - public String getVariantOptionsTableClientSecretField() - { - return (this.variantOptionsTableClientSecretField); - } - - - /******************************************************************************* ** Setter for variantOptionsTableClientSecretField *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap") 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 *******************************************************************************/ + @Deprecated(since = "Replaced by backendVariantsConfig.backendSettingSourceFieldNameMap") public QBackendMetaData withVariantOptionsTableClientSecretField(String variantOptionsTableClientSecretField) { - this.variantOptionsTableClientSecretField = variantOptionsTableClientSecretField; + this.setVariantOptionsTableClientSecretField(variantOptionsTableClientSecretField); 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; + } } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/frontend/QFrontendTableMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/frontend/QFrontendTableMetaData.java index 6e98fdec..01b7832e 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/frontend/QFrontendTableMetaData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/frontend/QFrontendTableMetaData.java @@ -86,7 +86,6 @@ public class QFrontendTableMetaData ////////////////////////////////////////////////////////////////////////////////// - /******************************************************************************* ** *******************************************************************************/ @@ -170,7 +169,7 @@ public class QFrontendTableMetaData if(backend != null && backend.getUsesVariants()) { usesVariants = true; - variantTableLabel = QContext.getQInstance().getTable(backend.getVariantOptionsTableName()).getLabel(); + variantTableLabel = QContext.getQInstance().getTable(backend.getBackendVariantsConfig().getOptionsTableName()).getLabel(); } this.helpContents = tableMetaData.getHelpContent(); diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/variants/BackendVariantSetting.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/variants/BackendVariantSetting.java new file mode 100644 index 00000000..ea57a3ed --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/variants/BackendVariantSetting.java @@ -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 . + */ + +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 +{ +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/variants/BackendVariantsConfig.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/variants/BackendVariantsConfig.java new file mode 100644 index 00000000..20237e7a --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/variants/BackendVariantsConfig.java @@ -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 . + */ + +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 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 getBackendSettingSourceFieldNameMap() + { + return (this.backendSettingSourceFieldNameMap); + } + + + + /******************************************************************************* + ** Setter for backendSettingSourceFieldNameMap + *******************************************************************************/ + public void setBackendSettingSourceFieldNameMap(Map 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 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); + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/variants/LegacyBackendVariantSetting.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/variants/LegacyBackendVariantSetting.java new file mode 100644 index 00000000..59ccc38c --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/variants/LegacyBackendVariantSetting.java @@ -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 . + */ + +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 +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/scheduler/QScheduleManager.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/scheduler/QScheduleManager.java index 3dfec3ae..88246600 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/scheduler/QScheduleManager.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/scheduler/QScheduleManager.java @@ -441,11 +441,16 @@ public class QScheduleManager try { HashMap parameters = new HashMap<>(paramMap); - HashMap 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 variantMap = new HashMap<>(Map.of(variantTypeKey, qRecord.getValue(variantOptionsTableIdFieldName))); parameters.put("backendVariantData", variantMap); - String identity = schedulableIdentity.getIdentity() + ";" + backendMetaData.getVariantOptionsTableTypeValue() + "=" + qRecord.getValue(backendMetaData.getVariantOptionsTableIdField()); - String description = schedulableIdentity.getDescription() + " for variant: " + backendMetaData.getVariantOptionsTableTypeValue() + "=" + qRecord.getValue(backendMetaData.getVariantOptionsTableIdField()); + String identity = schedulableIdentity.getIdentity() + ";" + variantTypeKey + "=" + qRecord.getValue(variantOptionsTableIdFieldName); + String description = schedulableIdentity.getDescription() + " for variant: " + variantTypeKey + "=" + qRecord.getValue(variantOptionsTableIdFieldName); BasicSchedulableIdentity variantIdentity = new BasicSchedulableIdentity(identity, description); diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/scheduler/SchedulerUtils.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/scheduler/SchedulerUtils.java index c7cc6a4c..e7b46ea3 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/scheduler/SchedulerUtils.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/scheduler/SchedulerUtils.java @@ -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.QLogger; 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.QueryOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; @@ -102,7 +99,8 @@ public class SchedulerUtils try { QBackendMetaData backendMetaData = qInstance.getBackend(process.getVariantBackend()); - Map thisVariantData = MapBuilder.of(backendMetaData.getVariantOptionsTableTypeValue(), qRecord.getValue(backendMetaData.getVariantOptionsTableIdField())); + QTableMetaData variantTable = QContext.getQInstance().getTable(backendMetaData.getBackendVariantsConfig().getOptionsTableName()); + Map thisVariantData = MapBuilder.of(backendMetaData.getBackendVariantsConfig().getVariantTypeKey(), qRecord.getValue(variantTable.getPrimaryKeyField())); executeSingleProcess(process, thisVariantData, processInputValues); } catch(Exception e) @@ -181,8 +179,8 @@ public class SchedulerUtils QBackendMetaData backendMetaData = QContext.getQInstance().getBackend(processMetaData.getVariantBackend()); QueryInput queryInput = new QueryInput(); - queryInput.setTableName(backendMetaData.getVariantOptionsTableName()); - queryInput.setFilter(new QQueryFilter(new QFilterCriteria(backendMetaData.getVariantOptionsTableTypeField(), QCriteriaOperator.EQUALS, backendMetaData.getVariantOptionsTableTypeValue()))); + queryInput.setTableName(backendMetaData.getBackendVariantsConfig().getOptionsTableName()); + queryInput.setFilter(backendMetaData.getBackendVariantsConfig().getOptionsFilter()); QueryOutput queryOutput = new QueryAction().execute(queryInput); records = queryOutput.getRecords(); diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java index c09d1ac8..9ce5d9f6 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.BiFunction; 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.QQueryFilter; import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; 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.UniqueKey; 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.processes.implementations.etl.streamedwithfrontend.AbstractTransformStep; 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. **